GPUImageAverageColor.m 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #import "GPUImageAverageColor.h"
  2. NSString *const kGPUImageColorAveragingVertexShaderString = SHADER_STRING
  3. (
  4. attribute vec4 position;
  5. attribute vec4 inputTextureCoordinate;
  6. uniform float texelWidth;
  7. uniform float texelHeight;
  8. varying vec2 upperLeftInputTextureCoordinate;
  9. varying vec2 upperRightInputTextureCoordinate;
  10. varying vec2 lowerLeftInputTextureCoordinate;
  11. varying vec2 lowerRightInputTextureCoordinate;
  12. void main()
  13. {
  14. gl_Position = position;
  15. upperLeftInputTextureCoordinate = inputTextureCoordinate.xy + vec2(-texelWidth, -texelHeight);
  16. upperRightInputTextureCoordinate = inputTextureCoordinate.xy + vec2(texelWidth, -texelHeight);
  17. lowerLeftInputTextureCoordinate = inputTextureCoordinate.xy + vec2(-texelWidth, texelHeight);
  18. lowerRightInputTextureCoordinate = inputTextureCoordinate.xy + vec2(texelWidth, texelHeight);
  19. }
  20. );
  21. #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
  22. NSString *const kGPUImageColorAveragingFragmentShaderString = SHADER_STRING
  23. (
  24. precision highp float;
  25. uniform sampler2D inputImageTexture;
  26. varying highp vec2 outputTextureCoordinate;
  27. varying highp vec2 upperLeftInputTextureCoordinate;
  28. varying highp vec2 upperRightInputTextureCoordinate;
  29. varying highp vec2 lowerLeftInputTextureCoordinate;
  30. varying highp vec2 lowerRightInputTextureCoordinate;
  31. void main()
  32. {
  33. highp vec4 upperLeftColor = texture2D(inputImageTexture, upperLeftInputTextureCoordinate);
  34. highp vec4 upperRightColor = texture2D(inputImageTexture, upperRightInputTextureCoordinate);
  35. highp vec4 lowerLeftColor = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate);
  36. highp vec4 lowerRightColor = texture2D(inputImageTexture, lowerRightInputTextureCoordinate);
  37. gl_FragColor = 0.25 * (upperLeftColor + upperRightColor + lowerLeftColor + lowerRightColor);
  38. }
  39. );
  40. #else
  41. NSString *const kGPUImageColorAveragingFragmentShaderString = SHADER_STRING
  42. (
  43. uniform sampler2D inputImageTexture;
  44. varying vec2 outputTextureCoordinate;
  45. varying vec2 upperLeftInputTextureCoordinate;
  46. varying vec2 upperRightInputTextureCoordinate;
  47. varying vec2 lowerLeftInputTextureCoordinate;
  48. varying vec2 lowerRightInputTextureCoordinate;
  49. void main()
  50. {
  51. vec4 upperLeftColor = texture2D(inputImageTexture, upperLeftInputTextureCoordinate);
  52. vec4 upperRightColor = texture2D(inputImageTexture, upperRightInputTextureCoordinate);
  53. vec4 lowerLeftColor = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate);
  54. vec4 lowerRightColor = texture2D(inputImageTexture, lowerRightInputTextureCoordinate);
  55. gl_FragColor = 0.25 * (upperLeftColor + upperRightColor + lowerLeftColor + lowerRightColor);
  56. }
  57. );
  58. #endif
  59. @implementation GPUImageAverageColor
  60. @synthesize colorAverageProcessingFinishedBlock = _colorAverageProcessingFinishedBlock;
  61. #pragma mark -
  62. #pragma mark Initialization and teardown
  63. - (id)init;
  64. {
  65. if (!(self = [super initWithVertexShaderFromString:kGPUImageColorAveragingVertexShaderString fragmentShaderFromString:kGPUImageColorAveragingFragmentShaderString]))
  66. {
  67. return nil;
  68. }
  69. texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
  70. texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
  71. finalStageSize = CGSizeMake(1.0, 1.0);
  72. __unsafe_unretained GPUImageAverageColor *weakSelf = self;
  73. [self setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime) {
  74. [weakSelf extractAverageColorAtFrameTime:frameTime];
  75. }];
  76. return self;
  77. }
  78. - (void)dealloc;
  79. {
  80. if (rawImagePixels != NULL)
  81. {
  82. free(rawImagePixels);
  83. }
  84. }
  85. #pragma mark -
  86. #pragma mark Managing the display FBOs
  87. - (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
  88. {
  89. if (self.preventRendering)
  90. {
  91. [firstInputFramebuffer unlock];
  92. return;
  93. }
  94. outputFramebuffer = nil;
  95. [GPUImageContext setActiveShaderProgram:filterProgram];
  96. glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
  97. glVertexAttribPointer(filterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates);
  98. GLuint currentTexture = [firstInputFramebuffer texture];
  99. NSUInteger numberOfReductionsInX = floor(log(inputTextureSize.width) / log(4.0));
  100. NSUInteger numberOfReductionsInY = floor(log(inputTextureSize.height) / log(4.0));
  101. NSUInteger reductionsToHitSideLimit = MIN(numberOfReductionsInX, numberOfReductionsInY);
  102. for (NSUInteger currentReduction = 0; currentReduction < reductionsToHitSideLimit; currentReduction++)
  103. {
  104. CGSize currentStageSize = CGSizeMake(floor(inputTextureSize.width / pow(4.0, currentReduction + 1.0)), floor(inputTextureSize.height / pow(4.0, currentReduction + 1.0)));
  105. if ( (currentStageSize.height < 2.0) || (currentStageSize.width < 2.0) )
  106. {
  107. // 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
  108. break;
  109. // currentStageSize.height = 2.0; // TODO: Rotate the image to account for this case, which causes FBO construction to fail
  110. }
  111. [outputFramebuffer unlock];
  112. outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:currentStageSize textureOptions:self.outputTextureOptions onlyTexture:NO];
  113. [outputFramebuffer activateFramebuffer];
  114. glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  115. glClear(GL_COLOR_BUFFER_BIT);
  116. glActiveTexture(GL_TEXTURE2);
  117. glBindTexture(GL_TEXTURE_2D, currentTexture);
  118. glUniform1i(filterInputTextureUniform, 2);
  119. glUniform1f(texelWidthUniform, 0.5 / currentStageSize.width);
  120. glUniform1f(texelHeightUniform, 0.5 / currentStageSize.height);
  121. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  122. currentTexture = [outputFramebuffer texture];
  123. finalStageSize = currentStageSize;
  124. }
  125. [firstInputFramebuffer unlock];
  126. }
  127. - (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
  128. {
  129. inputRotation = kGPUImageNoRotation;
  130. }
  131. - (void)extractAverageColorAtFrameTime:(CMTime)frameTime;
  132. {
  133. runSynchronouslyOnVideoProcessingQueue(^{
  134. // we need a normal color texture for averaging the color values
  135. NSAssert(self.outputTextureOptions.internalFormat == GL_RGBA, @"The output texture internal format for this filter must be GL_RGBA.");
  136. NSAssert(self.outputTextureOptions.type == GL_UNSIGNED_BYTE, @"The type of the output texture of this filter must be GL_UNSIGNED_BYTE.");
  137. NSUInteger totalNumberOfPixels = round(finalStageSize.width * finalStageSize.height);
  138. if (rawImagePixels == NULL)
  139. {
  140. rawImagePixels = (GLubyte *)malloc(totalNumberOfPixels * 4);
  141. }
  142. [GPUImageContext useImageProcessingContext];
  143. [outputFramebuffer activateFramebuffer];
  144. glReadPixels(0, 0, (int)finalStageSize.width, (int)finalStageSize.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels);
  145. NSUInteger redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0;
  146. NSUInteger byteIndex = 0;
  147. for (NSUInteger currentPixel = 0; currentPixel < totalNumberOfPixels; currentPixel++)
  148. {
  149. redTotal += rawImagePixels[byteIndex++];
  150. greenTotal += rawImagePixels[byteIndex++];
  151. blueTotal += rawImagePixels[byteIndex++];
  152. alphaTotal += rawImagePixels[byteIndex++];
  153. }
  154. CGFloat normalizedRedTotal = (CGFloat)redTotal / (CGFloat)totalNumberOfPixels / 255.0;
  155. CGFloat normalizedGreenTotal = (CGFloat)greenTotal / (CGFloat)totalNumberOfPixels / 255.0;
  156. CGFloat normalizedBlueTotal = (CGFloat)blueTotal / (CGFloat)totalNumberOfPixels / 255.0;
  157. CGFloat normalizedAlphaTotal = (CGFloat)alphaTotal / (CGFloat)totalNumberOfPixels / 255.0;
  158. if (_colorAverageProcessingFinishedBlock != NULL)
  159. {
  160. _colorAverageProcessingFinishedBlock(normalizedRedTotal, normalizedGreenTotal, normalizedBlueTotal, normalizedAlphaTotal, frameTime);
  161. }
  162. });
  163. }
  164. @end