Pods/GPUImage/framework/Source/GPUImagePoissonBlendFilter.m
#import "GPUImagePoissonBlendFilter.h"

#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
NSString *const kGPUImagePoissonBlendFragmentShaderString = SHADER_STRING
(
 precision mediump float;
 
 varying vec2 textureCoordinate;
 varying vec2 leftTextureCoordinate;
 varying vec2 rightTextureCoordinate;
 varying vec2 topTextureCoordinate;
 varying vec2 bottomTextureCoordinate;
 
 varying vec2 textureCoordinate2;
 varying vec2 leftTextureCoordinate2;
 varying vec2 rightTextureCoordinate2;
 varying vec2 topTextureCoordinate2;
 varying vec2 bottomTextureCoordinate2;
 
 uniform sampler2D inputImageTexture;
 uniform sampler2D inputImageTexture2;
 
 uniform lowp float mixturePercent;

 void main()
 {
     vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
     vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
     vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
     vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
     vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;

     vec4 centerColor2 = texture2D(inputImageTexture2, textureCoordinate2);
     vec3 bottomColor2 = texture2D(inputImageTexture2, bottomTextureCoordinate2).rgb;
     vec3 leftColor2 = texture2D(inputImageTexture2, leftTextureCoordinate2).rgb;
     vec3 rightColor2 = texture2D(inputImageTexture2, rightTextureCoordinate2).rgb;
     vec3 topColor2 = texture2D(inputImageTexture2, topTextureCoordinate2).rgb;

     vec3 meanColor = (bottomColor + leftColor + rightColor + topColor) / 4.0;
     vec3 diffColor = centerColor.rgb - meanColor;

     vec3 meanColor2 = (bottomColor2 + leftColor2 + rightColor2 + topColor2) / 4.0;
     vec3 diffColor2 = centerColor2.rgb - meanColor2;
     
     vec3 gradColor = (meanColor + diffColor2);
     
	 gl_FragColor = vec4(mix(centerColor.rgb, gradColor, centerColor2.a * mixturePercent), centerColor.a);
 }
);
#else
NSString *const kGPUImagePoissonBlendFragmentShaderString = SHADER_STRING
(
 varying vec2 textureCoordinate;
 varying vec2 leftTextureCoordinate;
 varying vec2 rightTextureCoordinate;
 varying vec2 topTextureCoordinate;
 varying vec2 bottomTextureCoordinate;
 
 varying vec2 textureCoordinate2;
 varying vec2 leftTextureCoordinate2;
 varying vec2 rightTextureCoordinate2;
 varying vec2 topTextureCoordinate2;
 varying vec2 bottomTextureCoordinate2;
 
 uniform sampler2D inputImageTexture;
 uniform sampler2D inputImageTexture2;
 
 uniform float mixturePercent;
 
 void main()
 {
     vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
     vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
     vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
     vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
     vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
     
     vec4 centerColor2 = texture2D(inputImageTexture2, textureCoordinate2);
     vec3 bottomColor2 = texture2D(inputImageTexture2, bottomTextureCoordinate2).rgb;
     vec3 leftColor2 = texture2D(inputImageTexture2, leftTextureCoordinate2).rgb;
     vec3 rightColor2 = texture2D(inputImageTexture2, rightTextureCoordinate2).rgb;
     vec3 topColor2 = texture2D(inputImageTexture2, topTextureCoordinate2).rgb;
     
     vec3 meanColor = (bottomColor + leftColor + rightColor + topColor) / 4.0;
     vec3 diffColor = centerColor.rgb - meanColor;
     
     vec3 meanColor2 = (bottomColor2 + leftColor2 + rightColor2 + topColor2) / 4.0;
     vec3 diffColor2 = centerColor2.rgb - meanColor2;
     
     vec3 gradColor = (meanColor + diffColor2);
     
	 gl_FragColor = vec4(mix(centerColor.rgb, gradColor, centerColor2.a * mixturePercent), centerColor.a);
 }
);
#endif

@implementation GPUImagePoissonBlendFilter

@synthesize mix = _mix;
@synthesize numIterations = _numIterations;

- (id)init;
{
    if (!(self = [super initWithFragmentShaderFromString:kGPUImagePoissonBlendFragmentShaderString]))
    {
		return nil;
    }
    
    mixUniform = [filterProgram uniformIndex:@"mixturePercent"];
    self.mix = 0.5;
    
    self.numIterations = 10;
    
    return self;
}

- (void)setMix:(CGFloat)newValue;
{
    _mix = newValue;
    
    [self setFloat:_mix forUniform:mixUniform program:filterProgram];
}

//- (void)setOutputFBO;
//{
//    if (self.numIterations % 2 == 1) {
//        [self setSecondFilterFBO];
//    } else {
//        [self setFilterFBO];
//    }
//}

- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
{
    // Run the first stage of the two-pass filter
    [GPUImageContext setActiveShaderProgram:filterProgram];
    
    [super renderToTextureWithVertices:vertices textureCoordinates:textureCoordinates];
    
    for (int pass = 1; pass < self.numIterations; pass++) {
        
        if (pass % 2 == 0) {
            
            [GPUImageContext setActiveShaderProgram:filterProgram];
            
            // TODO: This will over-unlock the incoming framebuffer
            [super renderToTextureWithVertices:vertices textureCoordinates:[[self class] textureCoordinatesForRotation:kGPUImageNoRotation]];
        } else {
            // Run the second stage of the two-pass filter
            secondOutputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:[self sizeOfFBO] textureOptions:self.outputTextureOptions onlyTexture:NO];
            [secondOutputFramebuffer activateFramebuffer];
            
            [GPUImageContext setActiveShaderProgram:filterProgram];
            
            glClearColor(backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha);
            glClear(GL_COLOR_BUFFER_BIT);
            
            glActiveTexture(GL_TEXTURE2);
            glBindTexture(GL_TEXTURE_2D, [outputFramebuffer texture]);
            glUniform1i(filterInputTextureUniform, 2);
            
            glActiveTexture(GL_TEXTURE3);
            glBindTexture(GL_TEXTURE_2D, [secondInputFramebuffer texture]);
            glUniform1i(filterInputTextureUniform2, 3);
            
            glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
            glVertexAttribPointer(filterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, [[self class] textureCoordinatesForRotation:kGPUImageNoRotation]);
            glVertexAttribPointer(filterSecondTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, [[self class] textureCoordinatesForRotation:inputRotation2]);
            
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);            
        }
    }
}

@end