123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- #import "GPUImageAverageColor.h"
- NSString *const kGPUImageColorAveragingVertexShaderString = SHADER_STRING
- (
- attribute vec4 position;
- attribute vec4 inputTextureCoordinate;
-
- uniform float texelWidth;
- uniform float texelHeight;
-
- varying vec2 upperLeftInputTextureCoordinate;
- varying vec2 upperRightInputTextureCoordinate;
- varying vec2 lowerLeftInputTextureCoordinate;
- varying vec2 lowerRightInputTextureCoordinate;
-
- void main()
- {
- gl_Position = position;
-
- upperLeftInputTextureCoordinate = inputTextureCoordinate.xy + vec2(-texelWidth, -texelHeight);
- upperRightInputTextureCoordinate = inputTextureCoordinate.xy + vec2(texelWidth, -texelHeight);
- lowerLeftInputTextureCoordinate = inputTextureCoordinate.xy + vec2(-texelWidth, texelHeight);
- lowerRightInputTextureCoordinate = inputTextureCoordinate.xy + vec2(texelWidth, texelHeight);
- }
- );
- #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
- NSString *const kGPUImageColorAveragingFragmentShaderString = SHADER_STRING
- (
- precision highp float;
-
- uniform sampler2D inputImageTexture;
-
- varying highp vec2 outputTextureCoordinate;
-
- varying highp vec2 upperLeftInputTextureCoordinate;
- varying highp vec2 upperRightInputTextureCoordinate;
- varying highp vec2 lowerLeftInputTextureCoordinate;
- varying highp vec2 lowerRightInputTextureCoordinate;
-
- void main()
- {
- highp vec4 upperLeftColor = texture2D(inputImageTexture, upperLeftInputTextureCoordinate);
- highp vec4 upperRightColor = texture2D(inputImageTexture, upperRightInputTextureCoordinate);
- highp vec4 lowerLeftColor = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate);
- highp vec4 lowerRightColor = texture2D(inputImageTexture, lowerRightInputTextureCoordinate);
-
- gl_FragColor = 0.25 * (upperLeftColor + upperRightColor + lowerLeftColor + lowerRightColor);
- }
- );
- #else
- NSString *const kGPUImageColorAveragingFragmentShaderString = SHADER_STRING
- (
- uniform sampler2D inputImageTexture;
-
- varying vec2 outputTextureCoordinate;
-
- varying vec2 upperLeftInputTextureCoordinate;
- varying vec2 upperRightInputTextureCoordinate;
- varying vec2 lowerLeftInputTextureCoordinate;
- varying vec2 lowerRightInputTextureCoordinate;
-
- void main()
- {
- vec4 upperLeftColor = texture2D(inputImageTexture, upperLeftInputTextureCoordinate);
- vec4 upperRightColor = texture2D(inputImageTexture, upperRightInputTextureCoordinate);
- vec4 lowerLeftColor = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate);
- vec4 lowerRightColor = texture2D(inputImageTexture, lowerRightInputTextureCoordinate);
-
- gl_FragColor = 0.25 * (upperLeftColor + upperRightColor + lowerLeftColor + lowerRightColor);
- }
- );
- #endif
- @implementation GPUImageAverageColor
- @synthesize colorAverageProcessingFinishedBlock = _colorAverageProcessingFinishedBlock;
- #pragma mark -
- #pragma mark Initialization and teardown
- - (id)init;
- {
- if (!(self = [super initWithVertexShaderFromString:kGPUImageColorAveragingVertexShaderString fragmentShaderFromString:kGPUImageColorAveragingFragmentShaderString]))
- {
- return nil;
- }
-
- texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
- texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
- finalStageSize = CGSizeMake(1.0, 1.0);
-
- __unsafe_unretained GPUImageAverageColor *weakSelf = self;
- [self setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime) {
- [weakSelf extractAverageColorAtFrameTime:frameTime];
- }];
- return self;
- }
- - (void)dealloc;
- {
- if (rawImagePixels != NULL)
- {
- free(rawImagePixels);
- }
- }
- #pragma mark -
- #pragma mark Managing the display FBOs
- - (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
- {
- if (self.preventRendering)
- {
- [firstInputFramebuffer unlock];
- return;
- }
-
- outputFramebuffer = nil;
- [GPUImageContext setActiveShaderProgram:filterProgram];
- glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
- glVertexAttribPointer(filterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates);
- GLuint currentTexture = [firstInputFramebuffer texture];
-
- NSUInteger numberOfReductionsInX = floor(log(inputTextureSize.width) / log(4.0));
- NSUInteger numberOfReductionsInY = floor(log(inputTextureSize.height) / log(4.0));
- NSUInteger reductionsToHitSideLimit = MIN(numberOfReductionsInX, numberOfReductionsInY);
- for (NSUInteger currentReduction = 0; currentReduction < reductionsToHitSideLimit; currentReduction++)
- {
- CGSize currentStageSize = CGSizeMake(floor(inputTextureSize.width / pow(4.0, currentReduction + 1.0)), floor(inputTextureSize.height / pow(4.0, currentReduction + 1.0)));
- if ( (currentStageSize.height < 2.0) || (currentStageSize.width < 2.0) )
- {
- // A really small last stage seems to cause significant errors in the average, so I abort and leave the rest to the CPU at this point
- break;
- // currentStageSize.height = 2.0; // TODO: Rotate the image to account for this case, which causes FBO construction to fail
- }
- [outputFramebuffer unlock];
- outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:currentStageSize textureOptions:self.outputTextureOptions onlyTexture:NO];
- [outputFramebuffer activateFramebuffer];
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, currentTexture);
-
- glUniform1i(filterInputTextureUniform, 2);
-
- glUniform1f(texelWidthUniform, 0.5 / currentStageSize.width);
- glUniform1f(texelHeightUniform, 0.5 / currentStageSize.height);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- currentTexture = [outputFramebuffer texture];
- finalStageSize = currentStageSize;
- }
- [firstInputFramebuffer unlock];
- }
- - (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
- {
- inputRotation = kGPUImageNoRotation;
- }
- - (void)extractAverageColorAtFrameTime:(CMTime)frameTime;
- {
- runSynchronouslyOnVideoProcessingQueue(^{
- // we need a normal color texture for averaging the color values
- NSAssert(self.outputTextureOptions.internalFormat == GL_RGBA, @"The output texture internal format for this filter must be GL_RGBA.");
- NSAssert(self.outputTextureOptions.type == GL_UNSIGNED_BYTE, @"The type of the output texture of this filter must be GL_UNSIGNED_BYTE.");
-
- NSUInteger totalNumberOfPixels = round(finalStageSize.width * finalStageSize.height);
-
- if (rawImagePixels == NULL)
- {
- rawImagePixels = (GLubyte *)malloc(totalNumberOfPixels * 4);
- }
-
- [GPUImageContext useImageProcessingContext];
- [outputFramebuffer activateFramebuffer];
- glReadPixels(0, 0, (int)finalStageSize.width, (int)finalStageSize.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels);
-
- NSUInteger redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0;
- NSUInteger byteIndex = 0;
- for (NSUInteger currentPixel = 0; currentPixel < totalNumberOfPixels; currentPixel++)
- {
- redTotal += rawImagePixels[byteIndex++];
- greenTotal += rawImagePixels[byteIndex++];
- blueTotal += rawImagePixels[byteIndex++];
- alphaTotal += rawImagePixels[byteIndex++];
- }
-
- CGFloat normalizedRedTotal = (CGFloat)redTotal / (CGFloat)totalNumberOfPixels / 255.0;
- CGFloat normalizedGreenTotal = (CGFloat)greenTotal / (CGFloat)totalNumberOfPixels / 255.0;
- CGFloat normalizedBlueTotal = (CGFloat)blueTotal / (CGFloat)totalNumberOfPixels / 255.0;
- CGFloat normalizedAlphaTotal = (CGFloat)alphaTotal / (CGFloat)totalNumberOfPixels / 255.0;
-
- if (_colorAverageProcessingFinishedBlock != NULL)
- {
- _colorAverageProcessingFinishedBlock(normalizedRedTotal, normalizedGreenTotal, normalizedBlueTotal, normalizedAlphaTotal, frameTime);
- }
- });
- }
- @end
|