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" #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE #import <UIKit/UIKit.h> #else // For now, just redefine this on the Mac typedef NS_ENUM(NSInteger, UIImageOrientation) { UIImageOrientationUp, // default orientation UIImageOrientationDown, // 180 deg rotation UIImageOrientationLeft, // 90 deg CCW UIImageOrientationRight, // 90 deg CW UIImageOrientationUpMirrored, // as above but image mirrored along other axis. horizontal flip UIImageOrientationDownMirrored, // horizontal flip UIImageOrientationLeftMirrored, // vertical flip UIImageOrientationRightMirrored, // vertical flip }; #endif void runOnMainQueueWithoutDeadlocking(void (^block)(void)); void runSynchronouslyOnVideoProcessingQueue(void (^block)(void)); void runAsynchronouslyOnVideoProcessingQueue(void (^block)(void)); void runSynchronouslyOnContextQueue(GPUImageContext *context, void (^block)(void)); void runAsynchronouslyOnContextQueue(GPUImageContext *context, void (^block)(void)); void reportAvailableMemoryForGPUImage(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 #if TARGET_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> void runOnMainQueueWithoutDeadlocking(void (^block)(void)) { if ([NSThread isMainThread]) { block(); } else { dispatch_sync(dispatch_get_main_queue(), block); } } void runSynchronouslyOnVideoProcessingQueue(void (^block)(void)) { dispatch_queue_t videoProcessingQueue = [GPUImageContext sharedContextQueue]; #if !OS_OBJECT_USE_OBJC #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if (dispatch_get_current_queue() == videoProcessingQueue) #pragma clang diagnostic pop #else if (dispatch_get_specific([GPUImageContext contextKey])) #endif { block(); }else { dispatch_sync(videoProcessingQueue, block); } } void runAsynchronouslyOnVideoProcessingQueue(void (^block)(void)) { dispatch_queue_t videoProcessingQueue = [GPUImageContext sharedContextQueue]; #if !OS_OBJECT_USE_OBJC #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if (dispatch_get_current_queue() == videoProcessingQueue) #pragma clang diagnostic pop #else if (dispatch_get_specific([GPUImageContext contextKey])) #endif { block(); }else { dispatch_async(videoProcessingQueue, block); } } void runSynchronouslyOnContextQueue(GPUImageContext *context, void (^block)(void)) { dispatch_queue_t videoProcessingQueue = [context contextQueue]; #if !OS_OBJECT_USE_OBJC #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if (dispatch_get_current_queue() == videoProcessingQueue) #pragma clang diagnostic pop #else if (dispatch_get_specific([GPUImageContext contextKey])) #endif { block(); }else { dispatch_sync(videoProcessingQueue, block); } } void runAsynchronouslyOnContextQueue(GPUImageContext *context, void (^block)(void)) { dispatch_queue_t videoProcessingQueue = [context contextQueue]; #if !OS_OBJECT_USE_OBJC #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if (dispatch_get_current_queue() == videoProcessingQueue) #pragma clang diagnostic pop #else if (dispatch_get_specific([GPUImageContext contextKey])) #endif { block(); }else { dispatch_async(videoProcessingQueue, block); } } void reportAvailableMemoryForGPUImage(NSString *tag) { if (!tag) tag = @"Default"; struct task_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, (unsigned int)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; #pragma mark - #pragma mark Initialization and teardown - (id)init; { if (!(self = [super init])) { return nil; } 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; return self; } - (void)dealloc { [self removeAllTargets]; } #pragma mark - #pragma mark Managing targets - (void)setInputFramebufferForTarget:(id<GPUImageInput>)target atIndex:(NSInteger)inputTextureIndex; { [target setInputFramebuffer:[self framebufferForOutput] atIndex:inputTextureIndex]; } - (GPUImageFramebuffer *)framebufferForOutput; { return outputFramebuffer; } - (void)removeOutputFramebuffer; { outputFramebuffer = nil; } - (void)notifyTargetsAboutNewOutputTexture; { for (id<GPUImageInput> currentTarget in targets) { 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> targetToRemove in targets) { 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; }); } #pragma mark - #pragma mark Manage the output texture - (void)forceProcessingAtSize:(CGSize)frameSize; { } - (void)forceProcessingAtSizeRespectingAspectRatio:(CGSize)frameSize; { } #pragma mark - #pragma mark Still image processing - (void)useNextFrameForImageCapture; { } - (CGImageRef)newCGImageFromCurrentlyProcessedOutput; { return nil; } - (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]; return processedImage; } - (BOOL)providesMonochromeOutput; { return NO; } #pragma mark - #pragma mark Platform-specific image output methods #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE - (UIImage *)imageFromCurrentFramebuffer; { UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation]; UIImageOrientation imageOrientation = UIImageOrientationLeft; switch (deviceOrientation) { case UIDeviceOrientationPortrait: imageOrientation = UIImageOrientationUp; break; case UIDeviceOrientationPortraitUpsideDown: imageOrientation = UIImageOrientationDown; break; case UIDeviceOrientationLandscapeLeft: imageOrientation = UIImageOrientationLeft; break; case UIDeviceOrientationLandscapeRight: 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.0 orientation:imageOrientation]; CGImageRelease(cgImageFromBytes); return finalImage; } - (UIImage *)imageByFilteringImage:(UIImage *)imageToFilter; { CGImageRef image = [self newCGImageByFilteringCGImage:[imageToFilter CGImage]]; UIImage *processedImage = [UIImage imageWithCGImage:image scale:[imageToFilter scale] orientation:[imageToFilter imageOrientation]]; CGImageRelease(image); return processedImage; } - (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); return finalImage; } - (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); return processedImage; } - (CGImageRef)newCGImageByFilteringImage:(NSImage *)imageToFilter { return [self newCGImageByFilteringCGImage:[imageToFilter CGImageForProposedRect:NULL context:[NSGraphicsContext currentContext] hints:nil]]; } #endif #pragma mark - #pragma mark Accessors - (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.type glTexParameteri(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