Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
MyOpenGLView.m
/* |
File: MyOpenGLView.m of QTCoreImage101 |
Author: QuickTime DTS |
Change History (most recent first): <3> 10/08/09 minor update |
<2> 06/14/05 call QTVisualContextTask while owning lock |
overide and add lock around update |
<1> 05/29/05 initial release |
© Copyright 2005 Apple Computer, Inc. All rights reserved. |
IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in |
consideration of your agreement to the following terms, and your use, installation, |
modification or redistribution of this Apple software constitutes acceptance of these |
terms. If you do not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject to these |
terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in |
this original Apple software (the "Apple Software"), to use, reproduce, modify and |
redistribute the Apple Software, with or without modifications, in source and/or binary |
forms; provided that if you redistribute the Apple Software in its entirety and without |
modifications, you must retain this notice and the following text and disclaimers in all |
such redistributions of the Apple Software. Neither the name, trademarks, service marks |
or logos of Apple Computer, Inc. may be used to endorse or promote products derived from |
the Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, are |
granted by Apple herein, including but not limited to any patent rights that may be |
infringed by your derivative works or by other works in which the Apple Software may be |
incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, |
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF |
NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE |
APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL |
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE |
USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER |
CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT |
LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
#import "MyOpenGLView.h" |
#pragma mark Render Callback |
// this is the CoreVideo DisplayLink callback notifying the application when the display will need each frame |
// and is called when the DisplayLink is running -- in response, we call our getFrameForTime method |
static CVReturn MyRenderCallback(CVDisplayLinkRef displayLink, |
const CVTimeStamp *inNow, |
const CVTimeStamp *inOutputTime, |
CVOptionFlags flagsIn, |
CVOptionFlags *flagsOut, |
void *displayLinkContext) |
{ |
return [(MyOpenGLView *)displayLinkContext getFrameForTime:inOutputTime flagsOut:flagsOut]; |
} |
#pragma mark |
@implementation MyOpenGLView |
// initialize |
-(void)awakeFromNib |
{ |
movie = nil; |
displayLink = NULL; |
textureContext = NULL; |
currentFrame = NULL; |
pointillizeFilter = nil; |
edgeWorkFilter = nil; |
ciContext = nil; |
// we need a lock around our draw function so two different |
// threads don't try and draw at the same time |
lock = [NSRecursiveLock new]; |
effectFilter = nil; |
effectValue = 1.0; |
} |
// destruction |
- (void)dealloc |
{ |
// make sure the cleanUp routine is |
// called first before going away |
[self cleanUp]; |
[super dealloc]; |
} |
// it is very important that we clean up the rendering |
// objects before the view is disposed, remember that with the |
// display link running you're applications render callback may be |
// called at any time including when the application is quitting or the |
// view is being disposed, additionally you need to make sure you're not |
// consuming OpenGL resources or leaking textures -- this clean up routine |
// makes sure to stop and release everything |
-(void)cleanUp |
{ |
// stop and release the movie |
if (movie){ |
[movie setRate:0.0]; |
SetMovieVisualContext([movie quickTimeMovie], NULL); |
[movie release]; |
movie = nil; |
} |
// it is critical to dispose of the display link |
if (displayLink) { |
CVDisplayLinkStop(displayLink); |
CVDisplayLinkRelease(displayLink); |
displayLink = NULL; |
} |
// don't leak textures |
if (currentFrame) { |
CVOpenGLTextureRelease(currentFrame); |
currentFrame = NULL; |
} |
// release the OpenGL Texture Context |
if (textureContext) { |
CFRelease(textureContext); |
textureContext = NULL; |
} |
// release the Core Image Filters |
if (pointillizeFilter) { |
[pointillizeFilter release]; |
pointillizeFilter = nil; |
} |
if (edgeWorkFilter) { |
[edgeWorkFilter release]; |
edgeWorkFilter = nil; |
} |
// release the Core Image Context |
if (ciContext) { |
[ciContext release]; |
ciContext = nil; |
} |
if (lock) { |
[lock release]; |
lock = nil; |
} |
} |
// do our setup -- this is a good place to create the display link and filters because |
// we're called only once and after the OpenGL context is created and the drawable is attached |
- (void)prepareOpenGL |
{ |
GLint swapInterval = 1; |
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // black background |
// set up the GL contexts swap interval -- passing 1 means that |
// the buffers are swapped only during the vertical retrace of the monitor |
[[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; |
// create a Core Image Context -- in Core Image, images are evaluated to a Core Image context |
// which represents a drawing destination. Core Image contexts are created per window rather than |
// one per view and can be created from an OpenGL graphics context |
// create CGColorSpaceRef needed for the contextWithCGLContext method |
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); |
// create CIContext -- the CIContext object provides an evaluation context for |
// rendering a Core Image image (CIImage) through Quartz 2D or OpenGL |
ciContext = [[CIContext contextWithCGLContext:CGLContextObj([[self openGLContext] CGLContextObj]) // Core Image draws all output into the surface attached to this OpenGL context |
pixelFormat:CGLPixelFormatObj([[self pixelFormat] CGLPixelFormatObj]) // must be the same pixel format used to create the cgl context |
options:[NSDictionary dictionaryWithObjectsAndKeys:(id)colorSpace, kCIContextOutputColorSpace, // dictionary containing color space information |
(id)colorSpace, kCIContextWorkingColorSpace, nil]] retain]; |
// release the colorspace we don't need it anymore |
CGColorSpaceRelease(colorSpace); |
// create CIFilters |
pointillizeFilter = [[CIFilter filterWithName:@"CIPointillize"] retain]; // pointillize filter |
[pointillizeFilter setDefaults]; // set the filter to its default values |
edgeWorkFilter = [[CIFilter filterWithName:@"CIEdgeWork"] retain]; // edgework filter |
[edgeWorkFilter setDefaults]; // set the filter to its default values |
// create display link for the main display |
CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &displayLink); |
if (NULL != displayLink) { |
// set the current display of a display link |
CVDisplayLinkSetCurrentCGDisplay(displayLink, kCGDirectMainDisplay); |
// set the renderer output callback function |
CVDisplayLinkSetOutputCallback(displayLink, &MyRenderCallback, self); |
// we don't activate the display link yet |
} |
// creates a new OpenGL texture context for a specified OpenGL context and pixel format |
QTOpenGLTextureContextCreate(kCFAllocatorDefault, // an allocator to Create functions |
CGLContextObj([[self openGLContext] CGLContextObj]), // the OpenGL context |
CGLPixelFormatObj([[self pixelFormat] CGLPixelFormatObj]), // pixelformat object that specifies buffer types and other attributes of the context |
NULL, // a CF Dictionary of attributes |
&textureContext); // returned OpenGL texture context |
// initally we use the pointillize effect |
effectFilter = pointillizeFilter; |
} |
// good practice to lock around update |
- (void)update |
{ |
[lock lock]; |
[super update]; |
[lock unlock]; |
} |
// adjust the viewport |
- (void)reshape |
{ |
GLfloat minX, minY, maxX, maxY; |
NSRect sceneBounds = [self bounds]; |
NSRect frame = [self frame]; |
minX = NSMinX(sceneBounds); |
minY = NSMinY(sceneBounds); |
maxX = NSMaxX(sceneBounds); |
maxY = NSMaxY(sceneBounds); |
// for best results when using Core Image to render into an OpenGL context follow these guidelines: |
// * ensure that the a single unit in the coordinate space of the OpenGL context represents a single pixel in the output device |
// * the Core Image coordinate space has the origin in the bottom left corner of the screen -- you should configure the OpenGL |
// context in the same way |
// * the OpenGL context blending state is respected by Core Image -- if the image you want to render contains translucent pixels, |
// itÕs best to enable blending using a blend function with the parameters GL_ONE, GL_ONE_MINUS_SRC_ALPHA |
// some typical initialization code for a view with width W and height H |
glViewport(0, 0, GLsizei(frame.size.width), GLsizei(frame.size.height)); // set the viewport |
glMatrixMode(GL_MODELVIEW); // select the modelview matrix |
glLoadIdentity(); // reset it |
glMatrixMode(GL_PROJECTION); // select the projection matrix |
glLoadIdentity(); // reset it |
gluOrtho2D(minX, maxX, minY, maxY); // define a 2-D orthographic projection matrix |
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
glEnable(GL_BLEND); |
} |
// draw |
- (void)drawRect:(NSRect)rect |
{ |
[lock lock]; // prevent drawing from another thread if we're drawing already |
// make the GL context the current context |
[[self openGLContext] makeCurrentContext]; |
// clear to black if nothing else |
glClear(GL_COLOR_BUFFER_BIT); |
if (NULL != currentFrame) { |
// we have a frame so draw something |
NSRect frame = [self frame]; |
CGRect imageRect; |
CIImage *inputImage; |
// creates a Core Image image from the contents of a CVImageBuffer or its subclasses |
// in our case an OpenGL texture-based image buffer of type CVOpenGLTextureRef |
inputImage = [CIImage imageWithCVImageBuffer:currentFrame]; |
// make sure to get the image extent before applying any filters -- it is the |
// rectangle that specifies the x-value of the rectangle origin, the y-value of |
// the rectangle origin, and the width and height in working space coordinates |
imageRect = [inputImage extent]; |
// set the input image and parameter for the effect we're rendering with |
[effectFilter setValue:inputImage forKey:@"inputImage"]; |
[effectFilter setValue:[NSNumber numberWithFloat:effectValue] forKey:@"inputRadius"]; |
// render our resulting image into our context |
[ciContext drawImage: [effectFilter valueForKey:@"outputImage"] |
atPoint: CGPointMake((int)((frame.size.width - imageRect.size.width) * 0.5), |
(int)((frame.size.height - imageRect.size.height) * 0.5)) // use integer coordinates to avoid interpolation |
fromRect:imageRect]; |
} |
glFlush(); |
// give time to the Visual Context so it can release internally held resources for later re-use |
// this function should be called in every rendering pass, after old images have been released, new |
// images have been used and all rendering has been flushed to the screen. |
QTVisualContextTask(textureContext); |
[lock unlock]; |
} |
#pragma mark Display Link |
// getFrameForTime is called from the Display Link callback when it's time for us to check to see |
// if we have a frame available to render -- if we do, draw -- if not, just task the Visual Context and split |
- (CVReturn)getFrameForTime:(const CVTimeStamp*)timeStamp flagsOut:(CVOptionFlags*)flagsOut |
{ |
// there is no autorelease pool when this method is called because it will be called from another thread |
// it's important to create one or you will leak objects |
NSAutoreleasePool *pool = [NSAutoreleasePool new]; |
// check for new frame |
if (NULL != textureContext && QTVisualContextIsNewImageAvailable(textureContext, timeStamp)) { |
// if we have a previous frame release it |
if (NULL != currentFrame) { |
CVOpenGLTextureRelease(currentFrame); |
currentFrame = NULL; |
} |
// get a "frame" (image buffer) from the Visual Context, indexed by the provided time |
OSStatus status = QTVisualContextCopyImageForTime(textureContext, NULL, timeStamp, ¤tFrame); |
// the above call may produce a null frame so check for this first |
// if we have a frame, then draw it |
if ((noErr == status) && (NULL != currentFrame)) { |
[self drawRect:NSZeroRect]; |
} |
} |
[pool release]; |
return kCVReturnSuccess; |
} |
#pragma mark Movie |
// open a Movie File and instantiate a QTMovie object |
-(void)openMovie:(NSString*)path |
{ |
if (textureContext != nil) { |
// if we already have a QTMovie release it |
if (nil != movie) [movie release]; |
movie = [[QTMovie alloc] initWithFile:path error:nil]; |
// set Movie to loop |
[movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsAttribute]; |
// targets a Movie to render into a visual context |
SetMovieVisualContext([movie quickTimeMovie], textureContext); |
// play the Movie |
[movie setRate:1.0]; |
// set the window title from the Movie if it has a name associated with it |
[[self window] setTitle:[movie attributeForKey:QTMovieDisplayNameAttribute]]; |
} |
} |
#pragma mark Accessors |
// return the display link ref |
-(CVDisplayLinkRef)displayLink; |
{ |
return displayLink; |
} |
// both of these are hooked up via bindings |
// set the effect parameter value |
-(IBAction)setEffectValue:(id)sender |
{ |
effectValue = [sender floatValue]; |
} |
// set which effect we're going to use to render |
-(IBAction)setEffect:(id)sender |
{ |
if ([sender intValue]) { |
effectFilter = edgeWorkFilter; |
} else { |
effectFilter = pointillizeFilter; |
} |
} |
@end |
Copyright © 2009 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2009-10-27