From 59e211c29b03d9f6fea68cd27ace141328edeffa Mon Sep 17 00:00:00 2001 From: xant Date: Fri, 27 Feb 2009 01:26:30 +0100 Subject: [PATCH] - implemented 'rotation' facility on CVideoFile layers --- xcode/CVideoFile.h | 8 +- xcode/CVideoFile.mm | 97 ++++++++++++++++++------ xcode/EffectsPanel.nib/info.nib | 2 +- xcode/EffectsPanel.nib/keyedobjects.nib | Bin 10668 -> 12407 bytes 4 files changed, 80 insertions(+), 27 deletions(-) diff --git a/xcode/CVideoFile.h b/xcode/CVideoFile.h index c8086c5b..70507cbe 100644 --- a/xcode/CVideoFile.h +++ b/xcode/CVideoFile.h @@ -39,12 +39,18 @@ class CFreej; // filters for CI rendering CIFilter *colorCorrectionFilter; // hue saturation brightness control through one CI filter - CIFilter *effectFilter; // zoom blur filter CIFilter *compositeFilter; // composites the timecode over the video CIFilter *alphaFilter; + CIFilter *exposureAdjustFilter; + CIFilter *rotateFilter; + CIFilter *translateFilter; + CIFilter *effectFilter; + NSArray *paramNames; + //CIFilter *resizeFilter; CIContext *ciContext; CIContext *cifjContext; + bool needsReshape; //CVImageBufferRef freejFrame; CVLayer *layer; diff --git a/xcode/CVideoFile.mm b/xcode/CVideoFile.mm index f7b95dbe..3c83c7c3 100644 --- a/xcode/CVideoFile.mm +++ b/xcode/CVideoFile.mm @@ -8,8 +8,8 @@ #import "CIAlphaFade.h" #import "CVideoFile.h" - #import +#include #define _ARGB2BGRA(__buf, __size) \ {\ @@ -63,7 +63,8 @@ static CVReturn renderCallback(CVDisplayLinkRef displayLink, - (void)prepareOpenGL { CVReturn ret; - + NSAutoreleasePool *pool; + pool = [[NSAutoreleasePool alloc] init]; lock = [[NSRecursiveLock alloc] init]; // Create CGColorSpaceRef @@ -80,6 +81,19 @@ static CVReturn renderCallback(CVDisplayLinkRef displayLink, // Create CIFilters used for both preview and main frame colorCorrectionFilter = [[CIFilter filterWithName:@"CIColorControls"] retain]; // Color filter [colorCorrectionFilter setDefaults]; // set the filter to its default values + exposureAdjustFilter = [[CIFilter filterWithName:@"CIExposureAdjust"] retain]; + [exposureAdjustFilter setDefaults]; + // adjust exposure + [exposureAdjustFilter setValue:[NSNumber numberWithFloat:0.0] forKey:@"inputEV"]; + + // rotate + NSAffineTransform *rotateTransform = [NSAffineTransform transform]; + [rotateTransform rotateByDegrees:0.0]; + rotateFilter = [[CIFilter filterWithName:@"CIAffineTransform"] retain]; + [rotateFilter setValue:rotateTransform forKey:@"inputTransform"]; + //translateFilter = [[CIFilter filterWithName:@"CIAffineTransform"] retain]; + //[translateFilter setValue:translateTransform forKey:@"inputTransform"]; + effectFilter = [[CIFilter filterWithName:@"CIZoomBlur"] retain]; // Effect filter [effectFilter setDefaults]; // set the filter to its default values [effectFilter setValue:[NSNumber numberWithFloat:0.0] forKey:@"inputAmount"]; // don't apply effects at startup @@ -115,7 +129,7 @@ static CVReturn renderCallback(CVDisplayLinkRef displayLink, */ //timeCodeOverlay = [[TimeCodeOverlay alloc] initWithAttributes:fontAttributes targetSize:NSMakeSize(720.0,480.0 / 4.0)]; // text overlay will go in the bottom quarter of the display - + [pool release]; } - (void)awakeFromNib @@ -145,6 +159,9 @@ static CVReturn renderCallback(CVDisplayLinkRef displayLink, [effectFilter release]; [compositeFilter release]; [alphaFilter release]; + [exposureAdjustFilter release]; + [rotateFilter release]; + //[translateFilter release]; ///[timeCodeOverlay release]; CVOpenGLTextureRelease(currentFrame); CVOpenGLTextureRelease(previewFrame); @@ -394,32 +411,55 @@ static CVReturn renderCallback(CVDisplayLinkRef displayLink, - (IBAction)setFilterParameter:(id)sender { + NSAutoreleasePool *pool; + float deg = 0; + float x = 0; + float y = 0; + NSAffineTransform *rotateTransform; + NSAffineTransform *translateTransform; + pool = [[NSAutoreleasePool alloc] init]; [lock lock]; switch([sender tag]) { - case 0: - [colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputContrast"]; - break; - - case 1: - [colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputBrightness"]; - break; - - case 2: - [colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputSaturation"]; - break; - - case 3: - [effectFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputAmount"]; - break; - case 4: + case 0: // opacity (AlphaFade) [alphaFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"outputOpacity"]; break; + case 1: //brightness (ColorCorrection) + [colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputBrightness"]; + break; + case 2: // saturation (ColorCorrection) + [colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputSaturation"]; + break; + case 3: // contrast (ColorCorrection) + [colorCorrectionFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputContrast"]; + break; + case 4: // exposure (ExposureAdjust) + [exposureAdjustFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputEV"]; + break; + case 5: // rotate and translate + rotateTransform = [NSAffineTransform transform]; + [rotateTransform rotateByDegrees:[sender floatValue]]; + deg = ([sender floatValue]*M_PI)/180.0; + if (deg) { + x = ((layer->geo.w)-((layer->geo.w)*cos(deg)-(layer->geo.h)*sin(deg)))/2; + y = ((layer->geo.h)-((layer->geo.w)*sin(deg)+(layer->geo.h)*cos(deg)))/2; + } + translateTransform = [NSAffineTransform transform]; + [translateTransform translateXBy:x yBy:y]; + [rotateTransform appendTransform:translateTransform]; + [rotateTransform concat]; + [translateTransform concat]; + [rotateFilter setValue:rotateTransform forKey:@"inputTransform"]; + + break; + case 7: + [effectFilter setValue:[NSNumber numberWithFloat:[sender floatValue]] forKey:@"inputAmount"]; + break; default: break; - } [lock unlock]; + [pool release]; // if(!CVDisplayLinkIsRunning(displayLink)) // [self display]; } @@ -459,8 +499,11 @@ static CVReturn renderCallback(CVDisplayLinkRef displayLink, if (doFilters) { // preview [colorCorrectionFilter setValue:previewInputImage forKey:@"inputImage"]; - [effectFilter setValue:[colorCorrectionFilter valueForKey:@"outputImage"] forKey:@"inputImage"]; - previewImage = [effectFilter valueForKey:@"outputImage"]; + [exposureAdjustFilter setValue:[colorCorrectionFilter valueForKey:@"outputImage"] forKey:@"inputImage"]; + [effectFilter setValue:[exposureAdjustFilter valueForKey:@"outputImage"] forKey:@"inputImage"]; + [rotateFilter setValue:[effectFilter valueForKey:@"outputImage"] forKey:@"inputImage"]; + //[translateFilter setValue:[rotateFilter valueForKey:@"outputImage"] forKey:@"inputImage"]; + previewImage = [rotateFilter valueForKey:@"outputImage"]; } // and do preview @@ -624,7 +667,8 @@ static CVReturn renderCallback(CVDisplayLinkRef displayLink, [movie gotoBeginning]; [self togglePlay:nil]; - layer->activate(); + if (layer) + layer->activate(); } - (IBAction)toggleFilters:(id)sender @@ -652,11 +696,14 @@ static CVReturn renderCallback(CVDisplayLinkRef displayLink, if (inputImage) { if (doFilters) { [colorCorrectionFilter setValue:inputImage forKey:@"inputImage"]; - [effectFilter setValue:[colorCorrectionFilter valueForKey:@"outputImage"] + [exposureAdjustFilter setValue:[colorCorrectionFilter valueForKey:@"outputImage"] forKey:@"inputImage"]; + [effectFilter setValue:[exposureAdjustFilter valueForKey:@"outputImage"] forKey:@"inputImage"]; [alphaFilter setValue:[effectFilter valueForKey:@"outputImage"] forKey:@"inputImage"]; - renderedImage = [alphaFilter valueForKey:@"outputImage"]; + [rotateFilter setValue:[alphaFilter valueForKey:@"outputImage"] forKey:@"inputImage"]; + //[translateFilter setValue:[rotateFilter valueForKey:@"outputImage"] forKey:@"inputImage"]; + renderedImage = [rotateFilter valueForKey:@"outputImage"]; //CVPixelBufferLockBaseAddress(freejFrame, 0); renderedImage = [renderedImage retain]; diff --git a/xcode/EffectsPanel.nib/info.nib b/xcode/EffectsPanel.nib/info.nib index 40eb27b1..a78a5486 100644 --- a/xcode/EffectsPanel.nib/info.nib +++ b/xcode/EffectsPanel.nib/info.nib @@ -10,7 +10,7 @@ 5 IBOpenObjects - 8 + 133 IBSystem Version 9G55 diff --git a/xcode/EffectsPanel.nib/keyedobjects.nib b/xcode/EffectsPanel.nib/keyedobjects.nib index b04418a5b57abdbef47ac2b3664436634e9c4e48..256de7bed921f100b15ca57d81ca25d3f5921337 100644 GIT binary patch literal 12407 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 literal 10668 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 DW8!(7