/* File: Buffer.m Abstract: Implementation file for Buffer class. Contains code that creates an NSBitmapImageRep object to hold the screen bits. The NSBitmapImageRep is then used to create an NSImage to write the image to a TIFF file on disk. Version: 1.0 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) 2007 Apple Inc. All Rights Reserved. */ #import "Buffer.h" #import "DisplayUtils.h" #import "ImageUtils.h" @interface Buffer (PrivateMethods) -(void)flipImageData; -(CGImageRef)createRGBImageFromBufferData; @end @implementation Buffer (PrivateMethods) /* * perform an in-place swap from Quadrant 1 to Quadrant III format * (upside-down PostScript/GL to right side up QD/CG raster format) * We do this in-place, which requires more copying, but will touch * only half the pages. (Display grabs are BIG!) * * Pixel reformatting may optionally be done here if needed. */ -(void)flipImageData { long top, bottom; void * buffer; void * topP; void * bottomP; void * base; long rowBytes; top = 0; bottom = mHeight - 1; base = mData; rowBytes = [self bytesPerRow]; buffer = malloc(rowBytes); NSAssert( buffer != nil, @"malloc failure"); while ( top < bottom ) { topP = (void *)((top * rowBytes) + (intptr_t)base); bottomP = (void *)((bottom * rowBytes) + (intptr_t)base); /* * Save and swap scanlines. * * This code does a simple in-place exchange with a temp buffer. * If you need to reformat the pixels, replace the first two bcopy() * calls with your own custom pixel reformatter. */ bcopy( topP, buffer, rowBytes ); bcopy( bottomP, topP, rowBytes ); bcopy( buffer, bottomP, rowBytes ); ++top; --bottom; } free( buffer ); } // Create a RGB CGImageRef from our buffer data -(CGImageRef)createRGBImageFromBufferData { CGColorSpaceRef cSpace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB); NSAssert( cSpace != NULL, @"CGColorSpaceCreateWithName failure"); CGContextRef bitmap = CGBitmapContextCreate(mData, mWidth, mHeight, 8, [self bytesPerRow], cSpace, #if __BIG_ENDIAN__ kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big /* XRGB Big Endian */); #else kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little /* XRGB Little Endian */); #endif NSAssert( bitmap != NULL, @"CGBitmapContextCreate failure"); // Get rid of color space CFRelease(cSpace); // Make an image out of our bitmap; does a cheap vm_copy of the // bitmap CGImageRef image = CGBitmapContextCreateImage(bitmap); NSAssert( image != NULL, @"CGBitmapContextCreate failure"); // Get rid of bitmap CFRelease(bitmap); return image; } @end @implementation Buffer - (id) init { //Make sure client goes through designated initializer [self doesNotRecognizeSelector:_cmd]; return nil; } // Create a buffer (NSBitmapImageRep) to hold the bits for // a full-screen display. - (Buffer *) initFullScreenSizedBuffer { if (self = [super init]) { // Get display dimensions [DisplayUtils getMainDisplayDimensions:&mWidth displayHeight:&mHeight]; mByteWidth = mWidth * 4; // Assume 4 bytes/pixel for now mByteWidth = (mByteWidth + 3) & ~3; // Align to 4 bytes mData = malloc(mByteWidth * mHeight); NSAssert( mData != 0, @"malloc failed"); } return self; } // Returns the base address in memory of the actual // buffer from our bitmap image representation -(void *)baseAddress { return mData; } // Returns the bytes per row for the actual buffer in // the bitmap image representation -(long)bytesPerRow { return mByteWidth; } // Create a TIFF file on the desktop from our data buffer -(void)createTIFFImageFileOnDesktop { // glReadPixels writes things from bottom to top, but we // need a top to bottom representation, so we must flip // the buffer contents. [self flipImageData]; // Create a Quartz image from our pixel buffer bits CGImageRef imageRef = [self createRGBImageFromBufferData]; NSAssert( imageRef != 0, @"cgImageFromPixelBuffer failed"); // Make full pathname to the desktop directory NSString *desktopDirectory = nil; NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDesktopDirectory, NSUserDomainMask, YES); if ([paths count] > 0) { desktopDirectory = [paths objectAtIndex:0]; } NSMutableString *fullFilePathStr = [NSMutableString stringWithString:desktopDirectory]; NSAssert( fullFilePathStr != nil, @"stringWithString failed"); [fullFilePathStr appendString:@"/ScreenSnapshot.tiff"]; NSString *finalPath = [NSString stringWithString:fullFilePathStr]; NSAssert( finalPath != nil, @"stringWithString failed"); CFURLRef url = CFURLCreateWithFileSystemPath ( kCFAllocatorDefault, (CFStringRef)finalPath, kCFURLPOSIXPathStyle, false); NSAssert( url != 0, @"CFURLCreateWithFileSystemPath failed"); // Save our screen bits to an image file on disk [ImageUtils createTIFFImageFileFromCGImage:imageRef destFullPathURL:url]; CGImageRelease(imageRef); CFRelease(url); } -(void)dealloc { /* Get rid of data */ free(mData); [super dealloc]; } @end