-
-
Save depthlove/95f66e0204a1ab8504a7d0ddbcb9f328 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #import <AVFoundation/AVFoundation.h> | |
| #import "GPUImageLayer.h" | |
| #import "GPUImageOutput.h" | |
| #import "GPUImageFilter.h" | |
| @interface GPUImageLayer() { | |
| GPUImageFramebuffer *inputFramebufferForDisplay; | |
| GLProgram *displayProgram; | |
| GLint displayPositionAttribute, displayTextureCoordinateAttribute; | |
| GLint displayInputTextureUniform; | |
| CGSize inputImageSize; | |
| GLfloat imageVertices[8]; | |
| GLfloat backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha; | |
| } | |
| // Initialization and teardown | |
| - (void)commonInit; | |
| // Managing the display FBOs | |
| - (void)createDisplayFramebuffer; | |
| - (void)destroyDisplayFramebuffer; | |
| // Handling fill mode | |
| - (void)recalculateViewGeometry; | |
| @end | |
| @implementation GPUImageLayer | |
| @synthesize sizeInPixels = _sizeInPixels; | |
| @synthesize fillMode = _fillMode; | |
| @synthesize enabled; | |
| #pragma mark - | |
| #pragma mark Initialization and teardown | |
| - (instancetype)init { | |
| self = [super init]; | |
| if (self) { | |
| [self commonInit]; | |
| } | |
| return self; | |
| } | |
| - (void)commonInit; | |
| { | |
| // I believe each of these views needs a separate OpenGL context, unlike on iOS where you're rendering to an FBO in a layer | |
| // NSOpenGLPixelFormatAttribute pixelFormatAttributes[] = { | |
| // NSOpenGLPFADoubleBuffer, | |
| // NSOpenGLPFAAccelerated, 0, | |
| // 0 | |
| // }; | |
| // | |
| // NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttributes]; | |
| // if (pixelFormat == nil) | |
| // { | |
| // NSLog(@"Error: No appropriate pixel format found"); | |
| // } | |
| // // TODO: Take into account the sharegroup | |
| // NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:[[GPUImageContext sharedImageProcessingContext] context]]; | |
| // if (context == nil) | |
| // { | |
| // NSAssert(NO, @"Problem creating the GPUImageView context"); | |
| // } | |
| // [self setOpenGLContext:context]; | |
| // [self setOpenGLContext:[[GPUImageContext sharedImageProcessingContext] context]]; | |
| CGLSetCurrentContext([GPUImageContext sharedImageProcessingContext].context.CGLContextObj); | |
| self.asynchronous = NO; | |
| inputRotation = kGPUImageNoRotation; | |
| // self.hidden = NO; | |
| self.enabled = YES; | |
| runSynchronouslyOnVideoProcessingQueue(^{ | |
| CGLSetCurrentContext([GPUImageContext sharedImageProcessingContext].context.CGLContextObj); | |
| // [self.openGLContext makeCurrentContext]; | |
| displayProgram = [[GPUImageContext sharedImageProcessingContext] programForVertexShaderString:kGPUImageVertexShaderString fragmentShaderString:kGPUImagePassthroughFragmentShaderString]; | |
| // displayProgram = [[GLProgram alloc] initWithVertexShaderString:kGPUImageVertexShaderString fragmentShaderString:kGPUImagePassthroughFragmentShaderString]; | |
| if (!displayProgram.initialized) | |
| { | |
| [displayProgram addAttribute:@"position"]; | |
| [displayProgram addAttribute:@"inputTextureCoordinate"]; | |
| if (![displayProgram link]) | |
| { | |
| NSString *progLog = [displayProgram programLog]; | |
| NSLog(@"Program link log: %@", progLog); | |
| NSString *fragLog = [displayProgram fragmentShaderLog]; | |
| NSLog(@"Fragment shader compile log: %@", fragLog); | |
| NSString *vertLog = [displayProgram vertexShaderLog]; | |
| NSLog(@"Vertex shader compile log: %@", vertLog); | |
| displayProgram = nil; | |
| NSAssert(NO, @"Filter shader link failed"); | |
| } | |
| } | |
| displayPositionAttribute = [displayProgram attributeIndex:@"position"]; | |
| displayTextureCoordinateAttribute = [displayProgram attributeIndex:@"inputTextureCoordinate"]; | |
| displayInputTextureUniform = [displayProgram uniformIndex:@"inputImageTexture"]; | |
| [GPUImageContext setActiveShaderProgram:displayProgram]; | |
| // [displayProgram use]; | |
| glEnableVertexAttribArray(displayPositionAttribute); | |
| glEnableVertexAttribArray(displayTextureCoordinateAttribute); | |
| [self setBackgroundColorRed:0.0 green:0.0 blue:0.0 alpha:1.0]; | |
| _fillItMode = kGPUImageFillModePreserveAspectRatio; | |
| [self createDisplayFramebuffer]; | |
| }); | |
| } | |
| - (void)dealloc | |
| { | |
| } | |
| #pragma mark - | |
| #pragma mark Managing the display FBOs | |
| - (void)createDisplayFramebuffer; | |
| { | |
| // Perhaps I'll use an FBO at some time later, but for now will render directly to the screen | |
| _sizeInPixels.width = self.bounds.size.width; | |
| _sizeInPixels.height = self.bounds.size.height; | |
| // NSLog(@"Backing width: %d, height: %d", backingWidth, backingHeight); | |
| } | |
| - (void)destroyDisplayFramebuffer; | |
| { | |
| CGLSetCurrentContext([GPUImageContext sharedImageProcessingContext].context.CGLContextObj); | |
| // [self.openGLContext makeCurrentContext]; | |
| } | |
| - (void)setDisplayFramebuffer; | |
| { | |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
| glBindRenderbuffer(GL_RENDERBUFFER, 0); | |
| glViewport(0, 0, (GLint)_sizeInPixels.width, (GLint)_sizeInPixels.height); | |
| } | |
| - (void)presentFramebuffer; | |
| { | |
| CGLSetCurrentContext([GPUImageContext sharedImageProcessingContext].context.CGLContextObj); | |
| // [self.openGLContext flushBuffer]; | |
| CGLFlushDrawable([GPUImageContext sharedImageProcessingContext].context.CGLContextObj); | |
| } | |
| - (void)setFrame:(CGRect)frame { | |
| [super setFrame:frame]; | |
| if ( (_sizeInPixels.width == self.bounds.size.width) && (_sizeInPixels.height == self.bounds.size.height) ) | |
| { | |
| return; | |
| } | |
| _sizeInPixels.width = self.bounds.size.width; | |
| _sizeInPixels.height = self.bounds.size.height; | |
| [self recalculateViewGeometry]; | |
| /* | |
| dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
| [self newFrameReadyAtTime:kCMTimeInvalid atIndex:0]; | |
| }); | |
| */ | |
| [self setNeedsDisplay]; | |
| } | |
| #pragma mark - | |
| #pragma mark Handling fill mode | |
| - (void)recalculateViewGeometry; | |
| { | |
| // runSynchronouslyOnVideoProcessingQueue(^{ | |
| CGFloat heightScaling, widthScaling; | |
| CGSize currentViewSize = self.bounds.size; | |
| // CGFloat imageAspectRatio = inputImageSize.width / inputImageSize.height; | |
| // CGFloat viewAspectRatio = currentViewSize.width / currentViewSize.height; | |
| CGRect insetRect = AVMakeRectWithAspectRatioInsideRect(inputImageSize, self.bounds); | |
| switch(_fillItMode) | |
| { | |
| case kGPUImageFillModeStretch: | |
| { | |
| widthScaling = 1.0; | |
| heightScaling = 1.0; | |
| }; break; | |
| case kGPUImageFillModePreserveAspectRatio: | |
| { | |
| widthScaling = insetRect.size.width / currentViewSize.width; | |
| heightScaling = insetRect.size.height / currentViewSize.height; | |
| }; break; | |
| case kGPUImageFillModePreserveAspectRatioAndFill: | |
| { | |
| // CGFloat widthHolder = insetRect.size.width / currentViewSize.width; | |
| widthScaling = currentViewSize.height / insetRect.size.height; | |
| heightScaling = currentViewSize.width / insetRect.size.width; | |
| }; break; | |
| } | |
| imageVertices[0] = -widthScaling; | |
| imageVertices[1] = -heightScaling; | |
| imageVertices[2] = widthScaling; | |
| imageVertices[3] = -heightScaling; | |
| imageVertices[4] = -widthScaling; | |
| imageVertices[5] = heightScaling; | |
| imageVertices[6] = widthScaling; | |
| imageVertices[7] = heightScaling; | |
| // }); | |
| // static const GLfloat imageVertices[] = { | |
| // -1.0f, -1.0f, | |
| // 1.0f, -1.0f, | |
| // -1.0f, 1.0f, | |
| // 1.0f, 1.0f, | |
| // }; | |
| } | |
| - (void)setBackgroundColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent alpha:(GLfloat)alphaComponent; | |
| { | |
| backgroundColorRed = redComponent; | |
| backgroundColorGreen = greenComponent; | |
| backgroundColorBlue = blueComponent; | |
| backgroundColorAlpha = alphaComponent; | |
| } | |
| + (const GLfloat *)textureCoordinatesForRotation:(GPUImageRotationMode)rotationMode; | |
| { | |
| // static const GLfloat noRotationTextureCoordinates[] = { | |
| // 0.0f, 0.0f, | |
| // 1.0f, 0.0f, | |
| // 0.0f, 1.0f, | |
| // 1.0f, 1.0f, | |
| // }; | |
| static const GLfloat noRotationTextureCoordinates[] = { | |
| 0.0f, 1.0f, | |
| 1.0f, 1.0f, | |
| 0.0f, 0.0f, | |
| 1.0f, 0.0f, | |
| }; | |
| static const GLfloat rotateRightTextureCoordinates[] = { | |
| 1.0f, 1.0f, | |
| 1.0f, 0.0f, | |
| 0.0f, 1.0f, | |
| 0.0f, 0.0f, | |
| }; | |
| static const GLfloat rotateLeftTextureCoordinates[] = { | |
| 0.0f, 0.0f, | |
| 0.0f, 1.0f, | |
| 1.0f, 0.0f, | |
| 1.0f, 1.0f, | |
| }; | |
| static const GLfloat verticalFlipTextureCoordinates[] = { | |
| 0.0f, 0.0f, | |
| 1.0f, 0.0f, | |
| 0.0f, 1.0f, | |
| 1.0f, 1.0f, | |
| }; | |
| static const GLfloat horizontalFlipTextureCoordinates[] = { | |
| 1.0f, 1.0f, | |
| 0.0f, 1.0f, | |
| 1.0f, 0.0f, | |
| 0.0f, 0.0f, | |
| }; | |
| static const GLfloat rotateRightVerticalFlipTextureCoordinates[] = { | |
| 1.0f, 0.0f, | |
| 1.0f, 1.0f, | |
| 0.0f, 0.0f, | |
| 0.0f, 1.0f, | |
| }; | |
| static const GLfloat rotateRightHorizontalFlipTextureCoordinates[] = { | |
| 1.0f, 1.0f, | |
| 1.0f, 0.0f, | |
| 0.0f, 1.0f, | |
| 0.0f, 0.0f, | |
| }; | |
| static const GLfloat rotate180TextureCoordinates[] = { | |
| 1.0f, 0.0f, | |
| 0.0f, 0.0f, | |
| 1.0f, 1.0f, | |
| 0.0f, 1.0f, | |
| }; | |
| switch(rotationMode) | |
| { | |
| case kGPUImageNoRotation: return noRotationTextureCoordinates; | |
| case kGPUImageRotateLeft: return rotateLeftTextureCoordinates; | |
| case kGPUImageRotateRight: return rotateRightTextureCoordinates; | |
| case kGPUImageFlipVertical: return verticalFlipTextureCoordinates; | |
| case kGPUImageFlipHorizonal: return horizontalFlipTextureCoordinates; | |
| case kGPUImageRotateRightFlipVertical: return rotateRightVerticalFlipTextureCoordinates; | |
| case kGPUImageRotateRightFlipHorizontal: return rotateRightHorizontalFlipTextureCoordinates; | |
| case kGPUImageRotate180: return rotate180TextureCoordinates; | |
| } | |
| } | |
| #pragma mark - | |
| #pragma mark GPUInput protocol | |
| - (void)drawInCGLContext:(CGLContextObj)ctx pixelFormat:(CGLPixelFormatObj)pf forLayerTime:(CFTimeInterval)t displayTime:(const CVTimeStamp *)ts { | |
| CGLSetCurrentContext(ctx); | |
| NSLog(@"Draw in CGLContext"); | |
| runSynchronouslyOnVideoProcessingQueue(^{ | |
| // [[self openGLContext] makeCurrentContext]; | |
| [GPUImageContext setActiveShaderProgram:displayProgram]; | |
| [self setDisplayFramebuffer]; | |
| [displayProgram use]; | |
| // glMatrixMode(GL_MODELVIEW); | |
| // glLoadIdentity(); | |
| // | |
| // glMatrixMode(GL_PROJECTION); | |
| // glLoadIdentity(); | |
| glClearColor(backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| glActiveTexture(GL_TEXTURE4); | |
| glBindTexture(GL_TEXTURE_2D, [inputFramebufferForDisplay texture]); | |
| glUniform1i(displayInputTextureUniform, 4); | |
| glVertexAttribPointer(displayPositionAttribute, 2, GL_FLOAT, 0, 0, imageVertices); | |
| glVertexAttribPointer(displayTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, [GPUImageLayer textureCoordinatesForRotation:inputRotation]); | |
| BOOL canLockFocus = YES; | |
| /* | |
| if ([self respondsToSelector:@selector(lockFocusIfCanDraw)]) | |
| { | |
| canLockFocus = [self lockFocusIfCanDraw]; | |
| } | |
| else | |
| { | |
| [self lockFocus]; | |
| } | |
| *//* | |
| */ | |
| if (canLockFocus) | |
| { | |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
| [self presentFramebuffer]; | |
| glBindTexture(GL_TEXTURE_2D, 0); | |
| // [self unlockFocus]; | |
| } | |
| [inputFramebufferForDisplay unlock]; | |
| inputFramebufferForDisplay = nil; | |
| }); | |
| [super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts]; | |
| } | |
| - (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex; | |
| { | |
| NSLog(@"New frame ready at time %i"); | |
| [self setNeedsDisplay]; | |
| } | |
| - (NSInteger)nextAvailableTextureIndex; | |
| { | |
| return 0; | |
| } | |
| - (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex; | |
| { | |
| inputFramebufferForDisplay = newInputFramebuffer; | |
| [inputFramebufferForDisplay lock]; | |
| } | |
| - (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex; | |
| { | |
| inputRotation = newInputRotation; | |
| } | |
| - (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex; | |
| { | |
| runSynchronouslyOnVideoProcessingQueue(^{ | |
| CGSize rotatedSize = newSize; | |
| if (GPUImageRotationSwapsWidthAndHeight(inputRotation)) | |
| { | |
| rotatedSize.width = newSize.height; | |
| rotatedSize.height = newSize.width; | |
| } | |
| if (!CGSizeEqualToSize(inputImageSize, rotatedSize)) | |
| { | |
| inputImageSize = rotatedSize; | |
| [self recalculateViewGeometry]; | |
| } | |
| }); | |
| } | |
| - (CGSize)maximumOutputSize; | |
| { | |
| if ([self respondsToSelector:@selector(setContentScaleFactor:)]) | |
| { | |
| CGSize pointSize = self.bounds.size; | |
| // TODO: Account for Retina displays | |
| return pointSize; | |
| // return CGSizeMake(self.contentScaleFactor * pointSize.width, self.contentScaleFactor * pointSize.height); | |
| } | |
| else | |
| { | |
| return self.bounds.size; | |
| } | |
| } | |
| - (void)endProcessing | |
| { | |
| } | |
| - (BOOL)shouldIgnoreUpdatesToThisTarget; | |
| { | |
| return NO; | |
| } | |
| - (void)conserveMemoryForNextFrame; | |
| { | |
| } | |
| - (BOOL)wantsMonochromeInput; | |
| { | |
| return NO; | |
| } | |
| - (void)setCurrentlyReceivingMonochromeInput:(BOOL)newValue; | |
| { | |
| } | |
| #pragma mark - | |
| #pragma mark Accessors | |
| - (CGSize)sizeInPixels; | |
| { | |
| if (CGSizeEqualToSize(_sizeInPixels, CGSizeZero)) | |
| { | |
| return [self maximumOutputSize]; | |
| } | |
| else | |
| { | |
| return _sizeInPixels; | |
| } | |
| } | |
| - (void)setFillMode:(GPUImageFillModeType)newValue; | |
| { | |
| _fillItMode = newValue; | |
| [self recalculateViewGeometry]; | |
| } | |
| @end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment