Sources/Classes/Application/View/Toolkit/OpenGLView.m

/*
     File: OpenGLView.m
 Abstract: 
 OpenGL view class with text labels and added utility methods.
 
  Version: 1.2
 
 Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
 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 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.
 
 Copyright (C) 2013 Apple Inc. All Rights Reserved.
 
 */
 
//---------------------------------------------------------------------------------------
 
//---------------------------------------------------------------------------------------
 
#import "QTMediaSampleNotifications.h"
 
#import "OpenGLView.h"
 
//---------------------------------------------------------------------------------------
 
//---------------------------------------------------------------------------------------
 
enum OpenGLViewFlags
{
    kOpenGLViewResized = 0,
    kOpenGLViewStopObjectRotation,
    kOpenGLViewCaptureEnabled,
    kOpenGLViewCaptureWriting,
    kOpenGLViewDisplayCounter,
    kOpenGLViewDisplayPrefTimer,
    kOpenGLViewDisplayRenderer,
    kOpenGLViewDisplayBounds
};
 
typedef enum OpenGLViewFlags OpenGLViewFlags;
 
//---------------------------------------------------------------------------------------
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
 
//---------------------------------------------------------------------------------------
 
@implementation OpenGLView
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Post Notification
 
//---------------------------------------------------------------------------------------
 
- (void) postOpenGLViewNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(viewWillTerminate:)
                                                 name:@"NSApplicationWillTerminateNotification"
                                               object:NSApp];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(suspendObjectRotation:)
                                                 name:QTMediaSampleAuthorIsSuspended
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(resumeObjectRotation:)
                                                 name:QTMediaSampleAuthorIsResumed
                                               object:nil];
} // postOpenGLViewNotifications
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Labels Initialization
 
//---------------------------------------------------------------------------------------
 
- (void) initPrefTimerLabel:(const NSRect *)theBounds
                  textColor:(NSColor *)theTextColor
                   boxColor:(NSColor *)theBoxColor
                borderColor:(NSColor *)theBorderColor
{
    NSPoint prefTimerInfoCoordinates = NSMakePoint(10.0f, 32.0f);
    
    mpLabelPrefTimer = [[OpenGLPrefTimerLabel alloc] initLabelWithFormat:nil
                                                                fontName:@"Helvetica"
                                                                fontSize:20.0f
                                                               textColor:theTextColor
                                                                boxColor:theBoxColor
                                                             borderColor:theBorderColor
                                                             coordinates:&prefTimerInfoCoordinates
                                                                  bounds:theBounds];
} // initPrefTimerLabel
 
//---------------------------------------------------------------------------------------
 
- (void) initRendererLabel:(const NSRect *)theBounds
                 textColor:(NSColor *)theTextColor
                  boxColor:(NSColor *)theBoxColor
               borderColor:(NSColor *)theBorderColor
{
    NSRect bounds = [self bounds];
    
    NSPoint infoCoordinates = NSMakePoint(10.0f, bounds.size.height - 52.0f);
    
    mpLabelRenderer = [[OpenGLRendererLabel alloc] initLabelWithFontName:@"Helvetica"
                                                                fontSize:12.0
                                                               textColor:theTextColor
                                                                boxColor:theBoxColor
                                                             borderColor:theBorderColor
                                                             coordinates:&infoCoordinates
                                                                  bounds:theBounds];
} // initRendererLabel
 
//---------------------------------------------------------------------------------------
 
- (void) initViewBoundsLabel:(const NSRect *)theBounds
                   textColor:(NSColor *)theTextColor
                    boxColor:(NSColor *)theBoxColor
                 borderColor:(NSColor *)theBorderColor
{
    NSPoint infoCoordinates = NSMakePoint(10.0f, 10.0f);
    
    mpLabelViewBounds = [[OpenGLViewBoundsLabel alloc] initLabelWithFormat:nil
                                                                  fontName:@"Helvetica"
                                                                  fontSize:12.0
                                                                 textColor:theTextColor
                                                                  boxColor:theBoxColor
                                                               borderColor:theBorderColor
                                                               coordinates:&infoCoordinates
                                                                    bounds:theBounds];
} // initViewBoundsLabel
 
//---------------------------------------------------------------------------------------
 
- (void) initLabels:(const NSRect *)theBounds
{
    NSColor *textColor = [NSColor colorWithDeviceRed:1.0f
                                               green:1.0f
                                                blue:1.0f
                                               alpha:1.0f];
    
    NSColor *boxColor = [NSColor colorWithDeviceRed:0.5f
                                              green:0.5f
                                               blue:0.5f
                                              alpha:0.5f];
    
    NSColor *borderColor = [NSColor colorWithDeviceRed:0.8f
                                                 green:0.8f
                                                  blue:0.8f
                                                 alpha:0.8f];
    
    [self initPrefTimerLabel:theBounds
                   textColor:textColor
                    boxColor:boxColor
                 borderColor:borderColor];
    
    [self initRendererLabel:theBounds
                  textColor:textColor
                   boxColor:boxColor
                borderColor:borderColor];
    
    [self initViewBoundsLabel:theBounds
                    textColor:textColor
                     boxColor:boxColor
                  borderColor:borderColor];
} // initLabels
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Designated initializer
 
//---------------------------------------------------------------------------------------
//
// Create an OpenGL Context to use - i.e. init the animated view superclass
//
//---------------------------------------------------------------------------------------
 
- (id) initWithFrame:(NSRect)theFrame
         pixelFormat:(NSOpenGLPixelFormat *)thePixelFormat
{
    self = [super initWithFrame:theFrame
                    pixelFormat:thePixelFormat];
    
    if( self )
    {
        // Instantiate label objects for displaying
        // performance timer and view bounds
        
        [self initLabels:&theFrame];
        
        mpSnapshot = nil;
        mpCapture  = nil;
        
        mpDirectory = nil;
        
        m_ViewFlags[kOpenGLViewResized]            = NO;
        m_ViewFlags[kOpenGLViewStopObjectRotation] = NO;
        m_ViewFlags[kOpenGLViewCaptureEnabled]     = NO;
        m_ViewFlags[kOpenGLViewCaptureWriting]     = NO;
        
        m_ViewFlags[kOpenGLViewDisplayPrefTimer] = YES;
        m_ViewFlags[kOpenGLViewDisplayRenderer]  = YES;
        m_ViewFlags[kOpenGLViewDisplayBounds]    = YES;
        
        [self postOpenGLViewNotifications];
    } // if
    
    return( self );
} // initWithFrame
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Deallocating Resources
 
//---------------------------------------------------------------------------------------
 
- (void) releaseOpenGLViewCapture
{
    if( mpCapture )
    {
        [mpCapture release];
        
        mpCapture = nil;
    } // if
} // releaseOpenGLViewCapture
 
//---------------------------------------------------------------------------------------
 
- (void) releaseOpenGLViewSnapshot
{
    if( mpSnapshot )
    {
        [mpSnapshot release];
        
        mpSnapshot = nil;
    } // if
} // releaseOpenGLViewSnapshot
 
//---------------------------------------------------------------------------------------
 
- (void) releaseOpenGLPrefTimerLabel
{
    if( mpLabelPrefTimer )
    {
        [mpLabelPrefTimer release];
        
        mpLabelPrefTimer = nil;
    } // if
} // releaseOpenGLPrefTimerLabel
 
//---------------------------------------------------------------------------------------
 
- (void) releaseOpenGLRendererLabel
{
    if( mpLabelRenderer )
    {
        [mpLabelRenderer release];
        
        mpLabelRenderer = nil;
    } // if
} // releaseOpenGLRendererLabel
 
//---------------------------------------------------------------------------------------
 
- (void) releaseOpenGLViewBoundsLabel
{
    if( mpLabelViewBounds )
    {
        [mpLabelViewBounds release];
        
        mpLabelViewBounds = nil;
    } // if
} // releaseOpenGLViewBoundsLabel
 
//---------------------------------------------------------------------------------------
 
- (void) releaseMovieDirectoryPathname
{
    if( mpDirectory )
    {
        [mpDirectory release];
        
        mpDirectory = nil;
    } // if
} // releaseMovieDirectoryPathname
 
//---------------------------------------------------------------------------------------
 
- (void) cleanUpOpenGLView
{
    // Release the renderer's label
    
    [self releaseOpenGLRendererLabel];
    
    // Release the view bounds' label
    
    [self releaseOpenGLViewBoundsLabel];
    
    // Release perf timer's label
    
    [self releaseOpenGLPrefTimerLabel];
    
    // Release the view snapshot object
    
    [self releaseOpenGLViewSnapshot];
    
    // Release the view capture object
    
    [self releaseOpenGLViewCapture];
    
    // Release the directory pathname string
    
    [self releaseMovieDirectoryPathname];
} // cleanUpOpenGLView
 
//---------------------------------------------------------------------------------------
 
- (void) dealloc
{
    [self cleanUpOpenGLView];
    
    // Dealloc the superclass
    
    [super dealloc];
} // dealloc
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Accessor
 
//---------------------------------------------------------------------------------------
 
- (BOOL) viewResized
{
    return( m_ViewFlags[kOpenGLViewResized] );
} // viewResized
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Capturing from a View
 
//---------------------------------------------------------------------------------------
 
- (void) viewCaptureCreate
{
    if( mpCapture == nil )
    {
        NSRect bounds = [self bounds];
        
        mpCapture = [[OpenGLViewCapture alloc] initViewCaptureWithFrame:&bounds
                                                              directory:mpDirectory
                                                                 prefix:@"capture"];
    } // if
    else
    {
        [mpCapture setMovieDirectory:mpDirectory];
    } // else
    
    if( mpCapture )
    {
        m_ViewFlags[kOpenGLViewCaptureEnabled] = YES;
    } // if
} // viewCaptureCreate
 
//---------------------------------------------------------------------------------------
 
- (void) viewCaptureEnable:(NSString *)theMovieDirectory
{
    if( theMovieDirectory )
    {
        [self releaseMovieDirectoryPathname];
        
        if( mpDirectory == nil )
        {
            mpDirectory = [[NSString alloc] initWithString:theMovieDirectory];
        } // if
        
        [self viewCaptureCreate];
    } // if
} // viewCaptureEnable
 
//---------------------------------------------------------------------------------------
 
- (void) viewCaptureSetDirectory:(NSString *)theMovieDirectory
{
    [mpCapture setMovieDirectory:theMovieDirectory];
} // viewCaptureSetDirectory
 
//---------------------------------------------------------------------------------------
 
- (void) viewCaptureDisable
{
    [self releaseOpenGLViewCapture];
    
    if( mpCapture == nil )
    {
        m_ViewFlags[kOpenGLViewCaptureEnabled] = NO;
    } // if
} // viewCaptureDisable
 
//---------------------------------------------------------------------------------------
 
- (void) viewCaptureStart
{
    m_ViewFlags[kOpenGLViewStopObjectRotation] = NO;
    
    [mpCapture start];
} // viewCaptureRestart
 
//---------------------------------------------------------------------------------------
 
- (void) viewCaptureStop
{
    m_ViewFlags[kOpenGLViewStopObjectRotation] = YES;
    
    [mpCapture stop];
} // viewCaptureStop
 
//---------------------------------------------------------------------------------------
 
- (void) viewCapturePause
{
    [mpCapture pause];
} // viewCapturePause
 
//---------------------------------------------------------------------------------------
 
- (NSUInteger) viewCaptureCount
{
    return( [mpCapture count] );
} // viewCaptureCount
 
//---------------------------------------------------------------------------------------
 
- (BOOL) viewCaptureIsSuspended
{
    return( [mpCapture isSuspended] );
} // viewCaptureIsSuspended
 
//---------------------------------------------------------------------------------------
 
- (void) viewCaptureEnableColorCorrection
{
    [mpCapture enableColorCorrection];
} // viewCaptureEnableColorCorrection
 
//---------------------------------------------------------------------------------------
 
- (void) viewCaptureDisableColorCorrection
{
    [mpCapture disableColorCorrection];
} // viewCaptureDisableColorCorrection
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Capturing from a View
 
//---------------------------------------------------------------------------------------
 
- (void) viewSnapshot:(NSString *)theDocPath
                 type:(NSNumber *)theDocFormat
          compression:(NSNumber *)theDocCompression
                title:(NSString *)theDocTitle
               author:(NSString *)theDocAuthor
              subject:(NSString *)theDocSubject
              creator:(NSString *)theDocCreator
{
    if( mpSnapshot == nil )
    {
        NSRect frame = [self bounds];
        
        mpSnapshot = [[OpenGLViewSnapshot alloc] initViewSnapshotWithFrame:&frame
                                                                      view:self];
    } // if
    
    [mpSnapshot setDocumentTitle:theDocTitle];
    [mpSnapshot setDocumentAuthor:theDocAuthor];
    [mpSnapshot setDocumentSubject:theDocSubject];
    [mpSnapshot setDocumentCreator:theDocCreator];
    
    if( theDocFormat )
    {
        [mpSnapshot setFormat:[theDocFormat unsignedIntValue]];
    } // if
    
    if( theDocCompression )
    {
        [mpSnapshot setCompression:[theDocCompression floatValue]];
    } // if
    
    [mpSnapshot saveAs:theDocPath];
} // viewSnapshot
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Showing/Hiding Labels
 
//---------------------------------------------------------------------------------------
 
- (void) prefTimerDisplayLabel:(const BOOL)theDisplayFlag
{
    m_ViewFlags[kOpenGLViewDisplayPrefTimer] = theDisplayFlag;
} // prefTimerLabelSetDisplay
 
//---------------------------------------------------------------------------------------
 
- (void) rendererDisplayLabel:(const BOOL)theDisplayFlag
{
    m_ViewFlags[kOpenGLViewDisplayRenderer] = theDisplayFlag;
} // rendererLabelSetDisplay
 
//---------------------------------------------------------------------------------------
 
- (void) viewBoundsDisplayLabel:(const BOOL)theDisplayFlag
{
    m_ViewFlags[kOpenGLViewDisplayBounds] = theDisplayFlag;
} // viewBoundsSetDisplay
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Preformance Timer Utilities
 
//---------------------------------------------------------------------------------------
 
- (void) prefTimerEnable
{
    [mpLabelPrefTimer labelSetNeedsDisplay:YES];
} // prefTimerEnable
 
//---------------------------------------------------------------------------------------
 
- (void) prefTimerDisable
{
    [mpLabelPrefTimer labelSetNeedsDisplay:NO];
} // prefTimerDisable
 
//---------------------------------------------------------------------------------------
 
- (void) prefTimerMoveTo:(const NSPoint *)thePoint
{
    [mpLabelPrefTimer moveTo:thePoint];
} // prefTimerMoveTo
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark View Updates
 
//---------------------------------------------------------------------------------------
//
// Handles resizing of OpenGL view, if the window dimensions change, window dimensions
// update, viewports reset, and projection matrix update.
//
//---------------------------------------------------------------------------------------
 
- (void) viewResize
{
    m_ViewFlags[kOpenGLViewResized] = [self viewUpdate];
    
    if( m_ViewFlags[kOpenGLViewResized] )
    {
        // OpenGL text objects needs to know the view bounds
        // have changed as well.
        
        NSRect viewBounds = [self viewBounds];
        
        // Get a new backing store for our view's snapshot
        
        [mpSnapshot setFrame:&viewBounds];
        
        // Resize the capture object
        
        if( m_ViewFlags[kOpenGLViewCaptureEnabled] )
        {
            [mpCapture setBounds:&viewBounds];
        } // if
        
        // Notify all labels that their bounds have changed
        
        [mpLabelViewBounds viewSetBounds:&viewBounds];
        [mpLabelRenderer   viewSetBounds:&viewBounds];
        [mpLabelPrefTimer  viewSetBounds:&viewBounds];
    } // if
} // viewResize
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Draw Utilities
 
//---------------------------------------------------------------------------------------
 
- (void) drawBegin
{
    // Make the OpenGL context the current context
    
    [self contextMakeCurrent];
    
    // Forces projection matrix update (does test for size changes)
    
    [self viewResize];
    
    // Clear our drawable
    
    [self viewClear];
} // drawBegin
 
//---------------------------------------------------------------------------------------
//
// Display the view bounds label
//
//---------------------------------------------------------------------------------------
 
- (void) drawViewBoundsLabel
{
    if( m_ViewFlags[kOpenGLViewDisplayBounds] )
    {
        [mpLabelViewBounds labelDraw];
    } // if
} // drawViewBoundsLabel
 
//---------------------------------------------------------------------------------------
//
// Display the pref timer label
//
//---------------------------------------------------------------------------------------
 
- (void) drawPrefTimerLabel
{
    if( m_ViewFlags[kOpenGLViewDisplayPrefTimer] )
    {
        [mpLabelPrefTimer labelSetNeedsUpdate:[self rotation]];
        [mpLabelPrefTimer labelDraw];
    } // if
} // drawPrefTimerLabel
 
//---------------------------------------------------------------------------------------
//
// Display the renderer label
//
//---------------------------------------------------------------------------------------
 
- (void) drawRendererLabel
{
    if( m_ViewFlags[kOpenGLViewDisplayRenderer] )
    {
        [mpLabelRenderer labelDraw];
    } // if
} // drawRendererLabel
 
//---------------------------------------------------------------------------------------
 
- (void) drawEnd
{
    // Take a snapshot of the view
    
    [mpSnapshot snapshot];
    
    // Start recording from a frame
    
    m_ViewFlags[kOpenGLViewCaptureWriting] = [mpCapture capture];
    
    // Draw the view bounds
    
    [self drawViewBoundsLabel];
    
    // Draw the pref timer
    
    [self drawPrefTimerLabel];
    
    // Draw the renderer's info
    
    [self drawRendererLabel];
    
    // Flush the current context
    
    [self contextFlushBuffer];
} // drawEnd
 
//---------------------------------------------------------------------------------------
 
- (BOOL) isWriting
{
    return m_ViewFlags[kOpenGLViewCaptureWriting];
} // isWriting
 
//---------------------------------------------------------------------------------------
 
#pragma mark -
#pragma mark Notification
 
//---------------------------------------------------------------------------------------
//
// It's important to clean up our rendering objects before we terminate -- Cocoa will
// not specifically release everything on application termination, so we explicitly
// call our clean up routine ourselves.
//
//---------------------------------------------------------------------------------------
 
- (void) viewWillTerminate:(NSNotification *)notification
{
    [self cleanUpOpenGLView];
} // viewWillTerminate
 
//---------------------------------------------------------------------------------------
//
// When flushing media author queue one needs to stop object rotation.
//
//---------------------------------------------------------------------------------------
 
- (void) suspendObjectRotation:(NSNotification *)notification
{
    if( !m_ViewFlags[kOpenGLViewStopObjectRotation] )
    {
        [self rotationStop];
        
        m_ViewFlags[kOpenGLViewStopObjectRotation] = YES;
    } // if
} // suspendObjectRotation
 
//---------------------------------------------------------------------------------------
//
// When media sample authoring is restarted, one needs to restart object rotation.
//
//---------------------------------------------------------------------------------------
 
- (void) resumeObjectRotation:(NSNotification *)notification
{
    if( m_ViewFlags[kOpenGLViewStopObjectRotation] && !m_ViewFlags[kOpenGLViewCaptureWriting] )
    {
        [self rotationStart];
        
        m_ViewFlags[kOpenGLViewStopObjectRotation] = NO;
    } // if
} // resumeObjectRotation
 
//---------------------------------------------------------------------------------------
 
@end
 
//---------------------------------------------------------------------------------------
 
//---------------------------------------------------------------------------------------