GPUImageOutput类将静态图像纹理上传到OpenGL ES中,然后使用这些纹理去处理进程链中的下一个对象。它的子类可以获得滤镜处理后的图片功能。[本文讲的很少,由于有许多地方不清楚,以后会更新]
方法
- (void)setInputFramebufferForTarget:(id<GPUImageInput>)target atIndex:(NSInteger)inputTextureIndex
说明:用于设置指定纹理的输入帧缓冲区的尺寸
参数:target是具体的对象,它接受了GPUImageInput的协议,例如:当我们使用[GPUImageLookupFilter]滤镜时,target就是一个GPUImageLookupFilter的实例。
- (void)addTarget:(id<GPUImageInput>)newTarget atTextureLocation:(NSInteger)textureLocation
说明:添加target
- (void)removeTarget:(id<GPUImageInput>)targetToRemove
说明:删除指定的target
- (void)removeAllTargets
说明:删除所有target
- (UIImage *)imageFromCurrentFramebufferWithOrientation:(UIImageOrientation)imageOrientation
说明:从帧缓冲区中取回对象,打印在屏幕中。
参数:imageOrientation时当前设备的手持方向
- (CGImageRef)newCGImageByFilteringCGImage:(CGImageRef)imageToFilter
说明:使用一张静态图片做滤镜纹理
完整代码
#import"GPUImageContext.h"#import"GPUImageFramebuffer.h"#ifTARGET_ipHONE_SIMULATOR || TARGET_OS_IPHONE#import<UIKit/UIKit.h>#else//For now, just redefine this on the Mactypedef NS_ENUM(NSInteger, UIImageOrientation) {
UIImageOrientationUp,//default orientationUIImageOrientationDown,//180 deg rotationUIImageOrientationLeft,//90 deg CCWUIImageOrientationRight,//90 deg CWUIImageOrientationUpMirrored,//as above but image mirrored along other axis. horizontal flipUIImageOrientationDownMirrored,//horizontal flipUIImageOrientationLeftMirrored,//vertical flipUIImageOrientationRightMirrored,//vertical flip};#endifvoidrunOnMainQueueWithoutDeadlocking(void(^block)(void));voidrunSynchronouslyOnVideoPRocessingQueue(void(^block)(void));voidrunAsynchronouslyOnVideoProcessingQueue(void(^block)(void));voidrunSynchronouslyOnContextQueue(GPUImageContext *context,void(^block)(void));voidrunAsynchronouslyOnContextQueue(GPUImageContext *context,void(^block)(void));voidreportAvailableMemoryForGPUImage(NSString *tag);
@class GPUImageMovieWriter;/** GPUImage's base source object
Images or frames of video are uploaded from source objects, which are subclasses of GPUImageOutput. These include:
- GPUImageVideoCamera (for live video from an iOS camera)
- GPUImageStillCamera (for taking photos with the camera)
- GPUImagePicture (for still images)
- GPUImageMovie (for movies)
Source objects upload still image frames to OpenGL ES as textures, then hand those textures off to the next objects in the processing chain.*/@interface GPUImageOutput : NSObject
{
GPUImageFramebuffer*outputFramebuffer;
NSMutableArray*targets, *targetTextureIndices;
CGSize inputTextureSize, cachedMaximumOutputSize, forcedMaximumSize;
BOOL overrideInputSize;
BOOL allTargetsWantMonoChromeData;
BOOL usingNextFrameForImageCapture;
}
@property(readwrite, nonatomic) BOOL shouldSmoothlyScaleOutput;
@property(readwrite, nonatomic) BOOL shouldIgnoreUpdatesToThisTarget;
@property(readwrite, nonatomic, retain) GPUImageMovieWriter*audioEncodingTarget;
@property(readwrite, nonatomic, unsafe_unretained) id<GPUImageInput>targetToIgnoreForUpdates;
@property(nonatomic, copy)void(^frameProcessingCompletionBlock)(GPUImageOutput*, CMTime);
@property(nonatomic) BOOL enabled;
@property(readwrite, nonatomic) GPUTextureOptions outputTextureOptions;///@name Managing targets- (void)setInputFramebufferForTarget:(id<GPUImageInput>)target atIndex:(NSInteger)inputTextureIndex;- (GPUImageFramebuffer *)framebufferForOutput;- (void)removeOutputFramebuffer;- (void)notifyTargetsAboutNewOutputTexture;/** Returns an array of the current targets.*/- (NSArray*)targets;/** Adds a target to receive notifications when new frames are available.
The target will be asked for its next available texture.
See [GPUImageInput newFrameReadyAtTime:]
@param newTarget Target to be added*/- (void)addTarget:(id<GPUImageInput>)newTarget;/** Adds a target to receive notifications when new frames are available.
See [GPUImageInput newFrameReadyAtTime:]
@param newTarget Target to be added*/- (void)addTarget:(id<GPUImageInput>)newTarget atTextureLocation:(NSInteger)textureLocation;/** Removes a target. The target will no longer receive notifications when new frames are available.
@param targetToRemove Target to be removed*/- (void)removeTarget:(id<GPUImageInput>)targetToRemove;/** Removes all targets.*/- (void)removeAllTargets;///@name Manage the output texture- (void)forceProcessingAtSize:(CGSize)frameSize;- (void)forceProcessingAtSizeRespectingaspectRatio:(CGSize)frameSize;///@name Still image processing- (void)useNextFrameForImageCapture;-(CGImageRef)newCGImageFromCurrentlyProcessedOutput;-(CGImageRef)newCGImageByFilteringCGImage:(CGImageRef)imageToFilter;//Platform-specific image output methods//If you're trying to use these methods, remember that you need to set -useNextFrameForImageCapture before running -processImage or running video and calling any of these methods, or you will get a nil image#ifTARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
- (UIImage *)imageFromCurrentFramebuffer;- (UIImage *)imageFromCurrentFramebufferWithOrientation:(UIImageOrientation)imageOrientation;- (UIImage *)imageByFilteringImage:(UIImage *)imageToFilter;- (CGImageRef)newCGImageByFilteringImage:(UIImage *)imageToFilter;#else- (NSImage *)imageFromCurrentFramebuffer;- (NSImage *)imageFromCurrentFramebufferWithOrientation:(UIImageOrientation)imageOrientation;- (NSImage *)imageByFilteringImage:(NSImage *)imageToFilter;- (CGImageRef)newCGImageByFilteringImage:(NSImage *)imageToFilter;#endif-(BOOL)providesMonochromeOutput;
@end
#import"GPUImageOutput.h"#import"GPUImageMovieWriter.h"#import"GPUImagePicture.h"#import<mach/mach.h>voidrunOnMainQueueWithoutDeadlocking(void(^block)(void))
{if([NSThread isMainThread])
{
block();
}else{
dispatch_sync(dispatch_get_main_queue(), block);
}
}voidrunSynchronouslyOnVideoProcessingQueue(void(^block)(void))
{
dispatch_queue_t videoProcessingQueue=[GPUImageContext sharedContextQueue];#if!OS_OBJECT_USE_OBJC#pragmaclang diagnostic push#pragmaclang diagnostic ignored "-Wdeprecated-declarations"if(dispatch_get_current_queue() ==videoProcessingQueue)#pragmaclang diagnostic pop#elseif(dispatch_get_specific([GPUImageContext contextKey]))#endif{
block();
}else{
dispatch_sync(videoProcessingQueue, block);
}
}voidrunAsynchronouslyOnVideoProcessingQueue(void(^block)(void))
{
dispatch_queue_t videoProcessingQueue=[GPUImageContext sharedContextQueue];#if!OS_OBJECT_USE_OBJC#pragmaclang diagnostic push#pragmaclang diagnostic ignored "-Wdeprecated-declarations"if(dispatch_get_current_queue() ==videoProcessingQueue)#pragmaclang diagnostic pop#elseif(dispatch_get_specific([GPUImageContext contextKey]))#endif{
block();
}else{
dispatch_async(videoProcessingQueue, block);
}
}voidrunSynchronouslyOnContextQueue(GPUImageContext *context,void(^block)(void))
{
dispatch_queue_t videoProcessingQueue=[context contextQueue];#if!OS_OBJECT_USE_OBJC#pragmaclang diagnostic push#pragmaclang diagnostic ignored "-Wdeprecated-declarations"if(dispatch_get_current_queue() ==videoProcessingQueue)#pragmaclang diagnostic pop#elseif(dispatch_get_specific([GPUImageContext contextKey]))#endif{
block();
}else{
dispatch_sync(videoProcessingQueue, block);
}
}voidrunAsynchronouslyOnContextQueue(GPUImageContext *context,void(^block)(void))
{
dispatch_queue_t videoProcessingQueue=[context contextQueue];#if!OS_OBJECT_USE_OBJC#pragmaclang diagnostic push#pragmaclang diagnostic ignored "-Wdeprecated-declarations"if(dispatch_get_current_queue() ==videoProcessingQueue)#pragmaclang diagnostic pop#elseif(dispatch_get_specific([GPUImageContext contextKey]))#endif{
block();
}else{
dispatch_async(videoProcessingQueue, block);
}
}voidreportAvailableMemoryForGPUImage(NSString *tag)
{if(!tag)
tag=@"Default";structtask_basic_info info;
mach_msg_type_number_t size=sizeof(info);
kern_return_t kerr=task_info(mach_task_self(),
TASK_BASIC_INFO,
(task_info_t)&info,&size);if( kerr ==KERN_SUCCESS ) {
NSLog(@"%@ - Memory used: %u", tag, (unsignedint)info.resident_size);//in bytes}else{
NSLog(@"%@ - Error: %s", tag, mach_error_string(kerr));
}
}
@implementation GPUImageOutput
@synthesize shouldSmoothlyScaleOutput=_shouldSmoothlyScaleOutput;
@synthesize shouldIgnoreUpdatesToThisTarget=_shouldIgnoreUpdatesToThisTarget;
@synthesize audioEncodingTarget=_audioEncodingTarget;
@synthesize targetToIgnoreForUpdates=_targetToIgnoreForUpdates;
@synthesize frameProcessingCompletionBlock=_frameProcessingCompletionBlock;
@synthesize enabled=_enabled;
@synthesize outputTextureOptions=_outputTextureOptions;#pragmamark -#pragmamark Initialization and teardown
-(id)init;
{if(!(self =[super init]))
{returnnil;
}
targets=[[NSMutableArray alloc] init];
targetTextureIndices=[[NSMutableArray alloc] init];
_enabled=YES;
allTargetsWantMonochromeData=YES;
usingNextFrameForImageCapture=NO;//set default texture options_outputTextureOptions.minFilter =GL_LINEAR;
_outputTextureOptions.magFilter=GL_LINEAR;
_outputTextureOptions.wrapS=GL_CLAMP_TO_EDGE;
_outputTextureOptions.wrapT=GL_CLAMP_TO_EDGE;
_outputTextureOptions.internalFormat=GL_RGBA;
_outputTextureOptions.format=GL_BGRA;
_outputTextureOptions.type=GL_UNSIGNED_BYTE;returnself;
}- (void)dealloc
{
[self removeAllTargets];
}#pragmamark -#pragmamark Managing targets
- (void)setInputFramebufferForTarget:(id<GPUImageInput>)target atIndex:(NSInteger)inputTextureIndex;
{
[target setInputFramebuffer:[self framebufferForOutput] atIndex:inputTextureIndex];
}- (GPUImageFramebuffer *)framebufferForOutput;
{returnoutputFramebuffer;
}- (void)removeOutputFramebuffer;
{
outputFramebuffer=nil;
}- (void)notifyTargetsAboutNewOutputTexture;
{for(id<GPUImageInput> currentTargetintargets)
{
NSInteger indexOfObject=[targets indexOfObject:currentTarget];
NSInteger textureIndex=[[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
[self setInputFramebufferForTarget:currentTarget atIndex:textureIndex];
}
}- (NSArray*)targets;
{return[NSArray arrayWithArray:targets];
}- (void)addTarget:(id<GPUImageInput>)newTarget;
{
NSInteger nextAvailableTextureIndex=[newTarget nextAvailableTextureIndex];
[self addTarget:newTarget atTextureLocation:nextAvailableTextureIndex];if([newTarget shouldIgnoreUpdatesToThisTarget])
{
_targetToIgnoreForUpdates=newTarget;
}
}- (void)addTarget:(id<GPUImageInput>)newTarget atTextureLocation:(NSInteger)textureLocation;
{if([targets containsObject:newTarget])
{return;
}
cachedMaximumOutputSize=CGSizeZero;
runSynchronouslyOnVideoProcessingQueue(^{
[self setInputFramebufferForTarget:newTarget atIndex:textureLocation];
[targets addObject:newTarget];
[targetTextureIndices addObject:[NSNumber numberWithInteger:textureLocation]];
allTargetsWantMonochromeData= allTargetsWantMonochromeData &&[newTarget wantsMonochromeInput];
});
}- (void)removeTarget:(id<GPUImageInput>)targetToRemove;
{if(![targets containsObject:targetToRemove])
{return;
}if(_targetToIgnoreForUpdates ==targetToRemove)
{
_targetToIgnoreForUpdates=nil;
}
cachedMaximumOutputSize=CGSizeZero;
NSInteger indexOfObject=[targets indexOfObject:targetToRemove];
NSInteger textureIndexOfTarget=[[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
runSynchronouslyOnVideoProcessingQueue(^{
[targetToRemove setInputSize:CGSizeZero atIndex:textureIndexOfTarget];
[targetToRemove setInputRotation:kGPUImageNoRotation atIndex:textureIndexOfTarget];
[targetTextureIndices removeObjectAtIndex:indexOfObject];
[targets removeObject:targetToRemove];
[targetToRemove endProcessing];
});
}- (void)removeAllTargets;
{
cachedMaximumOutputSize=CGSizeZero;
runSynchronouslyOnVideoProcessingQueue(^{for(id<GPUImageInput> targetToRemoveintargets)
{
NSInteger indexOfObject=[targets indexOfObject:targetToRemove];
NSInteger textureIndexOfTarget=[[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
[targetToRemove setInputSize:CGSizeZero atIndex:textureIndexOfTarget];
[targetToRemove setInputRotation:kGPUImageNoRotation atIndex:textureIndexOfTarget];
}
[targets removeAllObjects];
[targetTextureIndices removeAllObjects];
allTargetsWantMonochromeData=YES;
});
}#pragmamark -#pragmamark Manage the output texture
- (void)forceProcessingAtSize:(CGSize)frameSize;
{
}- (void)forceProcessingAtSizeRespectingAspectRatio:(CGSize)frameSize;
{
}#pragmamark -#pragmamark Still image processing
- (void)useNextFrameForImageCapture;
{
}-(CGImageRef)newCGImageFromCurrentlyProcessedOutput;
{returnnil;
}-(CGImageRef)newCGImageByFilteringCGImage:(CGImageRef)imageToFilter;
{
GPUImagePicture*stillImageSource =[[GPUImagePicture alloc] initWithCGImage:imageToFilter];
[self useNextFrameForImageCapture];
[stillImageSource addTarget:(id<GPUImageInput>)self];
[stillImageSource processImage];
CGImageRef processedImage=[self newCGImageFromCurrentlyProcessedOutput];
[stillImageSource removeTarget:(id<GPUImageInput>)self];returnprocessedImage;
}-(BOOL)providesMonochromeOutput;
{returnNO;
}#pragmamark -#pragmamark Platform-specific image output methods#ifTARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
- (UIImage *)imageFromCurrentFramebuffer;
{
UIDeviceOrientation deviceOrientation=[[UIDevice currentDevice] orientation];
UIImageOrientation imageOrientation=UIImageOrientationLeft;switch(deviceOrientation)
{caseUIDeviceOrientationPortrait:
imageOrientation=UIImageOrientationUp;
break;caseUIDeviceOrientationPortraitUpsideDown:
imageOrientation=UIImageOrientationDown;
break;caseUIDeviceOrientationLandscapeLeft:
imageOrientation=UIImageOrientationLeft;
break;caseUIDeviceOrientationLandscapeRight:
imageOrientation=UIImageOrientationRight;
break;default:
imageOrientation=UIImageOrientationUp;
break;
}return[self imageFromCurrentFramebufferWithOrientation:imageOrientation];
}- (UIImage *)imageFromCurrentFramebufferWithOrientation:(UIImageOrientation)imageOrientation;
{
CGImageRef cgImageFromBytes=[self newCGImageFromCurrentlyProcessedOutput];
UIImage*finalImage = [UIImage imageWithCGImage:cgImageFromBytes scale:1.0orientation:imageOrientation];
CGImageRelease(cgImageFromBytes);returnfinalImage;
}- (UIImage *)imageByFilteringImage:(UIImage *)imageToFilter;
{
CGImageRef image=[self newCGImageByFilteringCGImage:[imageToFilter CGImage]];
UIImage*processedImage =[UIImage imageWithCGImage:image scale:[imageToFilter scale] orientation:[imageToFilter imageOrientation]];
CGImageRelease(image);returnprocessedImage;
}- (CGImageRef)newCGImageByFilteringImage:(UIImage *)imageToFilter
{return[self newCGImageByFilteringCGImage:[imageToFilter CGImage]];
}#else- (NSImage *)imageFromCurrentFramebuffer;
{return[self imageFromCurrentFramebufferWithOrientation:UIImageOrientationLeft];
}- (NSImage *)imageFromCurrentFramebufferWithOrientation:(UIImageOrientation)imageOrientation;
{
CGImageRef cgImageFromBytes=[self newCGImageFromCurrentlyProcessedOutput];
NSImage*finalImage =[[NSImage alloc] initWithCGImage:cgImageFromBytes size:NSZeroSize];
CGImageRelease(cgImageFromBytes);returnfinalImage;
}- (NSImage *)imageByFilteringImage:(NSImage *)imageToFilter;
{
CGImageRef image=[self newCGImageByFilteringCGImage:[imageToFilter CGImageForProposedRect:NULL context:[NSGraphicsContext currentContext] hints:nil]];
NSImage*processedImage =[[NSImage alloc] initWithCGImage:image size:NSZeroSize];
CGImageRelease(image);returnprocessedImage;
}- (CGImageRef)newCGImageByFilteringImage:(NSImage *)imageToFilter
{return[self newCGImageByFilteringCGImage:[imageToFilter CGImageForProposedRect:NULL context:[NSGraphicsContext currentContext] hints:nil]];
}#endif#pragmamark -#pragmamarkaccessors
- (void)setAudioEncodingTarget:(GPUImageMovieWriter *)newValue;
{
_audioEncodingTarget=newValue;if( !_audioEncodingTarget.hasAudioTrack )
{
_audioEncodingTarget.hasAudioTrack=YES;
}
}-(void)setOutputTextureOptions:(GPUTextureOptions)outputTextureOptions
{
_outputTextureOptions=outputTextureOptions;if( outputFramebuffer.texture )
{
glBindTexture(GL_TEXTURE_2D, outputFramebuffer.texture);//_outputTextureOptions.format//_outputTextureOptions.internalFormat//_outputTextureOptions.magFilter//_outputTextureOptions.minFilter//_outputTextureOptions.typeglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _outputTextureOptions.wrapS);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _outputTextureOptions.wrapT);
glBindTexture(GL_TEXTURE_2D,0);
}
}
@end