#import "Framer.h" #include "Process_stub.h" // The framer takes the current frames from the camera // and processes them into NSImages @implementation Framer - (id) initWithCamera:(Camera*)camera { self = [super init]; // Stash the camera for later. mCamera = camera; return self; } // Get an image from the camera. - (NSImage*) getImage:(NSInteger)mode enableBlur:(NSInteger)iBlurEnable enableInvert:(NSInteger)iInvertOutput thresholdLow:(float)thresholdLow thresholdHigh:(float)thresholdHigh { // NSImage* image = [[NSImage alloc] initWithContentsOfFile: @"/Users/benl/tmp/lena.bmp"]; CVImageBufferRef currentBuffer = [mCamera currentBuffer]; // Make a CIImage, which wraps the raw, compressed data in the CVImageBuffer. CIImage* ciImage = [CIImage imageWithCVImageBuffer:currentBuffer]; // The image from my iSight comes as 1280x1024. // Scale this down to a more sensible size. // TODO: choose this based on the size instead of using a static scaling factor. if ([ciImage extent].size.width > 800) { CIFilter* filter = [CIFilter filterWithName:@"CIAffineTransform"]; NSAffineTransform* transform = [NSAffineTransform transform]; [transform scaleBy: 0.5]; [filter setDefaults]; [filter setValue: ciImage forKey: @"inputImage"]; [filter setValue: transform forKey: @"inputTransform"]; ciImage = [filter valueForKey: @"outputImage"]; } CGRect extent = [ciImage extent]; // NSLog(@"image size %d %d", (int)[ciImage extent].size.width, // (int)[ciImage extent].size.height); // Create the CGContext which we render the final image into. // This holds the actual pixel buffer. size_t width = (size_t)extent.size.width; size_t height = (size_t)extent.size.height; size_t bitsPerComponent = 8; size_t bytesPerPixel = 4; size_t bytesPerRow = bytesPerPixel * width; size_t dataSize = height * bytesPerRow; CGColorSpaceRef colorSpace = [self getScreenColorSpace]; CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Host; uint8_t* data = malloc(dataSize); CGContextRef cgContext = CGBitmapContextCreate ( data, width, height , bitsPerComponent , bytesPerRow , colorSpace, bitmapInfo); // Create the CIContext which will render the compressed image data into the bitmap. NSDictionary* ciContextOptions = [[NSDictionary alloc] initWithObjectsAndKeys: (id)colorSpace, kCIContextWorkingColorSpace, (id)colorSpace, kCIContextOutputColorSpace, nil]; CIContext* ciContext = [CIContext contextWithCGContext: cgContext options: ciContextOptions]; // Tell the context to draw its image. CGPoint point = {0, 0}; CGSize size = {width, height}; CGRect rect = {point, size}; [ciContext drawImage: ciImage atPoint: point fromRect: rect]; // At this point we've got the pixel data sitting in the buffer. // Now we can fool around with it directly. processImage ((int)mode, (int)iBlurEnable, (int)iInvertOutput , thresholdLow, thresholdHigh, width, height, data); // Make an image from the pixel data in the context. CGImageRef cgImage = CGBitmapContextCreateImage(cgContext); // Make the NSImage out of the CGImage // We need a NSImage to display in the window. NSSize nsSize = { extent.size.width, extent.size.height }; // NSSize nsSize = { 512, 512 }; NSImage* image = [[NSImage alloc] initWithCGImage: cgImage size: nsSize]; // TODO: release the cgImage when we're done with it. // Cleanup CGImageRelease (cgImage); CGContextRelease (cgContext); CGColorSpaceRelease (colorSpace); free(data); CVBufferRelease (currentBuffer); return image; } - (CGColorSpaceRef)getScreenColorSpace { CMProfileRef systemProfile = NULL; OSStatus status = CMGetSystemProfile(&systemProfile); NSParameterAssert( noErr == status); CGColorSpaceRef colorSpace = CGColorSpaceCreateWithPlatformColorSpace(systemProfile); CMCloseProfile(systemProfile); return colorSpace; } @end