Files
FreeJ/xcode/CVLayerController.mm
xant 60ec86c556 removed some more dead code
+ minor cleanings along the way
2010-08-09 22:20:02 +02:00

710 lines
22 KiB
Plaintext

/* FreeJ
* (c) Copyright 2009 Andrea Guzzo <xant@dyne.org>
*
* This source code is free software; you can redistribute it and/or
* modify it under the terms of the GNU Public License as published
* by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* Please refer to the GNU Public License for more details.
*
* You should have received a copy of the GNU Public License along with
* this source code; if not, write to:
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#import <CVLayerController.h>
#import <CVLayerView.h>
#import <CIAlphaFade.h>
#import <CVFilterPanel.h>
#import <QTKit/QTMovie.h>
#import <CVFilterInstance.h>
/* Utility to set a SInt32 value in a CFDictionary
*/
static OSStatus SetNumberValue(CFMutableDictionaryRef inDict,
CFStringRef inKey,
SInt32 inValue)
{
CFNumberRef number;
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue);
if (NULL == number) return coreFoundationUnknownErr;
CFDictionarySetValue(inDict, inKey, number);
CFRelease(number);
return noErr;
}
@implementation CVLayerController : NSObject
- (void)awakeFromNib
{
//[self init];
}
- (id)init
{
return [self initWithContext:nil];
}
- (id)initWithContext:(CFreej *)ctx
{
CGLPixelFormatObj pFormat;
GLint npix;
const int attrs[2] = { kCGLPFADoubleBuffer, NULL};
CGLError err = CGLChoosePixelFormat (
(CGLPixelFormatAttribute *)attrs,
&pFormat,
&npix
);
freej = ctx;
err = CGLCreateContext(pFormat , NULL, &glContext);
lock = [[NSRecursiveLock alloc] init];
[layerView setNeedsDisplay:NO];
layer = NULL;
doFilters = true;
currentFrame = NULL;
posterImage = NULL;
doPreview = YES;
imageParams = [[NSMutableDictionary dictionary] retain];
return self;
}
/*
- (void)setContext:(CFreej *)ctx
{
freej = ctx;
}
*/
- (void)dealloc
{
[colorCorrectionFilter release];
[compositeFilter release];
[alphaFilter release];
[exposureAdjustFilter release];
[rotateFilter release];
[scaleFilter release];
[translateFilter release];
///[timeCodeOverlay release];
if (currentFrame)
CVOpenGLTextureRelease(currentFrame);
if (imageParams)
[imageParams release];
[lock release];
[super dealloc];
}
- (void)prepareOpenGL
{
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
// Setup the timecode overlay
/*
NSDictionary *fontAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:[NSFont labelFontOfSize:24.0f], NSFontAttributeName,
[NSColor colorWithCalibratedRed:1.0f green:0.2f blue:0.2f alpha:0.60f], NSForegroundColorAttributeName,
nil];
*/
//timeCodeOverlay = [[TimeCodeOverlay alloc] initWithAttributes:fontAttributes targetSize:NSMakeSize(720.0,480.0 / 4.0)]; // text overlay will go in the bottom quarter of the display
GLint params[] = { 1 };
CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, params );
[pool release];
}
- (void)feedFrame:(CVPixelBufferRef)frame
{
CIImage *renderedImage = nil;
Layer *fjLayer = NULL;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (!filtersInitialized)
[self initFilters]; // initialize on first use
if (layer)
fjLayer = layer->fjLayer();
else
return;
[lock lock];
if (currentFrame)
CVPixelBufferRelease(currentFrame);
CVReturn err = CVPixelBufferCreate (
NULL,
fjLayer->geo.w,
fjLayer->geo.h,
k32ARGBPixelFormat,
NULL,
&currentFrame
);
//currentFrame = CVPixelBufferRetain(frame);
// TODO - check error code
//fjLayer->lock();
CIImage *inputImage = [CIImage imageWithCVImageBuffer:frame];
CGRect bounds = CGRectMake(0, 0, fjLayer->geo.w, fjLayer->geo.h);
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CVPixelBufferLockBaseAddress(currentFrame, 0);
// ensure to start drawing on a black background
memset(CVPixelBufferGetBaseAddress(currentFrame), 0, CVPixelBufferGetDataSize(currentFrame));
CGContextRef context = CGBitmapContextCreateWithData(
CVPixelBufferGetBaseAddress(currentFrame),
fjLayer->geo.w,
fjLayer->geo.h,
8,
fjLayer->geo.w*4,
colorSpace,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big,
NULL,
NULL
);
CVPixelBufferUnlockBaseAddress(currentFrame, 0);
if (!context) {
//fjLayer->unlock();
// todo - error messages
return;
}
NSDictionary *ciContextOptions = [NSDictionary dictionaryWithObject:(NSString*)kCGColorSpaceGenericRGB
forKey: kCIContextOutputColorSpace];
CIContext *ciCtx = [CIContext contextWithCGContext:context options:ciContextOptions];
CGContextRelease(context);
if (doFilters) {
[colorCorrectionFilter setValue:inputImage forKey:@"inputImage"];
[exposureAdjustFilter setValue:[colorCorrectionFilter valueForKey:@"outputImage"] forKey:@"inputImage"];
[alphaFilter setValue:[exposureAdjustFilter valueForKey:@"outputImage"]
forKey:@"inputImage"];
[rotateFilter setValue:[alphaFilter valueForKey:@"outputImage"] forKey:@"inputImage"];
if (fjLayer && (fjLayer->geo.x || fjLayer->geo.y)) {
NSAffineTransform *translateTransform = [NSAffineTransform transform];
[translateTransform translateXBy:fjLayer->geo.x yBy:fjLayer->geo.y];
[translateFilter setValue:translateTransform forKey:@"inputTransform"];
[translateFilter setValue:[rotateFilter valueForKey:@"outputImage"] forKey:@"inputImage"];
renderedImage = [translateFilter valueForKey:@"outputImage"];
} else {
renderedImage = [rotateFilter valueForKey:@"outputImage"];
}
} else {
renderedImage = inputImage;
}
[ciCtx drawImage:renderedImage inRect:bounds fromRect:bounds];
[lock unlock];
[pool release];
}
- (IBAction)toggleFilters:(id)sender
{
doFilters = doFilters?false:true;
}
- (IBAction)toggleVisibility:(id)sender
{
if (layer)
if (layer->isActive())
layer->deactivate();
else
layer->activate();
}
- (IBAction)togglePreview:(id)sender
{
doPreview = doPreview?NO:YES;
}
- (void) setLayer:(CVCocoaLayer *)lay
{
if (lay) {
layer = lay;
//layer->fps.set(30);
}
}
- (NSDictionary *)imageParams
{
return imageParams;
}
- (IBAction)setValue:(NSNumber *)value forImageParameter:(NSString *)parameter
{
Layer *fjLayer = NULL;
NSAutoreleasePool *pool;
float deg = 0;
float x = 0;
float y = 0;
NSAffineTransform *rotateTransform;
NSAffineTransform *rototranslateTransform;
NSString *paramName = NULL;
pool = [[NSAutoreleasePool alloc] init];
if (layer)
fjLayer = layer->fjLayer();
else
return;
// TODO - optimize the logic in this routine ... it's becoming huge!!
// to prevent its run() method to try rendering
// a frame while we change filter parameters
[lock lock];
#if 0
switch([sender tag])
{
case 0: // opacity (AlphaFade)
[alphaFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"outputOpacity"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender label]];
break;
case 1: //brightness (ColorCorrection)
[colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputBrightness"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender label]];
break;
case 2: // saturation (ColorCorrection)
[colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputSaturation"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender label]];
break;
case 3: // contrast (ColorCorrection)
[colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputContrast"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender label]];
break;
case 4: // exposure (ExposureAdjust)
[exposureAdjustFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputEV"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender label]];
break;
case 5: // rotate
rotateTransform = [NSAffineTransform transform];
[rotateTransform rotateByDegrees:[sender floatValue]];
deg = ([sender floatValue]*M_PI)/180.0;
if (deg && fjLayer) {
x = ((fjLayer->geo.w)-((fjLayer->geo.w)*cos(deg)-(fjLayer->geo.h)*sin(deg)))/2;
y = ((fjLayer->geo.h)-((fjLayer->geo.w)*sin(deg)+(fjLayer->geo.h)*cos(deg)))/2;
}
rototranslateTransform = [NSAffineTransform transform];
[rototranslateTransform translateXBy:x yBy:y];
[rotateTransform appendTransform:rototranslateTransform];
[rotateTransform concat];
[rototranslateTransform concat];
[rotateFilter setValue:rotateTransform forKey:@"inputTransform"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender label]];
break;
case 6: // traslate X
if (fjLayer)
fjLayer->geo.x = [sender floatValue];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender toolTip]];
break;
case 7: // traslate Y
if (fjLayer)
fjLayer->geo.y = [sender floatValue];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender toolTip]];
break;
default:
break;
}
#endif
[lock unlock];
[pool release];
}
- (IBAction)setImageParameter:(id)sender
{
Layer *fjLayer = NULL;
NSAutoreleasePool *pool;
float deg = 0;
float x = 0;
float y = 0;
NSAffineTransform *rotateTransform;
NSAffineTransform *rototranslateTransform;
NSString *paramName = NULL;
pool = [[NSAutoreleasePool alloc] init];
if (layer)
fjLayer = layer->fjLayer();
else
return;
// TODO - optimize the logic in this routine ... it's becoming huge!!
// to prevent its run() method to try rendering
// a frame while we change filter parameters
[lock lock];
switch([sender tag])
{
case 0: // opacity (AlphaFade)
[alphaFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"outputOpacity"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender toolTip]];
break;
case 1: //brightness (ColorCorrection)
[colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputBrightness"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender toolTip]];
break;
case 2: // saturation (ColorCorrection)
[colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputSaturation"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender toolTip]];
break;
case 3: // contrast (ColorCorrection)
[colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputContrast"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender toolTip]];
break;
case 4: // exposure (ExposureAdjust)
[exposureAdjustFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputEV"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender toolTip]];
break;
case 5: // rotate
rotateTransform = [NSAffineTransform transform];
[rotateTransform rotateByDegrees:[sender floatValue]];
deg = ([sender floatValue]*M_PI)/180.0;
if (deg && fjLayer) {
x = ((fjLayer->geo.w)-((fjLayer->geo.w)*cos(deg)-(fjLayer->geo.h)*sin(deg)))/2;
y = ((fjLayer->geo.h)-((fjLayer->geo.w)*sin(deg)+(fjLayer->geo.h)*cos(deg)))/2;
}
rototranslateTransform = [NSAffineTransform transform];
[rototranslateTransform translateXBy:x yBy:y];
[rotateTransform appendTransform:rototranslateTransform];
[rotateTransform concat];
[rototranslateTransform concat];
[rotateFilter setValue:rotateTransform forKey:@"inputTransform"];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender toolTip]];
break;
case 6: // traslate X
if (fjLayer)
fjLayer->geo.x = [sender floatValue];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender toolTip]];
break;
case 7: // traslate Y
if (fjLayer)
fjLayer->geo.y = [sender floatValue];
[imageParams setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:[sender toolTip]];
break;
default:
break;
}
[lock unlock];
[pool release];
}
- (void)setBlendMode:(NSString *)mode
{
if (layer)
layer->blendMode = mode;
}
- (void)setFilterCenterFromMouseLocation:(NSPoint)where
{
CIVector *centerVector = nil;
//[lock lock];
centerVector = [CIVector vectorWithX:where.x Y:where.y];
#if 0
[effectFilter setValue:centerVector forKey:@"inputCenter"];
#endif
//[lock unlock];
}
- (void)renderPreview:(CVPixelBufferRef)frame
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if ([self doPreview] && previewTarget) {
// scale the frame to fit the preview
if (![previewTarget isHiddenOrHasHiddenAncestor])
[previewTarget renderFrame:frame];
}
[pool release];
}
- (void)initFilters {
// Create CIFilters used for both preview and main frame
if (!colorCorrectionFilter) {
colorCorrectionFilter = [[CIFilter filterWithName:@"CIColorControls"] retain]; // Color filter
[colorCorrectionFilter setDefaults]; // set the filter to its default values
}
if (!exposureAdjustFilter) {
exposureAdjustFilter = [[CIFilter filterWithName:@"CIExposureAdjust"] retain];
[exposureAdjustFilter setDefaults];
// adjust exposure
[exposureAdjustFilter setValue:[NSNumber numberWithFloat:0.0] forKey:@"inputEV"];
}
// rotate
if (!rotateFilter) {
NSAffineTransform *rotateTransform = [NSAffineTransform transform];
rotateFilter = [[CIFilter filterWithName:@"CIAffineTransform"] retain];
[rotateTransform rotateByDegrees:0.0];
[rotateFilter setValue:rotateTransform forKey:@"inputTransform"];
}
if (!translateFilter) {
translateFilter = [[CIFilter filterWithName:@"CIAffineTransform"] retain];
NSAffineTransform *translateTransform = [NSAffineTransform transform];
[translateTransform translateXBy:0.0 yBy:0.0];
[translateFilter setValue:translateTransform forKey:@"inputTransform"];
}
if (!scaleFilter) {
scaleFilter = [[CIFilter filterWithName:@"CIAffineTransform"] retain];
//CIFilter *scaleFilter = [CIFilter filterWithName:@"CILanczosScaleTransform"];
[scaleFilter setDefaults]; // set the filter to its default values
}
//[scaleFilter setValue:[NSNumber numberWithFloat:scaleFactor] forKey:@"inputScale"];
if (!compositeFilter) {
compositeFilter = [[CIFilter filterWithName:@"CISourceOverCompositing"] retain]; // Composite filter
[compositeFilter setDefaults];
}
//[CIAlphaFade class];
if (!alphaFilter) {
alphaFilter = [[CIFilter filterWithName:@"CIAlphaFade"] retain]; // AlphaFade filter
[alphaFilter setDefaults]; // XXX - setDefaults doesn't work properly
#if MAC_OS_X_VERSION_10_6
[alphaFilter setValue:[NSNumber numberWithFloat:1.0] forKey:@"outputOpacity"]; // set default value
#else
[alphaFilter setValue:[NSNumber numberWithFloat:0.5] forKey:@"outputOpacity"]; // set default value
#endif
}
filtersInitialized = true;
}
- (CVPixelBufferRef)currentFrame
{
CVPixelBufferRef frame;
[lock lock];
frame = CVPixelBufferRetain(currentFrame);
[lock unlock];
return frame;
}
- (void)frameFiltered:(void *)buffer
{
// we have been notified that the frame has been filtered
// and 'buffer' holds the data for the filtered image
// (it will still have the some pixelformat and dimensions of original image)
// XXX - actually we don't store the filtered image, but we use it just for preview
if (layer && doPreview) {
CVPixelBufferRef pixelBufferFiltered;
CVReturn cvRet = CVPixelBufferCreateWithBytes (
NULL,
layer->width(),
layer->height(),
k32ARGBPixelFormat,
buffer,
layer->width()*4,
NULL,
NULL,
NULL,
&pixelBufferFiltered
);
// TODO - Error Messages
if (cvRet == noErr) {
[self renderPreview:pixelBufferFiltered];
CVPixelBufferRelease(pixelBufferFiltered);
}
}
}
- (void)startPreview
{
doPreview = YES;
}
- (void)start
{
if (!layer) {
/* TODO - avoid creating a CVLayer directly,
we should only know about CVCocoaLayer here */
CVLayer *cvLayer = new CVLayer(self);
if (freej) {
// TODO Geometry should expose a proper API
Context *ctx = [freej getContext];
/*
cvLayer->geo.w = ctx->screen->geo.w;
cvLayer->geo.h = ctx->screen->geo.h;
*/
cvLayer->init(ctx->screen->geo.w, ctx->screen->geo.h, 32);
} else {
cvLayer->init();
}
cvLayer->activate();
layer = (CVCocoaLayer *)cvLayer;
}
}
- (void)stop
{
if (layer) {
layer->deactivate();
delete layer;
layer = NULL;
}
}
- (CVPreview *)getPreviewTarget
{
return previewTarget;
}
- (void)setPreviewTarget:(CVPreview *)targetView
{
[lock lock];
previewTarget = targetView;
[lock unlock];
}
- (void)stopPreview
{
doPreview = NO;
}
- (void)lock
{
[lock lock];
}
- (void)unlock
{
[lock unlock];
}
- (bool)isVisible
{
if (layer)
return layer->isVisible();
return NO;
}
- (void)activate
{ if (layer) {
// ensure activating the underlying layer ...
// this won't do anything if the layer is already active
layer->activate();
if (freej) {
Layer *fjLayer = layer->fjLayer();
if (!fjLayer->screen) {
Context *ctx = [freej getContext];
ctx->screen->add_layer(layer->fjLayer());
}
}
}
}
- (NSString *)blendMode {
if (layer)
return layer->blendMode;
return NULL;
}
- (void)deactivate
{
if (layer)
layer->deactivate();
}
- (void)rotateBy:(float)deg
{
if (layer) {
}
}
- (void)translateXby:(float)x Yby:(float)y
{
if (layer)
layer->setOrigin(x, y);
}
- (void)toggleFilters
{
doFilters = doFilters?false:true;
}
- (void)toggleVisibility
{
if (layer)
if (layer->isActive())
layer->deactivate();
else
layer->activate();
}
- (void)togglePreview
{
doPreview = doPreview?NO:YES;
}
- (BOOL)doPreview
{
return doPreview;
}
- (char *)name {
//if (layer)
// return layer->fj_name();
if (layerView)
return (char *)[[layerView toolTip] UTF8String];
return (char*)"CVCocoaLayer";
}
- (Linklist<FilterInstance> *)activeFilters
{
NSMutableArray *result = nil;
if (layer) {
Layer *fjLayer = layer->fjLayer();
if (fjLayer) {
return &fjLayer->filters;
}
}
return NULL;
}
- (int)width
{
if (layer) {
return layer->width();
} else if (freej) {
Context *ctx = [freej getContext];
return ctx->screen->geo.w;
}
return 0;
}
- (int)height
{
if (layer) {
return layer->height();
} else if (freej) {
Context *ctx = [freej getContext];
return ctx->screen->geo.h;
}
return 0;
}
- (void)setRepeat:(BOOL)repeat
{
wantsRepeat = repeat;
}
- (BOOL)wantsRepeat
{
return wantsRepeat;
}
@synthesize freej;
@synthesize layer;
@synthesize layerView;
@synthesize wantsRepeat;
@synthesize doPreview;
@synthesize doFilters;
@end