#import "GPUImageView.h"
#import <OpenGLES/EAGLDrawable.h>
#import <QuartzCore/QuartzCore.h>
#import "GPUImageContext.h"
#import "GPUImageFilter.h"
#import <AVFoundation/AVFoundation.h>
#pragma mark -
#pragma mark Private methods and instance variables
@interface GPUImageView ()
{
GPUImageFramebuffer *inputFramebufferForDisplay;//输入帧缓存
GLuint displayRenderbuffer, displayFramebuffer;//展示的渲染缓存、待展示的帧缓存
GLProgram *displayProgram;//源程序
GLint displayPositionAttribute,//顶点属性
displayTextureCoordinateAttribute;//展示纹理属性
GLint displayInputTextureUniform;//展示输入纹理常量
CGSize inputImageSize;//输入像素尺寸
GLfloat imageVertices[8];//图像顶点数组
GLfloat backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha;//r g b a
CGSize boundsSizeAtFrameBufferEpoch;
}
@property (assign, nonatomic) NSUInteger aspectRatio;//比例
- (void)commonInit;//init
- (void)createDisplayFramebuffer;//创建帧缓存
- (void)destroyDisplayFramebuffer;//销毁帧缓存
- (void)recalculateViewGeometry;
@end
@implementation GPUImageView
@synthesize aspectRatio;
@synthesize sizeInPixels = _sizeInPixels;
@synthesize fillMode = _fillMode;
@synthesize enabled;
#pragma mark -
#pragma mark Initialization and teardown
+ (Class)layerClass //类型
{
return [CAEAGLLayer class];
}
- (id)initWithFrame:(CGRect)frame
{
if (!(self = [super initWithFrame:frame]))
{
return nil;
}
[self commonInit];
return self;
}
-(id)initWithCoder:(NSCoder *)coder
{
if (!(self = [super initWithCoder:coder]))
{
return nil;
}
[self commonInit];
return self;
}
- (void)commonInit;
{
// Set scaling to account for Retina display
if ([self respondsToSelector:@selector(setContentScaleFactor:)])
{
self.contentScaleFactor = [[UIScreen mainScreen] scale];
}
inputRotation = kGPUImageNoRotation;
self.opaque = YES;
self.hidden = NO;
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
self.enabled = YES;//基本配置
runSynchronouslyOnVideoProcessingQueue(^{
[GPUImageContext useImageProcessingContext];//获得当前上下文
displayProgram = [[GPUImageContext sharedImageProcessingContext] programForVertexShaderString: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"]; // This does assume a name of "inputTexture" for the fragment shader
[GPUImageContext setActiveShaderProgram:displayProgram];
glEnableVertexAttribArray(displayPositionAttribute);
glEnableVertexAttribArray(displayTextureCoordinateAttribute);
[self setBackgroundColorRed:0.0 green:0.0 blue:0.0 alpha:1.0];
_fillMode = kGPUImageFillModePreserveAspectRatio;
[self createDisplayFramebuffer];
});
}
- (void)layoutSubviews {
[super layoutSubviews];
if (!CGSizeEqualToSize(self.bounds.size, boundsSizeAtFrameBufferEpoch) &&
!CGSizeEqualToSize(self.bounds.size, CGSizeZero)) {
runSynchronouslyOnVideoProcessingQueue(^{
[self destroyDisplayFramebuffer];
[self createDisplayFramebuffer];
[self recalculateViewGeometry];
});
}
}
- (void)dealloc
{
runSynchronouslyOnVideoProcessingQueue(^{
[self destroyDisplayFramebuffer];
});
}
- (void)createDisplayFramebuffer;//创建帧缓存
{
[GPUImageContext useImageProcessingContext];
glGenFramebuffers(1, &displayFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, displayFramebuffer);
glGenRenderbuffers(1, &displayRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, displayRenderbuffer);
[[[GPUImageContext sharedImageProcessingContext] context] renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
GLint backingWidth, backingHeight;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
if ( (backingWidth == 0) || (backingHeight == 0) )
{
[self destroyDisplayFramebuffer];
return;
}
_sizeInPixels.width = (CGFloat)backingWidth;
_sizeInPixels.height = (CGFloat)backingHeight;
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, displayRenderbuffer);
GLuint framebufferCreationStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
NSAssert(framebufferCreationStatus == GL_FRAMEBUFFER_COMPLETE, @"Failure with display framebuffer generation for display of size: %f, %f", self.bounds.size.width, self.bounds.size.height);
boundsSizeAtFrameBufferEpoch = self.bounds.size;
}
- (void)destroyDisplayFramebuffer;//销毁帧缓存
{
[GPUImageContext useImageProcessingContext];
if (displayFramebuffer)
{
glDeleteFramebuffers(1, &displayFramebuffer);
displayFramebuffer = 0;
}
if (displayRenderbuffer)
{
glDeleteRenderbuffers(1, &displayRenderbuffer);
displayRenderbuffer = 0;
}
}
- (void)setDisplayFramebuffer;
{
if (!displayFramebuffer)
{
[self createDisplayFramebuffer];
}
glBindFramebuffer(GL_FRAMEBUFFER, displayFramebuffer);
glViewport(0, 0, (GLint)_sizeInPixels.width, (GLint)_sizeInPixels.height);
}
- (void)presentFramebuffer;
{
glBindRenderbuffer(GL_RENDERBUFFER, displayRenderbuffer);
[[GPUImageContext sharedImageProcessingContext] presentBufferForDisplay];
}
#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(_fillMode)
{
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)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
{
runSynchronouslyOnVideoProcessingQueue(^{
[GPUImageContext setActiveShaderProgram:displayProgram];
[self setDisplayFramebuffer];
glClearColor(backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_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, [GPUImageView textureCoordinatesForRotation:inputRotation]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
[self presentFramebuffer];
[inputFramebufferForDisplay unlock];
inputFramebufferForDisplay = nil;
});
}
- (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;
return CGSizeMake(self.contentScaleFactor * pointSize.width, self.contentScaleFactor * pointSize.height);
}
else
{
return self.bounds.size;
}
}
- (void)endProcessing
{
}
- (BOOL)shouldIgnoreUpdatesToThisTarget;
{
return NO;
}
- (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;
{
_fillMode = newValue;
[self recalculateViewGeometry];
}
@end