Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b73bfee6ac | |||
| 09398d0cb3 | |||
| f69b586d70 | |||
| cfb84d31fb |
+1
-3
@@ -2,7 +2,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "LFLiveKit"
|
||||
s.version = "1.9.5"
|
||||
s.version = "2.0"
|
||||
s.summary = "LaiFeng ios Live. LFLiveKit."
|
||||
s.homepage = "https://github.com/chenliming777"
|
||||
s.license = { :type => "MIT", :file => "LICENSE" }
|
||||
@@ -18,6 +18,4 @@ Pod::Spec.new do |s|
|
||||
|
||||
s.requires_arc = true
|
||||
|
||||
s.dependency 'LMGPUImage', '~> 0.1.9'
|
||||
s.dependency 'pili-librtmp', '~> 1.0.3.1'
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Generated
BIN
Binary file not shown.
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.9.5</string>
|
||||
<string>2.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#import "LFStreamRTMPSocket.h"
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFGPUImageBeautyFilter.h"
|
||||
#import "LFH264VideoEncoder.h"
|
||||
|
||||
#define LFLiveReportKey @"com.youku.liveSessionReport"
|
||||
|
||||
@@ -290,7 +291,12 @@
|
||||
|
||||
- (id<LFVideoEncoding>)videoEncoder {
|
||||
if (!_videoEncoder) {
|
||||
_videoEncoder = [[LFHardwareVideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
|
||||
if([[UIDevice currentDevice].systemVersion floatValue] < 8.0){
|
||||
_videoEncoder = [[LFH264VideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
|
||||
}else{
|
||||
_videoEncoder = [[LFHardwareVideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
|
||||
}
|
||||
|
||||
[_videoEncoder setDelegate:self];
|
||||
}
|
||||
return _videoEncoder;
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// This is Jeff LaMarche's GLProgram OpenGL shader wrapper class from his OpenGL ES 2.0 book.
|
||||
// A description of this can be found at his page on the topic:
|
||||
// http://iphonedevelopment.blogspot.com/2010/11/opengl-es-20-for-ios-chapter-4.html
|
||||
// I've extended this to be able to take programs as NSStrings in addition to files, for baked-in shaders
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
#import <OpenGLES/ES2/gl.h>
|
||||
#import <OpenGLES/ES2/glext.h>
|
||||
#else
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#import <OpenGL/gl.h>
|
||||
#endif
|
||||
|
||||
@interface GLProgram : NSObject
|
||||
{
|
||||
NSMutableArray *attributes;
|
||||
NSMutableArray *uniforms;
|
||||
GLuint program,
|
||||
vertShader,
|
||||
fragShader;
|
||||
}
|
||||
|
||||
@property(readwrite, nonatomic) BOOL initialized;
|
||||
@property(readwrite, copy, nonatomic) NSString *vertexShaderLog;
|
||||
@property(readwrite, copy, nonatomic) NSString *fragmentShaderLog;
|
||||
@property(readwrite, copy, nonatomic) NSString *programLog;
|
||||
|
||||
- (id)initWithVertexShaderString:(NSString *)vShaderString
|
||||
fragmentShaderString:(NSString *)fShaderString;
|
||||
- (id)initWithVertexShaderString:(NSString *)vShaderString
|
||||
fragmentShaderFilename:(NSString *)fShaderFilename;
|
||||
- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename
|
||||
fragmentShaderFilename:(NSString *)fShaderFilename;
|
||||
- (void)addAttribute:(NSString *)attributeName;
|
||||
- (GLuint)attributeIndex:(NSString *)attributeName;
|
||||
- (GLuint)uniformIndex:(NSString *)uniformName;
|
||||
- (BOOL)link;
|
||||
- (void)use;
|
||||
- (void)validate;
|
||||
@end
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
// This is Jeff LaMarche's GLProgram OpenGL shader wrapper class from his OpenGL ES 2.0 book.
|
||||
// A description of this can be found at his page on the topic:
|
||||
// http://iphonedevelopment.blogspot.com/2010/11/opengl-es-20-for-ios-chapter-4.html
|
||||
|
||||
|
||||
#import "GLProgram.h"
|
||||
// START:typedefs
|
||||
#pragma mark Function Pointer Definitions
|
||||
typedef void (*GLInfoFunction)(GLuint program, GLenum pname, GLint* params);
|
||||
typedef void (*GLLogFunction) (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog);
|
||||
// END:typedefs
|
||||
#pragma mark -
|
||||
#pragma mark Private Extension Method Declaration
|
||||
// START:extension
|
||||
@interface GLProgram()
|
||||
|
||||
- (BOOL)compileShader:(GLuint *)shader
|
||||
type:(GLenum)type
|
||||
string:(NSString *)shaderString;
|
||||
@end
|
||||
// END:extension
|
||||
#pragma mark -
|
||||
|
||||
@implementation GLProgram
|
||||
// START:init
|
||||
|
||||
@synthesize initialized = _initialized;
|
||||
|
||||
- (id)initWithVertexShaderString:(NSString *)vShaderString
|
||||
fragmentShaderString:(NSString *)fShaderString;
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
_initialized = NO;
|
||||
|
||||
attributes = [[NSMutableArray alloc] init];
|
||||
uniforms = [[NSMutableArray alloc] init];
|
||||
program = glCreateProgram();
|
||||
|
||||
if (![self compileShader:&vertShader
|
||||
type:GL_VERTEX_SHADER
|
||||
string:vShaderString])
|
||||
{
|
||||
NSLog(@"Failed to compile vertex shader");
|
||||
}
|
||||
|
||||
// Create and compile fragment shader
|
||||
if (![self compileShader:&fragShader
|
||||
type:GL_FRAGMENT_SHADER
|
||||
string:fShaderString])
|
||||
{
|
||||
NSLog(@"Failed to compile fragment shader");
|
||||
}
|
||||
|
||||
glAttachShader(program, vertShader);
|
||||
glAttachShader(program, fragShader);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithVertexShaderString:(NSString *)vShaderString
|
||||
fragmentShaderFilename:(NSString *)fShaderFilename;
|
||||
{
|
||||
NSString *fragShaderPathname = [[NSBundle mainBundle] pathForResource:fShaderFilename ofType:@"fsh"];
|
||||
NSString *fragmentShaderString = [NSString stringWithContentsOfFile:fragShaderPathname encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
if ((self = [self initWithVertexShaderString:vShaderString fragmentShaderString:fragmentShaderString]))
|
||||
{
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename
|
||||
fragmentShaderFilename:(NSString *)fShaderFilename;
|
||||
{
|
||||
NSString *vertShaderPathname = [[NSBundle mainBundle] pathForResource:vShaderFilename ofType:@"vsh"];
|
||||
NSString *vertexShaderString = [NSString stringWithContentsOfFile:vertShaderPathname encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
NSString *fragShaderPathname = [[NSBundle mainBundle] pathForResource:fShaderFilename ofType:@"fsh"];
|
||||
NSString *fragmentShaderString = [NSString stringWithContentsOfFile:fragShaderPathname encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
if ((self = [self initWithVertexShaderString:vertexShaderString fragmentShaderString:fragmentShaderString]))
|
||||
{
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
// END:init
|
||||
// START:compile
|
||||
- (BOOL)compileShader:(GLuint *)shader
|
||||
type:(GLenum)type
|
||||
string:(NSString *)shaderString
|
||||
{
|
||||
// CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
|
||||
|
||||
GLint status;
|
||||
const GLchar *source;
|
||||
|
||||
source =
|
||||
(GLchar *)[shaderString UTF8String];
|
||||
if (!source)
|
||||
{
|
||||
NSLog(@"Failed to load vertex shader");
|
||||
return NO;
|
||||
}
|
||||
|
||||
*shader = glCreateShader(type);
|
||||
glShaderSource(*shader, 1, &source, NULL);
|
||||
glCompileShader(*shader);
|
||||
|
||||
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
|
||||
|
||||
if (status != GL_TRUE)
|
||||
{
|
||||
GLint logLength;
|
||||
glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
|
||||
if (logLength > 0)
|
||||
{
|
||||
GLchar *log = (GLchar *)malloc(logLength);
|
||||
glGetShaderInfoLog(*shader, logLength, &logLength, log);
|
||||
if (shader == &vertShader)
|
||||
{
|
||||
self.vertexShaderLog = [NSString stringWithFormat:@"%s", log];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.fragmentShaderLog = [NSString stringWithFormat:@"%s", log];
|
||||
}
|
||||
|
||||
free(log);
|
||||
}
|
||||
}
|
||||
|
||||
// CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
|
||||
// NSLog(@"Compiled in %f ms", linkTime * 1000.0);
|
||||
|
||||
return status == GL_TRUE;
|
||||
}
|
||||
// END:compile
|
||||
#pragma mark -
|
||||
// START:addattribute
|
||||
- (void)addAttribute:(NSString *)attributeName
|
||||
{
|
||||
if (![attributes containsObject:attributeName])
|
||||
{
|
||||
[attributes addObject:attributeName];
|
||||
glBindAttribLocation(program,
|
||||
(GLuint)[attributes indexOfObject:attributeName],
|
||||
[attributeName UTF8String]);
|
||||
}
|
||||
}
|
||||
// END:addattribute
|
||||
// START:indexmethods
|
||||
- (GLuint)attributeIndex:(NSString *)attributeName
|
||||
{
|
||||
return (GLuint)[attributes indexOfObject:attributeName];
|
||||
}
|
||||
- (GLuint)uniformIndex:(NSString *)uniformName
|
||||
{
|
||||
return glGetUniformLocation(program, [uniformName UTF8String]);
|
||||
}
|
||||
// END:indexmethods
|
||||
#pragma mark -
|
||||
// START:link
|
||||
- (BOOL)link
|
||||
{
|
||||
// CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
|
||||
|
||||
GLint status;
|
||||
|
||||
glLinkProgram(program);
|
||||
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
||||
if (status == GL_FALSE)
|
||||
return NO;
|
||||
|
||||
if (vertShader)
|
||||
{
|
||||
glDeleteShader(vertShader);
|
||||
vertShader = 0;
|
||||
}
|
||||
if (fragShader)
|
||||
{
|
||||
glDeleteShader(fragShader);
|
||||
fragShader = 0;
|
||||
}
|
||||
|
||||
self.initialized = YES;
|
||||
|
||||
// CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
|
||||
// NSLog(@"Linked in %f ms", linkTime * 1000.0);
|
||||
|
||||
return YES;
|
||||
}
|
||||
// END:link
|
||||
// START:use
|
||||
- (void)use
|
||||
{
|
||||
glUseProgram(program);
|
||||
}
|
||||
// END:use
|
||||
#pragma mark -
|
||||
|
||||
- (void)validate;
|
||||
{
|
||||
GLint logLength;
|
||||
|
||||
glValidateProgram(program);
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
|
||||
if (logLength > 0)
|
||||
{
|
||||
GLchar *log = (GLchar *)malloc(logLength);
|
||||
glGetProgramInfoLog(program, logLength, &logLength, log);
|
||||
self.programLog = [NSString stringWithFormat:@"%s", log];
|
||||
free(log);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
// START:dealloc
|
||||
- (void)dealloc
|
||||
{
|
||||
if (vertShader)
|
||||
glDeleteShader(vertShader);
|
||||
|
||||
if (fragShader)
|
||||
glDeleteShader(fragShader);
|
||||
|
||||
if (program)
|
||||
glDeleteProgram(program);
|
||||
|
||||
}
|
||||
// END:dealloc
|
||||
@end
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
#import "GLProgram.h"
|
||||
|
||||
// Base classes
|
||||
#import "GPUImageContext.h"
|
||||
#import "GPUImageOutput.h"
|
||||
#import "GPUImageView.h"
|
||||
#import "GPUImageVideoCamera.h"
|
||||
#import "GPUImageStillCamera.h"
|
||||
#import "GPUImageMovie.h"
|
||||
#import "GPUImagePicture.h"
|
||||
#import "GPUImageRawDataInput.h"
|
||||
#import "GPUImageRawDataOutput.h"
|
||||
#import "GPUImageMovieWriter.h"
|
||||
#import "GPUImageFilterPipeline.h"
|
||||
#import "GPUImageTextureOutput.h"
|
||||
#import "GPUImageFilterGroup.h"
|
||||
#import "GPUImageTextureInput.h"
|
||||
#import "GPUImageUIElement.h"
|
||||
#import "GPUImageBuffer.h"
|
||||
#import "GPUImageFramebuffer.h"
|
||||
#import "GPUImageFramebufferCache.h"
|
||||
|
||||
// Filters
|
||||
#import "GPUImageFilter.h"
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
#import "GPUImagePixellateFilter.h"
|
||||
#import "GPUImagePixellatePositionFilter.h"
|
||||
#import "GPUImageSepiaFilter.h"
|
||||
#import "GPUImageColorInvertFilter.h"
|
||||
#import "GPUImageSaturationFilter.h"
|
||||
#import "GPUImageContrastFilter.h"
|
||||
#import "GPUImageExposureFilter.h"
|
||||
#import "GPUImageBrightnessFilter.h"
|
||||
#import "GPUImageLevelsFilter.h"
|
||||
#import "GPUImageSharpenFilter.h"
|
||||
#import "GPUImageGammaFilter.h"
|
||||
#import "GPUImageSobelEdgeDetectionFilter.h"
|
||||
#import "GPUImageSketchFilter.h"
|
||||
#import "GPUImageToonFilter.h"
|
||||
#import "GPUImageSmoothToonFilter.h"
|
||||
#import "GPUImageMultiplyBlendFilter.h"
|
||||
#import "GPUImageDissolveBlendFilter.h"
|
||||
#import "GPUImageKuwaharaFilter.h"
|
||||
#import "GPUImageKuwaharaRadius3Filter.h"
|
||||
#import "GPUImageVignetteFilter.h"
|
||||
#import "GPUImageGaussianBlurFilter.h"
|
||||
#import "GPUImageGaussianBlurPositionFilter.h"
|
||||
#import "GPUImageGaussianSelectiveBlurFilter.h"
|
||||
#import "GPUImageOverlayBlendFilter.h"
|
||||
#import "GPUImageDarkenBlendFilter.h"
|
||||
#import "GPUImageLightenBlendFilter.h"
|
||||
#import "GPUImageSwirlFilter.h"
|
||||
#import "GPUImageSourceOverBlendFilter.h"
|
||||
#import "GPUImageColorBurnBlendFilter.h"
|
||||
#import "GPUImageColorDodgeBlendFilter.h"
|
||||
#import "GPUImageScreenBlendFilter.h"
|
||||
#import "GPUImageExclusionBlendFilter.h"
|
||||
#import "GPUImageDifferenceBlendFilter.h"
|
||||
#import "GPUImageSubtractBlendFilter.h"
|
||||
#import "GPUImageHardLightBlendFilter.h"
|
||||
#import "GPUImageSoftLightBlendFilter.h"
|
||||
#import "GPUImageColorBlendFilter.h"
|
||||
#import "GPUImageHueBlendFilter.h"
|
||||
#import "GPUImageSaturationBlendFilter.h"
|
||||
#import "GPUImageLuminosityBlendFilter.h"
|
||||
#import "GPUImageCropFilter.h"
|
||||
#import "GPUImageGrayscaleFilter.h"
|
||||
#import "GPUImageTransformFilter.h"
|
||||
#import "GPUImageChromaKeyBlendFilter.h"
|
||||
#import "GPUImageHazeFilter.h"
|
||||
#import "GPUImageLuminanceThresholdFilter.h"
|
||||
#import "GPUImagePosterizeFilter.h"
|
||||
#import "GPUImageBoxBlurFilter.h"
|
||||
#import "GPUImageAdaptiveThresholdFilter.h"
|
||||
#import "GPUImageSolarizeFilter.h"
|
||||
#import "GPUImageUnsharpMaskFilter.h"
|
||||
#import "GPUImageBulgeDistortionFilter.h"
|
||||
#import "GPUImagePinchDistortionFilter.h"
|
||||
#import "GPUImageCrosshatchFilter.h"
|
||||
#import "GPUImageCGAColorspaceFilter.h"
|
||||
#import "GPUImagePolarPixellateFilter.h"
|
||||
#import "GPUImageStretchDistortionFilter.h"
|
||||
#import "GPUImagePerlinNoiseFilter.h"
|
||||
#import "GPUImageJFAVoronoiFilter.h"
|
||||
#import "GPUImageVoronoiConsumerFilter.h"
|
||||
#import "GPUImageMosaicFilter.h"
|
||||
#import "GPUImageTiltShiftFilter.h"
|
||||
#import "GPUImage3x3ConvolutionFilter.h"
|
||||
#import "GPUImageEmbossFilter.h"
|
||||
#import "GPUImageCannyEdgeDetectionFilter.h"
|
||||
#import "GPUImageThresholdEdgeDetectionFilter.h"
|
||||
#import "GPUImageMaskFilter.h"
|
||||
#import "GPUImageHistogramFilter.h"
|
||||
#import "GPUImageHistogramGenerator.h"
|
||||
#import "GPUImageHistogramEqualizationFilter.h"
|
||||
#import "GPUImagePrewittEdgeDetectionFilter.h"
|
||||
#import "GPUImageXYDerivativeFilter.h"
|
||||
#import "GPUImageHarrisCornerDetectionFilter.h"
|
||||
#import "GPUImageAlphaBlendFilter.h"
|
||||
#import "GPUImageNormalBlendFilter.h"
|
||||
#import "GPUImageNonMaximumSuppressionFilter.h"
|
||||
#import "GPUImageRGBFilter.h"
|
||||
#import "GPUImageMedianFilter.h"
|
||||
#import "GPUImageBilateralFilter.h"
|
||||
#import "GPUImageCrosshairGenerator.h"
|
||||
#import "GPUImageToneCurveFilter.h"
|
||||
#import "GPUImageNobleCornerDetectionFilter.h"
|
||||
#import "GPUImageShiTomasiFeatureDetectionFilter.h"
|
||||
#import "GPUImageErosionFilter.h"
|
||||
#import "GPUImageRGBErosionFilter.h"
|
||||
#import "GPUImageDilationFilter.h"
|
||||
#import "GPUImageRGBDilationFilter.h"
|
||||
#import "GPUImageOpeningFilter.h"
|
||||
#import "GPUImageRGBOpeningFilter.h"
|
||||
#import "GPUImageClosingFilter.h"
|
||||
#import "GPUImageRGBClosingFilter.h"
|
||||
#import "GPUImageColorPackingFilter.h"
|
||||
#import "GPUImageSphereRefractionFilter.h"
|
||||
#import "GPUImageMonochromeFilter.h"
|
||||
#import "GPUImageOpacityFilter.h"
|
||||
#import "GPUImageHighlightShadowFilter.h"
|
||||
#import "GPUImageFalseColorFilter.h"
|
||||
#import "GPUImageHSBFilter.h"
|
||||
#import "GPUImageHueFilter.h"
|
||||
#import "GPUImageGlassSphereFilter.h"
|
||||
#import "GPUImageLookupFilter.h"
|
||||
#import "GPUImageAmatorkaFilter.h"
|
||||
#import "GPUImageMissEtikateFilter.h"
|
||||
#import "GPUImageSoftEleganceFilter.h"
|
||||
#import "GPUImageAddBlendFilter.h"
|
||||
#import "GPUImageDivideBlendFilter.h"
|
||||
#import "GPUImagePolkaDotFilter.h"
|
||||
#import "GPUImageLocalBinaryPatternFilter.h"
|
||||
#import "GPUImageColorLocalBinaryPatternFilter.h"
|
||||
#import "GPUImageLanczosResamplingFilter.h"
|
||||
#import "GPUImageAverageColor.h"
|
||||
#import "GPUImageSolidColorGenerator.h"
|
||||
#import "GPUImageLuminosity.h"
|
||||
#import "GPUImageAverageLuminanceThresholdFilter.h"
|
||||
#import "GPUImageWhiteBalanceFilter.h"
|
||||
#import "GPUImageChromaKeyFilter.h"
|
||||
#import "GPUImageLowPassFilter.h"
|
||||
#import "GPUImageHighPassFilter.h"
|
||||
#import "GPUImageMotionDetector.h"
|
||||
#import "GPUImageHalftoneFilter.h"
|
||||
#import "GPUImageThresholdedNonMaximumSuppressionFilter.h"
|
||||
#import "GPUImageHoughTransformLineDetector.h"
|
||||
#import "GPUImageParallelCoordinateLineTransformFilter.h"
|
||||
#import "GPUImageThresholdSketchFilter.h"
|
||||
#import "GPUImageLineGenerator.h"
|
||||
#import "GPUImageLinearBurnBlendFilter.h"
|
||||
#import "GPUImageGaussianBlurPositionFilter.h"
|
||||
#import "GPUImagePixellatePositionFilter.h"
|
||||
#import "GPUImageTwoInputCrossTextureSamplingFilter.h"
|
||||
#import "GPUImagePoissonBlendFilter.h"
|
||||
#import "GPUImageMotionBlurFilter.h"
|
||||
#import "GPUImageZoomBlurFilter.h"
|
||||
#import "GPUImageLaplacianFilter.h"
|
||||
#import "GPUImageiOSBlurFilter.h"
|
||||
#import "GPUImageLuminanceRangeFilter.h"
|
||||
#import "GPUImageDirectionalNonMaximumSuppressionFilter.h"
|
||||
#import "GPUImageDirectionalSobelEdgeDetectionFilter.h"
|
||||
#import "GPUImageSingleComponentGaussianBlurFilter.h"
|
||||
#import "GPUImageThreeInputFilter.h"
|
||||
#import "GPUImageFourInputFilter.h"
|
||||
#import "GPUImageWeakPixelInclusionFilter.h"
|
||||
#import "GPUImageColorConversion.h"
|
||||
#import "GPUImageColourFASTFeatureDetector.h"
|
||||
#import "GPUImageColourFASTSamplingOperation.h"
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#import "GPUImage3x3TextureSamplingFilter.h"
|
||||
|
||||
/** Runs a 3x3 convolution kernel against the image
|
||||
*/
|
||||
@interface GPUImage3x3ConvolutionFilter : GPUImage3x3TextureSamplingFilter
|
||||
{
|
||||
GLint convolutionMatrixUniform;
|
||||
}
|
||||
|
||||
/** Convolution kernel to run against the image
|
||||
|
||||
The convolution kernel is a 3x3 matrix of values to apply to the pixel and its 8 surrounding pixels.
|
||||
The matrix is specified in row-major order, with the top left pixel being one.one and the bottom right three.three
|
||||
If the values in the matrix don't add up to 1.0, the image could be brightened or darkened.
|
||||
*/
|
||||
@property(readwrite, nonatomic) GPUMatrix3x3 convolutionKernel;
|
||||
|
||||
@end
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
#import "GPUImage3x3ConvolutionFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImage3x3ConvolutionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mediump mat3 convolutionMatrix;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
|
||||
mediump vec3 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).rgb;
|
||||
mediump vec3 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).rgb;
|
||||
mediump vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
mediump vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
|
||||
mediump vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
|
||||
mediump vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
|
||||
mediump vec3 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).rgb;
|
||||
mediump vec3 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).rgb;
|
||||
|
||||
mediump vec3 resultColor = topLeftColor * convolutionMatrix[0][0] + topColor * convolutionMatrix[0][1] + topRightColor * convolutionMatrix[0][2];
|
||||
resultColor += leftColor * convolutionMatrix[1][0] + centerColor.rgb * convolutionMatrix[1][1] + rightColor * convolutionMatrix[1][2];
|
||||
resultColor += bottomLeftColor * convolutionMatrix[2][0] + bottomColor * convolutionMatrix[2][1] + bottomRightColor * convolutionMatrix[2][2];
|
||||
|
||||
gl_FragColor = vec4(resultColor, centerColor.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImage3x3ConvolutionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mat3 convolutionMatrix;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
|
||||
vec3 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).rgb;
|
||||
vec3 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).rgb;
|
||||
vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
|
||||
vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
|
||||
vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
|
||||
vec3 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).rgb;
|
||||
vec3 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).rgb;
|
||||
|
||||
vec3 resultColor = topLeftColor * convolutionMatrix[0][0] + topColor * convolutionMatrix[0][1] + topRightColor * convolutionMatrix[0][2];
|
||||
resultColor += leftColor * convolutionMatrix[1][0] + centerColor.rgb * convolutionMatrix[1][1] + rightColor * convolutionMatrix[1][2];
|
||||
resultColor += bottomLeftColor * convolutionMatrix[2][0] + bottomColor * convolutionMatrix[2][1] + bottomRightColor * convolutionMatrix[2][2];
|
||||
|
||||
gl_FragColor = vec4(resultColor, centerColor.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImage3x3ConvolutionFilter
|
||||
|
||||
@synthesize convolutionKernel = _convolutionKernel;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithFragmentShaderFromString:kGPUImage3x3ConvolutionFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.convolutionKernel = (GPUMatrix3x3){
|
||||
{0.f, 0.f, 0.f},
|
||||
{0.f, 1.f, 0.f},
|
||||
{0.f, 0.f, 0.f}
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:fragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
convolutionMatrixUniform = [filterProgram uniformIndex:@"convolutionMatrix"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setConvolutionKernel:(GPUMatrix3x3)newValue;
|
||||
{
|
||||
_convolutionKernel = newValue;
|
||||
|
||||
[self setMatrix3f:_convolutionKernel forUniform:convolutionMatrixUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
extern NSString *const kGPUImageNearbyTexelSamplingVertexShaderString;
|
||||
|
||||
@interface GPUImage3x3TextureSamplingFilter : GPUImageFilter
|
||||
{
|
||||
GLint texelWidthUniform, texelHeightUniform;
|
||||
|
||||
CGFloat texelWidth, texelHeight;
|
||||
BOOL hasOverriddenImageSizeFactor;
|
||||
}
|
||||
|
||||
// The texel width and height determines how far out to sample from this texel. By default, this is the normalized width of a pixel, but this can be overridden for different effects.
|
||||
@property(readwrite, nonatomic) CGFloat texelWidth;
|
||||
@property(readwrite, nonatomic) CGFloat texelHeight;
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,121 @@
|
||||
#import "GPUImage3x3TextureSamplingFilter.h"
|
||||
|
||||
// Override vertex shader to remove dependent texture reads
|
||||
NSString *const kGPUImageNearbyTexelSamplingVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec4 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidth;
|
||||
uniform float texelHeight;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 widthStep = vec2(texelWidth, 0.0);
|
||||
vec2 heightStep = vec2(0.0, texelHeight);
|
||||
vec2 widthHeightStep = vec2(texelWidth, texelHeight);
|
||||
vec2 widthNegativeHeightStep = vec2(texelWidth, -texelHeight);
|
||||
|
||||
textureCoordinate = inputTextureCoordinate.xy;
|
||||
leftTextureCoordinate = inputTextureCoordinate.xy - widthStep;
|
||||
rightTextureCoordinate = inputTextureCoordinate.xy + widthStep;
|
||||
|
||||
topTextureCoordinate = inputTextureCoordinate.xy - heightStep;
|
||||
topLeftTextureCoordinate = inputTextureCoordinate.xy - widthHeightStep;
|
||||
topRightTextureCoordinate = inputTextureCoordinate.xy + widthNegativeHeightStep;
|
||||
|
||||
bottomTextureCoordinate = inputTextureCoordinate.xy + heightStep;
|
||||
bottomLeftTextureCoordinate = inputTextureCoordinate.xy - widthNegativeHeightStep;
|
||||
bottomRightTextureCoordinate = inputTextureCoordinate.xy + widthHeightStep;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@implementation GPUImage3x3TextureSamplingFilter
|
||||
|
||||
@synthesize texelWidth = _texelWidth;
|
||||
@synthesize texelHeight = _texelHeight;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithVertexShaderFromString:(NSString *)vertexShaderString fragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
{
|
||||
if (!(self = [super initWithVertexShaderFromString:vertexShaderString fragmentShaderFromString:fragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
|
||||
texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
{
|
||||
if (!(self = [self initWithVertexShaderFromString:kGPUImageNearbyTexelSamplingVertexShaderString fragmentShaderFromString:fragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
if (!hasOverriddenImageSizeFactor)
|
||||
{
|
||||
_texelWidth = 1.0 / filterFrameSize.width;
|
||||
_texelHeight = 1.0 / filterFrameSize.height;
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
if (GPUImageRotationSwapsWidthAndHeight(inputRotation))
|
||||
{
|
||||
glUniform1f(texelWidthUniform, _texelHeight);
|
||||
glUniform1f(texelHeightUniform, _texelWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
glUniform1f(texelWidthUniform, _texelWidth);
|
||||
glUniform1f(texelHeightUniform, _texelHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setTexelWidth:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelWidth = newValue;
|
||||
|
||||
[self setFloat:_texelWidth forUniform:texelWidthUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setTexelHeight:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelHeight = newValue;
|
||||
|
||||
[self setFloat:_texelHeight forUniform:texelHeightUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@interface GPUImageAdaptiveThresholdFilter : GPUImageFilterGroup
|
||||
|
||||
/** A multiplier for the background averaging blur radius in pixels, with a default of 4
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat blurRadiusInPixels;
|
||||
|
||||
@end
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#import "GPUImageAdaptiveThresholdFilter.h"
|
||||
#import "GPUImageFilter.h"
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
#import "GPUImageGrayscaleFilter.h"
|
||||
#import "GPUImageBoxBlurFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageAdaptiveThresholdFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp float blurredInput = texture2D(inputImageTexture, textureCoordinate).r;
|
||||
highp float localLuminance = texture2D(inputImageTexture2, textureCoordinate2).r;
|
||||
highp float thresholdResult = step(blurredInput - 0.05, localLuminance);
|
||||
|
||||
gl_FragColor = vec4(vec3(thresholdResult), 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageAdaptiveThresholdFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
float blurredInput = texture2D(inputImageTexture, textureCoordinate).r;
|
||||
float localLuminance = texture2D(inputImageTexture2, textureCoordinate2).r;
|
||||
float thresholdResult = step(blurredInput - 0.05, localLuminance);
|
||||
|
||||
gl_FragColor = vec4(vec3(thresholdResult), 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@interface GPUImageAdaptiveThresholdFilter()
|
||||
{
|
||||
GPUImageBoxBlurFilter *boxBlurFilter;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GPUImageAdaptiveThresholdFilter
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// First pass: reduce to luminance
|
||||
GPUImageGrayscaleFilter *luminanceFilter = [[GPUImageGrayscaleFilter alloc] init];
|
||||
[self addFilter:luminanceFilter];
|
||||
|
||||
// Second pass: perform a box blur
|
||||
boxBlurFilter = [[GPUImageBoxBlurFilter alloc] init];
|
||||
[self addFilter:boxBlurFilter];
|
||||
|
||||
// Third pass: compare the blurred background luminance to the local value
|
||||
GPUImageFilter *adaptiveThresholdFilter = [[GPUImageTwoInputFilter alloc] initWithFragmentShaderFromString:kGPUImageAdaptiveThresholdFragmentShaderString];
|
||||
[self addFilter:adaptiveThresholdFilter];
|
||||
|
||||
[luminanceFilter addTarget:boxBlurFilter];
|
||||
|
||||
[boxBlurFilter addTarget:adaptiveThresholdFilter];
|
||||
// To prevent double updating of this filter, disable updates from the sharp luminance image side
|
||||
[luminanceFilter addTarget:adaptiveThresholdFilter];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObject:luminanceFilter];
|
||||
self.terminalFilter = adaptiveThresholdFilter;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBlurRadiusInPixels:(CGFloat)newValue;
|
||||
{
|
||||
boxBlurFilter.blurRadiusInPixels = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)blurRadiusInPixels;
|
||||
{
|
||||
return boxBlurFilter.blurRadiusInPixels;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageAddBlendFilter : GPUImageTwoInputFilter
|
||||
|
||||
@end
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#import "GPUImageAddBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageAddBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
lowp vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
mediump float r;
|
||||
if (overlay.r * base.a + base.r * overlay.a >= overlay.a * base.a) {
|
||||
r = overlay.a * base.a + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
} else {
|
||||
r = overlay.r + base.r;
|
||||
}
|
||||
|
||||
mediump float g;
|
||||
if (overlay.g * base.a + base.g * overlay.a >= overlay.a * base.a) {
|
||||
g = overlay.a * base.a + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
} else {
|
||||
g = overlay.g + base.g;
|
||||
}
|
||||
|
||||
mediump float b;
|
||||
if (overlay.b * base.a + base.b * overlay.a >= overlay.a * base.a) {
|
||||
b = overlay.a * base.a + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
} else {
|
||||
b = overlay.b + base.b;
|
||||
}
|
||||
|
||||
mediump float a = overlay.a + base.a - overlay.a * base.a;
|
||||
|
||||
gl_FragColor = vec4(r, g, b, a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageAddBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
float r;
|
||||
if (overlay.r * base.a + base.r * overlay.a >= overlay.a * base.a) {
|
||||
r = overlay.a * base.a + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
} else {
|
||||
r = overlay.r + base.r;
|
||||
}
|
||||
|
||||
float g;
|
||||
if (overlay.g * base.a + base.g * overlay.a >= overlay.a * base.a) {
|
||||
g = overlay.a * base.a + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
} else {
|
||||
g = overlay.g + base.g;
|
||||
}
|
||||
|
||||
float b;
|
||||
if (overlay.b * base.a + base.b * overlay.a >= overlay.a * base.a) {
|
||||
b = overlay.a * base.a + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
} else {
|
||||
b = overlay.b + base.b;
|
||||
}
|
||||
|
||||
float a = overlay.a + base.a - overlay.a * base.a;
|
||||
|
||||
gl_FragColor = vec4(r, g, b, a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@implementation GPUImageAddBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageAddBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageAlphaBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
GLint mixUniform;
|
||||
}
|
||||
|
||||
// Mix ranges from 0.0 (only image 1) to 1.0 (only image 2), with 1.0 as the normal level
|
||||
@property(readwrite, nonatomic) CGFloat mix;
|
||||
|
||||
@end
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#import "GPUImageAlphaBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageAlphaBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
uniform lowp float mixturePercent;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
lowp vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(mix(textureColor.rgb, textureColor2.rgb, textureColor2.a * mixturePercent), textureColor.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageAlphaBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
uniform float mixturePercent;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(mix(textureColor.rgb, textureColor2.rgb, textureColor2.a * mixturePercent), textureColor.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageAlphaBlendFilter
|
||||
|
||||
@synthesize mix = _mix;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageAlphaBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
mixUniform = [filterProgram uniformIndex:@"mixturePercent"];
|
||||
self.mix = 0.5;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setMix:(CGFloat)newValue;
|
||||
{
|
||||
_mix = newValue;
|
||||
|
||||
[self setFloat:_mix forUniform:mixUniform program:filterProgram];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@class GPUImagePicture;
|
||||
|
||||
/** A photo filter based on Photoshop action by Amatorka
|
||||
http://amatorka.deviantart.com/art/Amatorka-Action-2-121069631
|
||||
*/
|
||||
|
||||
// Note: If you want to use this effect you have to add lookup_amatorka.png
|
||||
// from Resources folder to your application bundle.
|
||||
|
||||
@interface GPUImageAmatorkaFilter : GPUImageFilterGroup
|
||||
{
|
||||
GPUImagePicture *lookupImageSource;
|
||||
}
|
||||
|
||||
@end
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#import "GPUImageAmatorkaFilter.h"
|
||||
#import "GPUImagePicture.h"
|
||||
#import "GPUImageLookupFilter.h"
|
||||
|
||||
@implementation GPUImageAmatorkaFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
UIImage *image = [UIImage imageNamed:@"lookup_amatorka.png"];
|
||||
#else
|
||||
NSImage *image = [NSImage imageNamed:@"lookup_amatorka.png"];
|
||||
#endif
|
||||
|
||||
NSAssert(image, @"To use GPUImageAmatorkaFilter you need to add lookup_amatorka.png from GPUImage/framework/Resources to your application bundle.");
|
||||
|
||||
lookupImageSource = [[GPUImagePicture alloc] initWithImage:image];
|
||||
GPUImageLookupFilter *lookupFilter = [[GPUImageLookupFilter alloc] init];
|
||||
[self addFilter:lookupFilter];
|
||||
|
||||
[lookupImageSource addTarget:lookupFilter atTextureLocation:1];
|
||||
[lookupImageSource processImage];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObjects:lookupFilter, nil];
|
||||
self.terminalFilter = lookupFilter;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
extern NSString *const kGPUImageColorAveragingVertexShaderString;
|
||||
|
||||
@interface GPUImageAverageColor : GPUImageFilter
|
||||
{
|
||||
GLint texelWidthUniform, texelHeightUniform;
|
||||
|
||||
NSUInteger numberOfStages;
|
||||
|
||||
GLubyte *rawImagePixels;
|
||||
CGSize finalStageSize;
|
||||
}
|
||||
|
||||
// This block is called on the completion of color averaging for a frame
|
||||
@property(nonatomic, copy) void(^colorAverageProcessingFinishedBlock)(CGFloat redComponent, CGFloat greenComponent, CGFloat blueComponent, CGFloat alphaComponent, CMTime frameTime);
|
||||
|
||||
- (void)extractAverageColorAtFrameTime:(CMTime)frameTime;
|
||||
|
||||
@end
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
#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)));
|
||||
|
||||
[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.25 / currentStageSize.width);
|
||||
glUniform1f(texelHeightUniform, 0.25 / 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
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@interface GPUImageAverageLuminanceThresholdFilter : GPUImageFilterGroup
|
||||
|
||||
// This is multiplied by the continually calculated average image luminosity to arrive at the final threshold. Default is 1.0.
|
||||
@property(readwrite, nonatomic) CGFloat thresholdMultiplier;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,47 @@
|
||||
#import "GPUImageAverageLuminanceThresholdFilter.h"
|
||||
#import "GPUImageLuminosity.h"
|
||||
#import "GPUImageLuminanceThresholdFilter.h"
|
||||
|
||||
@interface GPUImageAverageLuminanceThresholdFilter()
|
||||
{
|
||||
GPUImageLuminosity *luminosityFilter;
|
||||
GPUImageLuminanceThresholdFilter *luminanceThresholdFilter;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GPUImageAverageLuminanceThresholdFilter
|
||||
|
||||
@synthesize thresholdMultiplier = _thresholdMultiplier;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.thresholdMultiplier = 1.0;
|
||||
|
||||
luminosityFilter = [[GPUImageLuminosity alloc] init];
|
||||
[self addFilter:luminosityFilter];
|
||||
|
||||
luminanceThresholdFilter = [[GPUImageLuminanceThresholdFilter alloc] init];
|
||||
[self addFilter:luminanceThresholdFilter];
|
||||
|
||||
__unsafe_unretained GPUImageAverageLuminanceThresholdFilter *weakSelf = self;
|
||||
__unsafe_unretained GPUImageLuminanceThresholdFilter *weakThreshold = luminanceThresholdFilter;
|
||||
|
||||
[luminosityFilter setLuminosityProcessingFinishedBlock:^(CGFloat luminosity, CMTime frameTime) {
|
||||
weakThreshold.threshold = luminosity * weakSelf.thresholdMultiplier;
|
||||
}];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObjects:luminosityFilter, luminanceThresholdFilter, nil];
|
||||
self.terminalFilter = luminanceThresholdFilter;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,10 @@
|
||||
#import "GPUImageGaussianBlurFilter.h"
|
||||
|
||||
@interface GPUImageBilateralFilter : GPUImageGaussianBlurFilter
|
||||
{
|
||||
CGFloat firstDistanceNormalizationFactorUniform;
|
||||
CGFloat secondDistanceNormalizationFactorUniform;
|
||||
}
|
||||
// A normalization factor for the distance between central color and sample color.
|
||||
@property(nonatomic, readwrite) CGFloat distanceNormalizationFactor;
|
||||
@end
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
#import "GPUImageBilateralFilter.h"
|
||||
|
||||
NSString *const kGPUImageBilateralBlurVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec4 inputTextureCoordinate;
|
||||
|
||||
const int GAUSSIAN_SAMPLES = 9;
|
||||
|
||||
uniform float texelWidthOffset;
|
||||
uniform float texelHeightOffset;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 blurCoordinates[GAUSSIAN_SAMPLES];
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
textureCoordinate = inputTextureCoordinate.xy;
|
||||
|
||||
// Calculate the positions for the blur
|
||||
int multiplier = 0;
|
||||
vec2 blurStep;
|
||||
vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);
|
||||
|
||||
for (int i = 0; i < GAUSSIAN_SAMPLES; i++)
|
||||
{
|
||||
multiplier = (i - ((GAUSSIAN_SAMPLES - 1) / 2));
|
||||
// Blur in x (horizontal)
|
||||
blurStep = float(multiplier) * singleStepOffset;
|
||||
blurCoordinates[i] = inputTextureCoordinate.xy + blurStep;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageBilateralFilterFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
const lowp int GAUSSIAN_SAMPLES = 9;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 blurCoordinates[GAUSSIAN_SAMPLES];
|
||||
|
||||
uniform mediump float distanceNormalizationFactor;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 centralColor;
|
||||
lowp float gaussianWeightTotal;
|
||||
lowp vec4 sum;
|
||||
lowp vec4 sampleColor;
|
||||
lowp float distanceFromCentralColor;
|
||||
lowp float gaussianWeight;
|
||||
|
||||
centralColor = texture2D(inputImageTexture, blurCoordinates[4]);
|
||||
gaussianWeightTotal = 0.18;
|
||||
sum = centralColor * 0.18;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[0]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[1]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[2]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[3]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[5]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[6]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[7]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[8]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
gl_FragColor = sum / gaussianWeightTotal;
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageBilateralFilterFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
const int GAUSSIAN_SAMPLES = 9;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 blurCoordinates[GAUSSIAN_SAMPLES];
|
||||
|
||||
uniform float distanceNormalizationFactor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 centralColor;
|
||||
float gaussianWeightTotal;
|
||||
vec4 sum;
|
||||
vec4 sampleColor;
|
||||
float distanceFromCentralColor;
|
||||
float gaussianWeight;
|
||||
|
||||
centralColor = texture2D(inputImageTexture, blurCoordinates[4]);
|
||||
gaussianWeightTotal = 0.18;
|
||||
sum = centralColor * 0.18;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[0]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[1]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[2]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[3]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[5]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[6]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[7]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[8]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
gl_FragColor = sum / gaussianWeightTotal;
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageBilateralFilter
|
||||
|
||||
@synthesize distanceNormalizationFactor = _distanceNormalizationFactor;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
|
||||
if (!(self = [super initWithFirstStageVertexShaderFromString:kGPUImageBilateralBlurVertexShaderString
|
||||
firstStageFragmentShaderFromString:kGPUImageBilateralFilterFragmentShaderString
|
||||
secondStageVertexShaderFromString:kGPUImageBilateralBlurVertexShaderString
|
||||
secondStageFragmentShaderFromString:kGPUImageBilateralFilterFragmentShaderString])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
firstDistanceNormalizationFactorUniform = [filterProgram uniformIndex:@"distanceNormalizationFactor"];
|
||||
secondDistanceNormalizationFactorUniform = [filterProgram uniformIndex:@"distanceNormalizationFactor"];
|
||||
|
||||
self.texelSpacingMultiplier = 4.0;
|
||||
self.distanceNormalizationFactor = 8.0;
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setDistanceNormalizationFactor:(CGFloat)newValue
|
||||
{
|
||||
_distanceNormalizationFactor = newValue;
|
||||
|
||||
[self setFloat:newValue
|
||||
forUniform:firstDistanceNormalizationFactorUniform
|
||||
program:filterProgram];
|
||||
|
||||
[self setFloat:newValue
|
||||
forUniform:secondDistanceNormalizationFactorUniform
|
||||
program:secondFilterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import "GPUImageGaussianBlurFilter.h"
|
||||
|
||||
/** A hardware-accelerated box blur of an image
|
||||
*/
|
||||
@interface GPUImageBoxBlurFilter : GPUImageGaussianBlurFilter
|
||||
|
||||
@end
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
#import "GPUImageBoxBlurFilter.h"
|
||||
|
||||
|
||||
@implementation GPUImageBoxBlurFilter
|
||||
|
||||
+ (NSString *)vertexShaderForOptimizedBlurOfRadius:(NSUInteger)blurRadius sigma:(CGFloat)sigma;
|
||||
{
|
||||
if (blurRadius < 1)
|
||||
{
|
||||
return kGPUImageVertexShaderString;
|
||||
}
|
||||
|
||||
// From these weights we calculate the offsets to read interpolated values from
|
||||
NSUInteger numberOfOptimizedOffsets = MIN(blurRadius / 2 + (blurRadius % 2), 7);
|
||||
|
||||
NSMutableString *shaderString = [[NSMutableString alloc] init];
|
||||
// Header
|
||||
[shaderString appendFormat:@"\
|
||||
attribute vec4 position;\n\
|
||||
attribute vec4 inputTextureCoordinate;\n\
|
||||
\n\
|
||||
uniform float texelWidthOffset;\n\
|
||||
uniform float texelHeightOffset;\n\
|
||||
\n\
|
||||
varying vec2 blurCoordinates[%lu];\n\
|
||||
\n\
|
||||
void main()\n\
|
||||
{\n\
|
||||
gl_Position = position;\n\
|
||||
\n\
|
||||
vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);\n", (unsigned long)(1 + (numberOfOptimizedOffsets * 2))];
|
||||
|
||||
// Inner offset loop
|
||||
[shaderString appendString:@"blurCoordinates[0] = inputTextureCoordinate.xy;\n"];
|
||||
for (NSUInteger currentOptimizedOffset = 0; currentOptimizedOffset < numberOfOptimizedOffsets; currentOptimizedOffset++)
|
||||
{
|
||||
GLfloat optimizedOffset = (GLfloat)(currentOptimizedOffset * 2) + 1.5;
|
||||
|
||||
[shaderString appendFormat:@"\
|
||||
blurCoordinates[%lu] = inputTextureCoordinate.xy + singleStepOffset * %f;\n\
|
||||
blurCoordinates[%lu] = inputTextureCoordinate.xy - singleStepOffset * %f;\n", (unsigned long)((currentOptimizedOffset * 2) + 1), optimizedOffset, (unsigned long)((currentOptimizedOffset * 2) + 2), optimizedOffset];
|
||||
}
|
||||
|
||||
// Footer
|
||||
[shaderString appendString:@"}\n"];
|
||||
|
||||
return shaderString;
|
||||
}
|
||||
|
||||
+ (NSString *)fragmentShaderForOptimizedBlurOfRadius:(NSUInteger)blurRadius sigma:(CGFloat)sigma;
|
||||
{
|
||||
if (blurRadius < 1)
|
||||
{
|
||||
return kGPUImagePassthroughFragmentShaderString;
|
||||
}
|
||||
|
||||
NSUInteger numberOfOptimizedOffsets = MIN(blurRadius / 2 + (blurRadius % 2), 7);
|
||||
NSUInteger trueNumberOfOptimizedOffsets = blurRadius / 2 + (blurRadius % 2);
|
||||
|
||||
NSMutableString *shaderString = [[NSMutableString alloc] init];
|
||||
|
||||
// Header
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
[shaderString appendFormat:@"\
|
||||
uniform sampler2D inputImageTexture;\n\
|
||||
uniform highp float texelWidthOffset;\n\
|
||||
uniform highp float texelHeightOffset;\n\
|
||||
\n\
|
||||
varying highp vec2 blurCoordinates[%lu];\n\
|
||||
\n\
|
||||
void main()\n\
|
||||
{\n\
|
||||
lowp vec4 sum = vec4(0.0);\n", (unsigned long)(1 + (numberOfOptimizedOffsets * 2)) ];
|
||||
#else
|
||||
[shaderString appendFormat:@"\
|
||||
uniform sampler2D inputImageTexture;\n\
|
||||
uniform float texelWidthOffset;\n\
|
||||
uniform float texelHeightOffset;\n\
|
||||
\n\
|
||||
varying vec2 blurCoordinates[%lu];\n\
|
||||
\n\
|
||||
void main()\n\
|
||||
{\n\
|
||||
vec4 sum = vec4(0.0);\n", 1 + (numberOfOptimizedOffsets * 2) ];
|
||||
#endif
|
||||
|
||||
GLfloat boxWeight = 1.0 / (GLfloat)((blurRadius * 2) + 1);
|
||||
|
||||
// Inner texture loop
|
||||
[shaderString appendFormat:@"sum += texture2D(inputImageTexture, blurCoordinates[0]) * %f;\n", boxWeight];
|
||||
|
||||
for (NSUInteger currentBlurCoordinateIndex = 0; currentBlurCoordinateIndex < numberOfOptimizedOffsets; currentBlurCoordinateIndex++)
|
||||
{
|
||||
[shaderString appendFormat:@"sum += texture2D(inputImageTexture, blurCoordinates[%lu]) * %f;\n", (unsigned long)((currentBlurCoordinateIndex * 2) + 1), boxWeight * 2.0];
|
||||
[shaderString appendFormat:@"sum += texture2D(inputImageTexture, blurCoordinates[%lu]) * %f;\n", (unsigned long)((currentBlurCoordinateIndex * 2) + 2), boxWeight * 2.0];
|
||||
}
|
||||
|
||||
// If the number of required samples exceeds the amount we can pass in via varyings, we have to do dependent texture reads in the fragment shader
|
||||
if (trueNumberOfOptimizedOffsets > numberOfOptimizedOffsets)
|
||||
{
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
[shaderString appendString:@"highp vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);\n"];
|
||||
#else
|
||||
[shaderString appendString:@"vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);\n"];
|
||||
#endif
|
||||
|
||||
for (NSUInteger currentOverlowTextureRead = numberOfOptimizedOffsets; currentOverlowTextureRead < trueNumberOfOptimizedOffsets; currentOverlowTextureRead++)
|
||||
{
|
||||
GLfloat optimizedOffset = (GLfloat)(currentOverlowTextureRead * 2) + 1.5;
|
||||
|
||||
[shaderString appendFormat:@"sum += texture2D(inputImageTexture, blurCoordinates[0] + singleStepOffset * %f) * %f;\n", optimizedOffset, boxWeight * 2.0];
|
||||
[shaderString appendFormat:@"sum += texture2D(inputImageTexture, blurCoordinates[0] - singleStepOffset * %f) * %f;\n", optimizedOffset, boxWeight * 2.0];
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
[shaderString appendString:@"\
|
||||
gl_FragColor = sum;\n\
|
||||
}\n"];
|
||||
|
||||
return shaderString;
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
[super setupFilterForSize:filterFrameSize];
|
||||
|
||||
if (shouldResizeBlurRadiusWithImageSize == YES)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
// NSString *currentGaussianBlurVertexShader = [GPUImageGaussianBlurFilter vertexShaderForStandardGaussianOfRadius:4 sigma:2.0];
|
||||
// NSString *currentGaussianBlurFragmentShader = [GPUImageGaussianBlurFilter fragmentShaderForStandardGaussianOfRadius:4 sigma:2.0];
|
||||
|
||||
NSString *currentBoxBlurVertexShader = [[self class] vertexShaderForOptimizedBlurOfRadius:4 sigma:0.0];
|
||||
NSString *currentBoxBlurFragmentShader = [[self class] fragmentShaderForOptimizedBlurOfRadius:4 sigma:0.0];
|
||||
|
||||
if (!(self = [super initWithFirstStageVertexShaderFromString:currentBoxBlurVertexShader firstStageFragmentShaderFromString:currentBoxBlurFragmentShader secondStageVertexShaderFromString:currentBoxBlurVertexShader secondStageFragmentShaderFromString:currentBoxBlurFragmentShader]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
_blurRadiusInPixels = 4.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBlurRadiusInPixels:(CGFloat)newValue;
|
||||
{
|
||||
CGFloat newBlurRadius = round(round(newValue / 2.0) * 2.0); // For now, only do even radii
|
||||
|
||||
if (newBlurRadius != _blurRadiusInPixels)
|
||||
{
|
||||
_blurRadiusInPixels = newBlurRadius;
|
||||
|
||||
NSString *newGaussianBlurVertexShader = [[self class] vertexShaderForOptimizedBlurOfRadius:_blurRadiusInPixels sigma:0.0];
|
||||
NSString *newGaussianBlurFragmentShader = [[self class] fragmentShaderForOptimizedBlurOfRadius:_blurRadiusInPixels sigma:0.0];
|
||||
|
||||
// NSLog(@"Optimized vertex shader: \n%@", newGaussianBlurVertexShader);
|
||||
// NSLog(@"Optimized fragment shader: \n%@", newGaussianBlurFragmentShader);
|
||||
//
|
||||
[self switchToVertexShader:newGaussianBlurVertexShader fragmentShader:newGaussianBlurFragmentShader];
|
||||
}
|
||||
shouldResizeBlurRadiusWithImageSize = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageBrightnessFilter : GPUImageFilter
|
||||
{
|
||||
GLint brightnessUniform;
|
||||
}
|
||||
|
||||
// Brightness ranges from -1.0 to 1.0, with 0.0 as the normal level
|
||||
@property(readwrite, nonatomic) CGFloat brightness;
|
||||
|
||||
@end
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#import "GPUImageBrightnessFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageBrightnessFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform lowp float brightness;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((textureColor.rgb + vec3(brightness)), textureColor.w);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageBrightnessFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform float brightness;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((textureColor.rgb + vec3(brightness)), textureColor.w);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageBrightnessFilter
|
||||
|
||||
@synthesize brightness = _brightness;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageBrightnessFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
brightnessUniform = [filterProgram uniformIndex:@"brightness"];
|
||||
self.brightness = 0.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBrightness:(CGFloat)newValue;
|
||||
{
|
||||
_brightness = newValue;
|
||||
|
||||
[self setFloat:_brightness forUniform:brightnessUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageBuffer : GPUImageFilter
|
||||
{
|
||||
NSMutableArray *bufferedFramebuffers;
|
||||
}
|
||||
|
||||
@property(readwrite, nonatomic) NSUInteger bufferSize;
|
||||
|
||||
@end
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
#import "GPUImageBuffer.h"
|
||||
|
||||
@interface GPUImageBuffer()
|
||||
|
||||
@end
|
||||
|
||||
@implementation GPUImageBuffer
|
||||
|
||||
@synthesize bufferSize = _bufferSize;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithFragmentShaderFromString:kGPUImagePassthroughFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
bufferedFramebuffers = [[NSMutableArray alloc] init];
|
||||
// [bufferedTextures addObject:[NSNumber numberWithInt:outputTexture]];
|
||||
_bufferSize = 1;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
for (GPUImageFramebuffer *currentFramebuffer in bufferedFramebuffers)
|
||||
{
|
||||
[currentFramebuffer unlock];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark GPUImageInput
|
||||
|
||||
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
if ([bufferedFramebuffers count] >= _bufferSize)
|
||||
{
|
||||
outputFramebuffer = [bufferedFramebuffers objectAtIndex:0];
|
||||
[bufferedFramebuffers removeObjectAtIndex:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing yet in the buffer, so don't process further until the buffer is full
|
||||
outputFramebuffer = firstInputFramebuffer;
|
||||
[firstInputFramebuffer lock];
|
||||
}
|
||||
|
||||
[bufferedFramebuffers addObject:firstInputFramebuffer];
|
||||
|
||||
// Need to pass along rotation information, as we're just holding on to buffered framebuffers and not rotating them ourselves
|
||||
for (id<GPUImageInput> currentTarget in targets)
|
||||
{
|
||||
if (currentTarget != self.targetToIgnoreForUpdates)
|
||||
{
|
||||
NSInteger indexOfObject = [targets indexOfObject:currentTarget];
|
||||
NSInteger textureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
|
||||
|
||||
[currentTarget setInputRotation:inputRotation atIndex:textureIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// Let the downstream video elements see the previous frame from the buffer before rendering a new one into place
|
||||
[self informTargetsAboutNewFrameAtTime:frameTime];
|
||||
|
||||
// [self renderToTextureWithVertices:imageVertices textureCoordinates:[[self class] textureCoordinatesForRotation:inputRotation]];
|
||||
}
|
||||
|
||||
- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
|
||||
{
|
||||
// No need to render to another texture anymore, since we'll be hanging on to the textures in our buffer
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBufferSize:(NSUInteger)newValue;
|
||||
{
|
||||
if ( (newValue == _bufferSize) || (newValue < 1) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (newValue > _bufferSize)
|
||||
{
|
||||
NSUInteger texturesToAdd = newValue - _bufferSize;
|
||||
for (NSUInteger currentTextureIndex = 0; currentTextureIndex < texturesToAdd; currentTextureIndex++)
|
||||
{
|
||||
// TODO: Deal with the growth of the size of the buffer by rotating framebuffers, no textures
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSUInteger texturesToRemove = _bufferSize - newValue;
|
||||
for (NSUInteger currentTextureIndex = 0; currentTextureIndex < texturesToRemove; currentTextureIndex++)
|
||||
{
|
||||
GPUImageFramebuffer *lastFramebuffer = [bufferedFramebuffers lastObject];
|
||||
[bufferedFramebuffers removeObjectAtIndex:([bufferedFramebuffers count] - 1)];
|
||||
|
||||
[lastFramebuffer unlock];
|
||||
lastFramebuffer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
_bufferSize = newValue;
|
||||
}
|
||||
|
||||
@end
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
/// Creates a bulge distortion on the image
|
||||
@interface GPUImageBulgeDistortionFilter : GPUImageFilter
|
||||
{
|
||||
GLint aspectRatioUniform, radiusUniform, centerUniform, scaleUniform;
|
||||
}
|
||||
|
||||
/// The center about which to apply the distortion, with a default of (0.5, 0.5)
|
||||
@property(readwrite, nonatomic) CGPoint center;
|
||||
/// The radius of the distortion, ranging from 0.0 to 1.0, with a default of 0.25
|
||||
@property(readwrite, nonatomic) CGFloat radius;
|
||||
/// The amount of distortion to apply, from -1.0 to 1.0, with a default of 0.5
|
||||
@property(readwrite, nonatomic) CGFloat scale;
|
||||
|
||||
@end
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
#import "GPUImageBulgeDistortionFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageBulgeDistortionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform highp float aspectRatio;
|
||||
uniform highp vec2 center;
|
||||
uniform highp float radius;
|
||||
uniform highp float scale;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec2 textureCoordinateToUse = vec2(textureCoordinate.x, ((textureCoordinate.y - center.y) * aspectRatio) + center.y);
|
||||
highp float dist = distance(center, textureCoordinateToUse);
|
||||
textureCoordinateToUse = textureCoordinate;
|
||||
|
||||
if (dist < radius)
|
||||
{
|
||||
textureCoordinateToUse -= center;
|
||||
highp float percent = 1.0 - ((radius - dist) / radius) * scale;
|
||||
percent = percent * percent;
|
||||
|
||||
textureCoordinateToUse = textureCoordinateToUse * percent;
|
||||
textureCoordinateToUse += center;
|
||||
}
|
||||
|
||||
gl_FragColor = texture2D(inputImageTexture, textureCoordinateToUse );
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageBulgeDistortionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform float aspectRatio;
|
||||
uniform vec2 center;
|
||||
uniform float radius;
|
||||
uniform float scale;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 textureCoordinateToUse = vec2(textureCoordinate.x, ((textureCoordinate.y - center.y) * aspectRatio) + center.y);
|
||||
float dist = distance(center, textureCoordinateToUse);
|
||||
textureCoordinateToUse = textureCoordinate;
|
||||
|
||||
if (dist < radius)
|
||||
{
|
||||
textureCoordinateToUse -= center;
|
||||
float percent = 1.0 - ((radius - dist) / radius) * scale;
|
||||
percent = percent * percent;
|
||||
|
||||
textureCoordinateToUse = textureCoordinateToUse * percent;
|
||||
textureCoordinateToUse += center;
|
||||
}
|
||||
|
||||
gl_FragColor = texture2D(inputImageTexture, textureCoordinateToUse );
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
@interface GPUImageBulgeDistortionFilter ()
|
||||
|
||||
- (void)adjustAspectRatio;
|
||||
|
||||
@property (readwrite, nonatomic) CGFloat aspectRatio;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GPUImageBulgeDistortionFilter
|
||||
|
||||
@synthesize aspectRatio = _aspectRatio;
|
||||
@synthesize center = _center;
|
||||
@synthesize radius = _radius;
|
||||
@synthesize scale = _scale;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageBulgeDistortionFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
aspectRatioUniform = [filterProgram uniformIndex:@"aspectRatio"];
|
||||
radiusUniform = [filterProgram uniformIndex:@"radius"];
|
||||
scaleUniform = [filterProgram uniformIndex:@"scale"];
|
||||
centerUniform = [filterProgram uniformIndex:@"center"];
|
||||
|
||||
self.radius = 0.25;
|
||||
self.scale = 0.5;
|
||||
self.center = CGPointMake(0.5, 0.5);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)adjustAspectRatio;
|
||||
{
|
||||
if (GPUImageRotationSwapsWidthAndHeight(inputRotation))
|
||||
{
|
||||
[self setAspectRatio:(inputTextureSize.width / inputTextureSize.height)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setAspectRatio:(inputTextureSize.height / inputTextureSize.width)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forceProcessingAtSize:(CGSize)frameSize;
|
||||
{
|
||||
[super forceProcessingAtSize:frameSize];
|
||||
[self adjustAspectRatio];
|
||||
}
|
||||
|
||||
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
CGSize oldInputSize = inputTextureSize;
|
||||
[super setInputSize:newSize atIndex:textureIndex];
|
||||
|
||||
if ( (!CGSizeEqualToSize(oldInputSize, inputTextureSize)) && (!CGSizeEqualToSize(newSize, CGSizeZero)) )
|
||||
{
|
||||
[self adjustAspectRatio];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAspectRatio:(CGFloat)newValue;
|
||||
{
|
||||
_aspectRatio = newValue;
|
||||
|
||||
[self setFloat:_aspectRatio forUniform:aspectRatioUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
[super setInputRotation:newInputRotation atIndex:textureIndex];
|
||||
[self setCenter:self.center];
|
||||
[self adjustAspectRatio];
|
||||
}
|
||||
|
||||
- (void)setRadius:(CGFloat)newValue;
|
||||
{
|
||||
_radius = newValue;
|
||||
|
||||
[self setFloat:_radius forUniform:radiusUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setScale:(CGFloat)newValue;
|
||||
{
|
||||
_scale = newValue;
|
||||
|
||||
[self setFloat:_scale forUniform:scaleUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setCenter:(CGPoint)newValue;
|
||||
{
|
||||
_center = newValue;
|
||||
|
||||
CGPoint rotatedPoint = [self rotatedPoint:_center forRotation:inputRotation];
|
||||
|
||||
[self setPoint:rotatedPoint forUniform:centerUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageCGAColorspaceFilter : GPUImageFilter
|
||||
|
||||
@end
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// GPUImageCGAColorspaceFilter.m
|
||||
//
|
||||
|
||||
#import "GPUImageCGAColorspaceFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageCGAColorspaceFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec2 sampleDivisor = vec2(1.0 / 200.0, 1.0 / 320.0);
|
||||
//highp vec4 colorDivisor = vec4(colorDepth);
|
||||
|
||||
highp vec2 samplePos = textureCoordinate - mod(textureCoordinate, sampleDivisor);
|
||||
highp vec4 color = texture2D(inputImageTexture, samplePos );
|
||||
|
||||
//gl_FragColor = texture2D(inputImageTexture, samplePos );
|
||||
mediump vec4 colorCyan = vec4(85.0 / 255.0, 1.0, 1.0, 1.0);
|
||||
mediump vec4 colorMagenta = vec4(1.0, 85.0 / 255.0, 1.0, 1.0);
|
||||
mediump vec4 colorWhite = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
mediump vec4 colorBlack = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
mediump vec4 endColor;
|
||||
highp float blackDistance = distance(color, colorBlack);
|
||||
highp float whiteDistance = distance(color, colorWhite);
|
||||
highp float magentaDistance = distance(color, colorMagenta);
|
||||
highp float cyanDistance = distance(color, colorCyan);
|
||||
|
||||
mediump vec4 finalColor;
|
||||
|
||||
highp float colorDistance = min(magentaDistance, cyanDistance);
|
||||
colorDistance = min(colorDistance, whiteDistance);
|
||||
colorDistance = min(colorDistance, blackDistance);
|
||||
|
||||
if (colorDistance == blackDistance) {
|
||||
finalColor = colorBlack;
|
||||
} else if (colorDistance == whiteDistance) {
|
||||
finalColor = colorWhite;
|
||||
} else if (colorDistance == cyanDistance) {
|
||||
finalColor = colorCyan;
|
||||
} else {
|
||||
finalColor = colorMagenta;
|
||||
}
|
||||
|
||||
gl_FragColor = finalColor;
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageCGAColorspaceFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 sampleDivisor = vec2(1.0 / 200.0, 1.0 / 320.0);
|
||||
//highp vec4 colorDivisor = vec4(colorDepth);
|
||||
|
||||
vec2 samplePos = textureCoordinate - mod(textureCoordinate, sampleDivisor);
|
||||
vec4 color = texture2D(inputImageTexture, samplePos );
|
||||
|
||||
//gl_FragColor = texture2D(inputImageTexture, samplePos );
|
||||
vec4 colorCyan = vec4(85.0 / 255.0, 1.0, 1.0, 1.0);
|
||||
vec4 colorMagenta = vec4(1.0, 85.0 / 255.0, 1.0, 1.0);
|
||||
vec4 colorWhite = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
vec4 colorBlack = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
vec4 endColor;
|
||||
float blackDistance = distance(color, colorBlack);
|
||||
float whiteDistance = distance(color, colorWhite);
|
||||
float magentaDistance = distance(color, colorMagenta);
|
||||
float cyanDistance = distance(color, colorCyan);
|
||||
|
||||
vec4 finalColor;
|
||||
|
||||
float colorDistance = min(magentaDistance, cyanDistance);
|
||||
colorDistance = min(colorDistance, whiteDistance);
|
||||
colorDistance = min(colorDistance, blackDistance);
|
||||
|
||||
if (colorDistance == blackDistance) {
|
||||
finalColor = colorBlack;
|
||||
} else if (colorDistance == whiteDistance) {
|
||||
finalColor = colorWhite;
|
||||
} else if (colorDistance == cyanDistance) {
|
||||
finalColor = colorCyan;
|
||||
} else {
|
||||
finalColor = colorMagenta;
|
||||
}
|
||||
|
||||
gl_FragColor = finalColor;
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageCGAColorspaceFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageCGAColorspaceFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@class GPUImageGrayscaleFilter;
|
||||
@class GPUImageSingleComponentGaussianBlurFilter;
|
||||
@class GPUImageDirectionalSobelEdgeDetectionFilter;
|
||||
@class GPUImageDirectionalNonMaximumSuppressionFilter;
|
||||
@class GPUImageWeakPixelInclusionFilter;
|
||||
|
||||
/** This applies the edge detection process described by John Canny in
|
||||
|
||||
Canny, J., A Computational Approach To Edge Detection, IEEE Trans. Pattern Analysis and Machine Intelligence, 8(6):679–698, 1986.
|
||||
|
||||
and implemented in OpenGL ES by
|
||||
|
||||
A. Ensor, S. Hall. GPU-based Image Analysis on Mobile Devices. Proceedings of Image and Vision Computing New Zealand 2011.
|
||||
|
||||
It starts with a conversion to luminance, followed by an accelerated 9-hit Gaussian blur. A Sobel operator is applied to obtain the overall
|
||||
gradient strength in the blurred image, as well as the direction (in texture sampling steps) of the gradient. A non-maximum suppression filter
|
||||
acts along the direction of the gradient, highlighting strong edges that pass the threshold and completely removing those that fail the lower
|
||||
threshold. Finally, pixels from in-between these thresholds are either included in edges or rejected based on neighboring pixels.
|
||||
*/
|
||||
@interface GPUImageCannyEdgeDetectionFilter : GPUImageFilterGroup
|
||||
{
|
||||
GPUImageGrayscaleFilter *luminanceFilter;
|
||||
GPUImageSingleComponentGaussianBlurFilter *blurFilter;
|
||||
GPUImageDirectionalSobelEdgeDetectionFilter *edgeDetectionFilter;
|
||||
GPUImageDirectionalNonMaximumSuppressionFilter *nonMaximumSuppressionFilter;
|
||||
GPUImageWeakPixelInclusionFilter *weakPixelInclusionFilter;
|
||||
}
|
||||
|
||||
/** The image width and height factors tweak the appearance of the edges.
|
||||
|
||||
These parameters affect the visibility of the detected edges
|
||||
|
||||
By default, they match the inverse of the filter size in pixels
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat texelWidth;
|
||||
/** The image width and height factors tweak the appearance of the edges.
|
||||
|
||||
These parameters affect the visibility of the detected edges
|
||||
|
||||
By default, they match the inverse of the filter size in pixels
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat texelHeight;
|
||||
|
||||
/** The underlying blur radius for the Gaussian blur. Default is 2.0.
|
||||
*/
|
||||
@property (readwrite, nonatomic) CGFloat blurRadiusInPixels;
|
||||
|
||||
/** The underlying blur texel spacing multiplier. Default is 1.0.
|
||||
*/
|
||||
@property (readwrite, nonatomic) CGFloat blurTexelSpacingMultiplier;
|
||||
|
||||
/** Any edge with a gradient magnitude above this threshold will pass and show up in the final result.
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat upperThreshold;
|
||||
|
||||
/** Any edge with a gradient magnitude below this threshold will fail and be removed from the final result.
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat lowerThreshold;
|
||||
|
||||
@end
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
#import "GPUImageCannyEdgeDetectionFilter.h"
|
||||
|
||||
#import "GPUImageGrayscaleFilter.h"
|
||||
#import "GPUImageDirectionalSobelEdgeDetectionFilter.h"
|
||||
#import "GPUImageDirectionalNonMaximumSuppressionFilter.h"
|
||||
#import "GPUImageWeakPixelInclusionFilter.h"
|
||||
#import "GPUImageSingleComponentGaussianBlurFilter.h"
|
||||
|
||||
@implementation GPUImageCannyEdgeDetectionFilter
|
||||
|
||||
@synthesize upperThreshold;
|
||||
@synthesize lowerThreshold;
|
||||
@synthesize blurRadiusInPixels;
|
||||
@synthesize blurTexelSpacingMultiplier;
|
||||
@synthesize texelWidth;
|
||||
@synthesize texelHeight;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// First pass: convert image to luminance
|
||||
luminanceFilter = [[GPUImageGrayscaleFilter alloc] init];
|
||||
[self addFilter:luminanceFilter];
|
||||
|
||||
// Second pass: apply a variable Gaussian blur
|
||||
blurFilter = [[GPUImageSingleComponentGaussianBlurFilter alloc] init];
|
||||
[self addFilter:blurFilter];
|
||||
|
||||
// Third pass: run the Sobel edge detection, with calculated gradient directions, on this blurred image
|
||||
edgeDetectionFilter = [[GPUImageDirectionalSobelEdgeDetectionFilter alloc] init];
|
||||
[self addFilter:edgeDetectionFilter];
|
||||
|
||||
// Fourth pass: apply non-maximum suppression
|
||||
nonMaximumSuppressionFilter = [[GPUImageDirectionalNonMaximumSuppressionFilter alloc] init];
|
||||
[self addFilter:nonMaximumSuppressionFilter];
|
||||
|
||||
// Fifth pass: include weak pixels to complete edges
|
||||
weakPixelInclusionFilter = [[GPUImageWeakPixelInclusionFilter alloc] init];
|
||||
[self addFilter:weakPixelInclusionFilter];
|
||||
|
||||
[luminanceFilter addTarget:blurFilter];
|
||||
[blurFilter addTarget:edgeDetectionFilter];
|
||||
[edgeDetectionFilter addTarget:nonMaximumSuppressionFilter];
|
||||
[nonMaximumSuppressionFilter addTarget:weakPixelInclusionFilter];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObject:luminanceFilter];
|
||||
// self.terminalFilter = nonMaximumSuppressionFilter;
|
||||
self.terminalFilter = weakPixelInclusionFilter;
|
||||
|
||||
self.blurRadiusInPixels = 2.0;
|
||||
self.blurTexelSpacingMultiplier = 1.0;
|
||||
self.upperThreshold = 0.4;
|
||||
self.lowerThreshold = 0.1;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBlurRadiusInPixels:(CGFloat)newValue;
|
||||
{
|
||||
blurFilter.blurRadiusInPixels = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)blurRadiusInPixels;
|
||||
{
|
||||
return blurFilter.blurRadiusInPixels;
|
||||
}
|
||||
|
||||
- (void)setBlurTexelSpacingMultiplier:(CGFloat)newValue;
|
||||
{
|
||||
blurFilter.texelSpacingMultiplier = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)blurTexelSpacingMultiplier;
|
||||
{
|
||||
return blurFilter.texelSpacingMultiplier;
|
||||
}
|
||||
|
||||
- (void)setTexelWidth:(CGFloat)newValue;
|
||||
{
|
||||
edgeDetectionFilter.texelWidth = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)texelWidth;
|
||||
{
|
||||
return edgeDetectionFilter.texelWidth;
|
||||
}
|
||||
|
||||
- (void)setTexelHeight:(CGFloat)newValue;
|
||||
{
|
||||
edgeDetectionFilter.texelHeight = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)texelHeight;
|
||||
{
|
||||
return edgeDetectionFilter.texelHeight;
|
||||
}
|
||||
|
||||
- (void)setUpperThreshold:(CGFloat)newValue;
|
||||
{
|
||||
nonMaximumSuppressionFilter.upperThreshold = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)upperThreshold;
|
||||
{
|
||||
return nonMaximumSuppressionFilter.upperThreshold;
|
||||
}
|
||||
|
||||
- (void)setLowerThreshold:(CGFloat)newValue;
|
||||
{
|
||||
nonMaximumSuppressionFilter.lowerThreshold = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)lowerThreshold;
|
||||
{
|
||||
return nonMaximumSuppressionFilter.lowerThreshold;
|
||||
}
|
||||
|
||||
@end
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
/** Selectively replaces a color in the first image with the second image
|
||||
*/
|
||||
@interface GPUImageChromaKeyBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
GLint colorToReplaceUniform, thresholdSensitivityUniform, smoothingUniform;
|
||||
}
|
||||
|
||||
/** The threshold sensitivity controls how similar pixels need to be colored to be replaced
|
||||
|
||||
The default value is 0.3
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat thresholdSensitivity;
|
||||
|
||||
/** The degree of smoothing controls how gradually similar colors are replaced in the image
|
||||
|
||||
The default value is 0.1
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat smoothing;
|
||||
|
||||
/** The color to be replaced is specified using individual red, green, and blue components (normalized to 1.0).
|
||||
|
||||
The default is green: (0.0, 1.0, 0.0).
|
||||
|
||||
@param redComponent Red component of color to be replaced
|
||||
@param greenComponent Green component of color to be replaced
|
||||
@param blueComponent Blue component of color to be replaced
|
||||
*/
|
||||
- (void)setColorToReplaceRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
|
||||
@end
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
#import "GPUImageChromaKeyBlendFilter.h"
|
||||
|
||||
// Shader code based on Apple's CIChromaKeyFilter example: https://developer.apple.com/library/mac/#samplecode/CIChromaKeyFilter/Introduction/Intro.html
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageChromaKeyBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform float thresholdSensitivity;
|
||||
uniform float smoothing;
|
||||
uniform vec3 colorToReplace;
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;
|
||||
float maskCr = 0.7132 * (colorToReplace.r - maskY);
|
||||
float maskCb = 0.5647 * (colorToReplace.b - maskY);
|
||||
|
||||
float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
|
||||
float Cr = 0.7132 * (textureColor.r - Y);
|
||||
float Cb = 0.5647 * (textureColor.b - Y);
|
||||
|
||||
// float blendValue = 1.0 - smoothstep(thresholdSensitivity - smoothing, thresholdSensitivity , abs(Cr - maskCr) + abs(Cb - maskCb));
|
||||
float blendValue = 1.0 - smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
|
||||
gl_FragColor = mix(textureColor, textureColor2, blendValue);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageChromaKeyBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform float thresholdSensitivity;
|
||||
uniform float smoothing;
|
||||
uniform vec3 colorToReplace;
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;
|
||||
float maskCr = 0.7132 * (colorToReplace.r - maskY);
|
||||
float maskCb = 0.5647 * (colorToReplace.b - maskY);
|
||||
|
||||
float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
|
||||
float Cr = 0.7132 * (textureColor.r - Y);
|
||||
float Cb = 0.5647 * (textureColor.b - Y);
|
||||
|
||||
// float blendValue = 1.0 - smoothstep(thresholdSensitivity - smoothing, thresholdSensitivity , abs(Cr - maskCr) + abs(Cb - maskCb));
|
||||
float blendValue = 1.0 - smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
|
||||
gl_FragColor = mix(textureColor, textureColor2, blendValue);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageChromaKeyBlendFilter
|
||||
|
||||
@synthesize thresholdSensitivity = _thresholdSensitivity;
|
||||
@synthesize smoothing = _smoothing;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageChromaKeyBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
thresholdSensitivityUniform = [filterProgram uniformIndex:@"thresholdSensitivity"];
|
||||
smoothingUniform = [filterProgram uniformIndex:@"smoothing"];
|
||||
colorToReplaceUniform = [filterProgram uniformIndex:@"colorToReplace"];
|
||||
|
||||
self.thresholdSensitivity = 0.4;
|
||||
self.smoothing = 0.1;
|
||||
[self setColorToReplaceRed:0.0 green:1.0 blue:0.0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setColorToReplaceRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
{
|
||||
GPUVector3 colorToReplace = {redComponent, greenComponent, blueComponent};
|
||||
|
||||
[self setVec3:colorToReplace forUniform:colorToReplaceUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setThresholdSensitivity:(CGFloat)newValue;
|
||||
{
|
||||
_thresholdSensitivity = newValue;
|
||||
|
||||
[self setFloat:(GLfloat)_thresholdSensitivity forUniform:thresholdSensitivityUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setSmoothing:(CGFloat)newValue;
|
||||
{
|
||||
_smoothing = newValue;
|
||||
|
||||
[self setFloat:(GLfloat)_smoothing forUniform:smoothingUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageChromaKeyFilter : GPUImageFilter
|
||||
{
|
||||
GLint colorToReplaceUniform, thresholdSensitivityUniform, smoothingUniform;
|
||||
}
|
||||
|
||||
/** The threshold sensitivity controls how similar pixels need to be colored to be replaced
|
||||
|
||||
The default value is 0.3
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat thresholdSensitivity;
|
||||
|
||||
/** The degree of smoothing controls how gradually similar colors are replaced in the image
|
||||
|
||||
The default value is 0.1
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat smoothing;
|
||||
|
||||
/** The color to be replaced is specified using individual red, green, and blue components (normalized to 1.0).
|
||||
|
||||
The default is green: (0.0, 1.0, 0.0).
|
||||
|
||||
@param redComponent Red component of color to be replaced
|
||||
@param greenComponent Green component of color to be replaced
|
||||
@param blueComponent Blue component of color to be replaced
|
||||
*/
|
||||
- (void)setColorToReplaceRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
|
||||
@end
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
#import "GPUImageChromaKeyFilter.h"
|
||||
|
||||
// Shader code based on Apple's CIChromaKeyFilter example: https://developer.apple.com/library/mac/#samplecode/CIChromaKeyFilter/Introduction/Intro.html
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageChromaKeyFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform float thresholdSensitivity;
|
||||
uniform float smoothing;
|
||||
uniform vec3 colorToReplace;
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;
|
||||
float maskCr = 0.7132 * (colorToReplace.r - maskY);
|
||||
float maskCb = 0.5647 * (colorToReplace.b - maskY);
|
||||
|
||||
float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
|
||||
float Cr = 0.7132 * (textureColor.r - Y);
|
||||
float Cb = 0.5647 * (textureColor.b - Y);
|
||||
|
||||
// float blendValue = 1.0 - smoothstep(thresholdSensitivity - smoothing, thresholdSensitivity , abs(Cr - maskCr) + abs(Cb - maskCb));
|
||||
float blendValue = smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
|
||||
gl_FragColor = vec4(textureColor.rgb, textureColor.a * blendValue);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageChromaKeyFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform float thresholdSensitivity;
|
||||
uniform float smoothing;
|
||||
uniform vec3 colorToReplace;
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;
|
||||
float maskCr = 0.7132 * (colorToReplace.r - maskY);
|
||||
float maskCb = 0.5647 * (colorToReplace.b - maskY);
|
||||
|
||||
float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
|
||||
float Cr = 0.7132 * (textureColor.r - Y);
|
||||
float Cb = 0.5647 * (textureColor.b - Y);
|
||||
|
||||
// float blendValue = 1.0 - smoothstep(thresholdSensitivity - smoothing, thresholdSensitivity , abs(Cr - maskCr) + abs(Cb - maskCb));
|
||||
float blendValue = smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
|
||||
gl_FragColor = vec4(textureColor.rgb, textureColor.a * blendValue);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageChromaKeyFilter
|
||||
|
||||
@synthesize thresholdSensitivity = _thresholdSensitivity;
|
||||
@synthesize smoothing = _smoothing;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageChromaKeyFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
thresholdSensitivityUniform = [filterProgram uniformIndex:@"thresholdSensitivity"];
|
||||
smoothingUniform = [filterProgram uniformIndex:@"smoothing"];
|
||||
colorToReplaceUniform = [filterProgram uniformIndex:@"colorToReplace"];
|
||||
|
||||
self.thresholdSensitivity = 0.4;
|
||||
self.smoothing = 0.1;
|
||||
[self setColorToReplaceRed:0.0 green:1.0 blue:0.0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setColorToReplaceRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
{
|
||||
GPUVector3 colorToReplace = {redComponent, greenComponent, blueComponent};
|
||||
|
||||
[self setVec3:colorToReplace forUniform:colorToReplaceUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setThresholdSensitivity:(CGFloat)newValue;
|
||||
{
|
||||
_thresholdSensitivity = newValue;
|
||||
|
||||
[self setFloat:(GLfloat)_thresholdSensitivity forUniform:thresholdSensitivityUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setSmoothing:(CGFloat)newValue;
|
||||
{
|
||||
_smoothing = newValue;
|
||||
|
||||
[self setFloat:(GLfloat)_smoothing forUniform:smoothingUniform program:filterProgram];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@class GPUImageErosionFilter;
|
||||
@class GPUImageDilationFilter;
|
||||
|
||||
// A filter that first performs a dilation on the red channel of an image, followed by an erosion of the same radius.
|
||||
// This helps to filter out smaller dark elements.
|
||||
|
||||
@interface GPUImageClosingFilter : GPUImageFilterGroup
|
||||
{
|
||||
GPUImageErosionFilter *erosionFilter;
|
||||
GPUImageDilationFilter *dilationFilter;
|
||||
}
|
||||
|
||||
@property(readwrite, nonatomic) CGFloat verticalTexelSpacing, horizontalTexelSpacing;
|
||||
|
||||
- (id)initWithRadius:(NSUInteger)radius;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,57 @@
|
||||
#import "GPUImageClosingFilter.h"
|
||||
#import "GPUImageErosionFilter.h"
|
||||
#import "GPUImageDilationFilter.h"
|
||||
|
||||
@implementation GPUImageClosingFilter
|
||||
|
||||
@synthesize verticalTexelSpacing = _verticalTexelSpacing;
|
||||
@synthesize horizontalTexelSpacing = _horizontalTexelSpacing;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithRadius:1]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithRadius:(NSUInteger)radius;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// First pass: dilation
|
||||
dilationFilter = [[GPUImageDilationFilter alloc] initWithRadius:radius];
|
||||
[self addFilter:dilationFilter];
|
||||
|
||||
// Second pass: erosion
|
||||
erosionFilter = [[GPUImageErosionFilter alloc] initWithRadius:radius];
|
||||
[self addFilter:erosionFilter];
|
||||
|
||||
[dilationFilter addTarget:erosionFilter];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObjects:dilationFilter, nil];
|
||||
self.terminalFilter = erosionFilter;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setVerticalTexelSpacing:(CGFloat)newValue;
|
||||
{
|
||||
_verticalTexelSpacing = newValue;
|
||||
erosionFilter.verticalTexelSpacing = newValue;
|
||||
dilationFilter.verticalTexelSpacing = newValue;
|
||||
}
|
||||
|
||||
- (void)setHorizontalTexelSpacing:(CGFloat)newValue;
|
||||
{
|
||||
_horizontalTexelSpacing = newValue;
|
||||
erosionFilter.horizontalTexelSpacing = newValue;
|
||||
dilationFilter.horizontalTexelSpacing = newValue;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageColorBlendFilter : GPUImageTwoInputFilter
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,113 @@
|
||||
#import "GPUImageColorBlendFilter.h"
|
||||
|
||||
/**
|
||||
* Color blend mode based upon pseudo code from the PDF specification.
|
||||
*/
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
highp float lum(lowp vec3 c) {
|
||||
return dot(c, vec3(0.3, 0.59, 0.11));
|
||||
}
|
||||
|
||||
lowp vec3 clipcolor(lowp vec3 c) {
|
||||
highp float l = lum(c);
|
||||
lowp float n = min(min(c.r, c.g), c.b);
|
||||
lowp float x = max(max(c.r, c.g), c.b);
|
||||
|
||||
if (n < 0.0) {
|
||||
c.r = l + ((c.r - l) * l) / (l - n);
|
||||
c.g = l + ((c.g - l) * l) / (l - n);
|
||||
c.b = l + ((c.b - l) * l) / (l - n);
|
||||
}
|
||||
if (x > 1.0) {
|
||||
c.r = l + ((c.r - l) * (1.0 - l)) / (x - l);
|
||||
c.g = l + ((c.g - l) * (1.0 - l)) / (x - l);
|
||||
c.b = l + ((c.b - l) * (1.0 - l)) / (x - l);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
lowp vec3 setlum(lowp vec3 c, highp float l) {
|
||||
highp float d = l - lum(c);
|
||||
c = c + vec3(d);
|
||||
return clipcolor(c);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec4 baseColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
highp vec4 overlayColor = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(baseColor.rgb * (1.0 - overlayColor.a) + setlum(overlayColor.rgb, lum(baseColor.rgb)) * overlayColor.a, baseColor.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
float lum(vec3 c) {
|
||||
return dot(c, vec3(0.3, 0.59, 0.11));
|
||||
}
|
||||
|
||||
vec3 clipcolor(vec3 c) {
|
||||
float l = lum(c);
|
||||
float n = min(min(c.r, c.g), c.b);
|
||||
float x = max(max(c.r, c.g), c.b);
|
||||
|
||||
if (n < 0.0) {
|
||||
c.r = l + ((c.r - l) * l) / (l - n);
|
||||
c.g = l + ((c.g - l) * l) / (l - n);
|
||||
c.b = l + ((c.b - l) * l) / (l - n);
|
||||
}
|
||||
if (x > 1.0) {
|
||||
c.r = l + ((c.r - l) * (1.0 - l)) / (x - l);
|
||||
c.g = l + ((c.g - l) * (1.0 - l)) / (x - l);
|
||||
c.b = l + ((c.b - l) * (1.0 - l)) / (x - l);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
vec3 setlum(vec3 c, float l) {
|
||||
float d = l - lum(c);
|
||||
c = c + vec3(d);
|
||||
return clipcolor(c);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 baseColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlayColor = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(baseColor.rgb * (1.0 - overlayColor.a) + setlum(overlayColor.rgb, lum(baseColor.rgb)) * overlayColor.a, baseColor.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
@implementation GPUImageColorBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageColorBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
/** Applies a color burn blend of two images
|
||||
*/
|
||||
@interface GPUImageColorBurnBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
#import "GPUImageColorBurnBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorBurnBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
mediump vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
mediump vec4 whiteColor = vec4(1.0);
|
||||
gl_FragColor = whiteColor - (whiteColor - textureColor) / textureColor2;
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorBurnBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
vec4 whiteColor = vec4(1.0);
|
||||
gl_FragColor = whiteColor - (whiteColor - textureColor) / textureColor2;
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageColorBurnBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageColorBurnBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef GPUImageColorConversion_h
|
||||
#define GPUImageColorConversion_h
|
||||
|
||||
extern GLfloat *kColorConversion601;
|
||||
extern GLfloat *kColorConversion601FullRange;
|
||||
extern GLfloat *kColorConversion709;
|
||||
extern NSString *const kGPUImageYUVVideoRangeConversionForRGFragmentShaderString;
|
||||
extern NSString *const kGPUImageYUVFullRangeConversionForLAFragmentShaderString;
|
||||
extern NSString *const kGPUImageYUVVideoRangeConversionForLAFragmentShaderString;
|
||||
|
||||
|
||||
#endif /* GPUImageColorConversion_h */
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
// Color Conversion Constants (YUV to RGB) including adjustment from 16-235/16-240 (video range)
|
||||
|
||||
// BT.601, which is the standard for SDTV.
|
||||
GLfloat kColorConversion601Default[] = {
|
||||
1.164, 1.164, 1.164,
|
||||
0.0, -0.392, 2.017,
|
||||
1.596, -0.813, 0.0,
|
||||
};
|
||||
|
||||
// BT.601 full range (ref: http://www.equasys.de/colorconversion.html)
|
||||
GLfloat kColorConversion601FullRangeDefault[] = {
|
||||
1.0, 1.0, 1.0,
|
||||
0.0, -0.343, 1.765,
|
||||
1.4, -0.711, 0.0,
|
||||
};
|
||||
|
||||
// BT.709, which is the standard for HDTV.
|
||||
GLfloat kColorConversion709Default[] = {
|
||||
1.164, 1.164, 1.164,
|
||||
0.0, -0.213, 2.112,
|
||||
1.793, -0.533, 0.0,
|
||||
};
|
||||
|
||||
|
||||
GLfloat *kColorConversion601 = kColorConversion601Default;
|
||||
GLfloat *kColorConversion601FullRange = kColorConversion601FullRangeDefault;
|
||||
GLfloat *kColorConversion709 = kColorConversion709Default;
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageYUVVideoRangeConversionForRGFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D luminanceTexture;
|
||||
uniform sampler2D chrominanceTexture;
|
||||
uniform mediump mat3 colorConversionMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec3 yuv;
|
||||
lowp vec3 rgb;
|
||||
|
||||
yuv.x = texture2D(luminanceTexture, textureCoordinate).r;
|
||||
yuv.yz = texture2D(chrominanceTexture, textureCoordinate).rg - vec2(0.5, 0.5);
|
||||
rgb = colorConversionMatrix * yuv;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageYUVVideoRangeConversionForRGFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D luminanceTexture;
|
||||
uniform sampler2D chrominanceTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 yuv;
|
||||
vec3 rgb;
|
||||
|
||||
yuv.x = texture2D(luminanceTexture, textureCoordinate).r;
|
||||
yuv.yz = texture2D(chrominanceTexture, textureCoordinate).rg - vec2(0.5, 0.5);
|
||||
|
||||
// BT.601, which is the standard for SDTV is provided as a reference
|
||||
/*
|
||||
rgb = mat3( 1, 1, 1,
|
||||
0, -.39465, 2.03211,
|
||||
1.13983, -.58060, 0) * yuv;
|
||||
*/
|
||||
|
||||
// Using BT.709 which is the standard for HDTV
|
||||
rgb = mat3( 1, 1, 1,
|
||||
0, -.21482, 2.12798,
|
||||
1.28033, -.38059, 0) * yuv;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
NSString *const kGPUImageYUVFullRangeConversionForLAFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D luminanceTexture;
|
||||
uniform sampler2D chrominanceTexture;
|
||||
uniform mediump mat3 colorConversionMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec3 yuv;
|
||||
lowp vec3 rgb;
|
||||
|
||||
yuv.x = texture2D(luminanceTexture, textureCoordinate).r;
|
||||
yuv.yz = texture2D(chrominanceTexture, textureCoordinate).ra - vec2(0.5, 0.5);
|
||||
rgb = colorConversionMatrix * yuv;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1);
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageYUVVideoRangeConversionForLAFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D luminanceTexture;
|
||||
uniform sampler2D chrominanceTexture;
|
||||
uniform mediump mat3 colorConversionMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec3 yuv;
|
||||
lowp vec3 rgb;
|
||||
|
||||
yuv.x = texture2D(luminanceTexture, textureCoordinate).r - (16.0/255.0);
|
||||
yuv.yz = texture2D(chrominanceTexture, textureCoordinate).ra - vec2(0.5, 0.5);
|
||||
rgb = colorConversionMatrix * yuv;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageYUVVideoRangeConversionForLAFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D luminanceTexture;
|
||||
uniform sampler2D chrominanceTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 yuv;
|
||||
vec3 rgb;
|
||||
|
||||
yuv.x = texture2D(luminanceTexture, textureCoordinate).r;
|
||||
yuv.yz = texture2D(chrominanceTexture, textureCoordinate).ra - vec2(0.5, 0.5);
|
||||
|
||||
// BT.601, which is the standard for SDTV is provided as a reference
|
||||
/*
|
||||
rgb = mat3( 1, 1, 1,
|
||||
0, -.39465, 2.03211,
|
||||
1.13983, -.58060, 0) * yuv;
|
||||
*/
|
||||
|
||||
// Using BT.709 which is the standard for HDTV
|
||||
rgb = mat3( 1, 1, 1,
|
||||
0, -.21482, 2.12798,
|
||||
1.28033, -.38059, 0) * yuv;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
/** Applies a color dodge blend of two images
|
||||
*/
|
||||
@interface GPUImageColorDodgeBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
#import "GPUImageColorDodgeBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorDodgeBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
|
||||
precision mediump float;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
vec3 baseOverlayAlphaProduct = vec3(overlay.a * base.a);
|
||||
vec3 rightHandProduct = overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a);
|
||||
|
||||
vec3 firstBlendColor = baseOverlayAlphaProduct + rightHandProduct;
|
||||
vec3 overlayRGB = clamp((overlay.rgb / clamp(overlay.a, 0.01, 1.0)) * step(0.0, overlay.a), 0.0, 0.99);
|
||||
|
||||
vec3 secondBlendColor = (base.rgb * overlay.a) / (1.0 - overlayRGB) + rightHandProduct;
|
||||
|
||||
vec3 colorChoice = step((overlay.rgb * base.a + base.rgb * overlay.a), baseOverlayAlphaProduct);
|
||||
|
||||
gl_FragColor = vec4(mix(firstBlendColor, secondBlendColor, colorChoice), 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorDodgeBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
vec3 baseOverlayAlphaProduct = vec3(overlay.a * base.a);
|
||||
vec3 rightHandProduct = overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a);
|
||||
|
||||
vec3 firstBlendColor = baseOverlayAlphaProduct + rightHandProduct;
|
||||
vec3 overlayRGB = clamp((overlay.rgb / clamp(overlay.a, 0.01, 1.0)) * step(0.0, overlay.a), 0.0, 0.99);
|
||||
|
||||
vec3 secondBlendColor = (base.rgb * overlay.a) / (1.0 - overlayRGB) + rightHandProduct;
|
||||
|
||||
vec3 colorChoice = step((overlay.rgb * base.a + base.rgb * overlay.a), baseOverlayAlphaProduct);
|
||||
|
||||
gl_FragColor = vec4(mix(firstBlendColor, secondBlendColor, colorChoice), 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageColorDodgeBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageColorDodgeBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageColorInvertFilter : GPUImageFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#import "GPUImageColorInvertFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageInvertFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((1.0 - textureColor.rgb), textureColor.w);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageInvertFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((1.0 - textureColor.rgb), textureColor.w);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageColorInvertFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageInvertFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPUImage3x3TextureSamplingFilter.h"
|
||||
|
||||
@interface GPUImageColorLocalBinaryPatternFilter : GPUImage3x3TextureSamplingFilter
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,159 @@
|
||||
#import "GPUImageColorLocalBinaryPatternFilter.h"
|
||||
|
||||
// This is based on "Accelerating image recognition on mobile devices using GPGPU" by Miguel Bordallo Lopez, Henri Nykanen, Jari Hannuksela, Olli Silven and Markku Vehvilainen
|
||||
// http://www.ee.oulu.fi/~jhannuks/publications/SPIE2011a.pdf
|
||||
|
||||
// Right pixel is the most significant bit, traveling clockwise to get to the upper right, which is the least significant
|
||||
// If the external pixel is greater than or equal to the center, set to 1, otherwise 0
|
||||
//
|
||||
// 2 1 0
|
||||
// 3 7
|
||||
// 4 5 6
|
||||
|
||||
// 01101101
|
||||
// 76543210
|
||||
|
||||
@implementation GPUImageColorLocalBinaryPatternFilter
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorLocalBinaryPatternFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec3 centerColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
lowp vec3 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).rgb;
|
||||
lowp vec3 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).rgb;
|
||||
lowp vec3 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).rgb;
|
||||
lowp vec3 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).rgb;
|
||||
lowp vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
|
||||
lowp vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
|
||||
lowp vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
|
||||
lowp vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
|
||||
|
||||
lowp float redByteTally = 1.0 / 255.0 * step(centerColor.r, topRightColor.r);
|
||||
redByteTally += 2.0 / 255.0 * step(centerColor.r, topColor.r);
|
||||
redByteTally += 4.0 / 255.0 * step(centerColor.r, topLeftColor.r);
|
||||
redByteTally += 8.0 / 255.0 * step(centerColor.r, leftColor.r);
|
||||
redByteTally += 16.0 / 255.0 * step(centerColor.r, bottomLeftColor.r);
|
||||
redByteTally += 32.0 / 255.0 * step(centerColor.r, bottomColor.r);
|
||||
redByteTally += 64.0 / 255.0 * step(centerColor.r, bottomRightColor.r);
|
||||
redByteTally += 128.0 / 255.0 * step(centerColor.r, rightColor.r);
|
||||
|
||||
lowp float blueByteTally = 1.0 / 255.0 * step(centerColor.b, topRightColor.b);
|
||||
blueByteTally += 2.0 / 255.0 * step(centerColor.b, topColor.b);
|
||||
blueByteTally += 4.0 / 255.0 * step(centerColor.b, topLeftColor.b);
|
||||
blueByteTally += 8.0 / 255.0 * step(centerColor.b, leftColor.b);
|
||||
blueByteTally += 16.0 / 255.0 * step(centerColor.b, bottomLeftColor.b);
|
||||
blueByteTally += 32.0 / 255.0 * step(centerColor.b, bottomColor.b);
|
||||
blueByteTally += 64.0 / 255.0 * step(centerColor.b, bottomRightColor.b);
|
||||
blueByteTally += 128.0 / 255.0 * step(centerColor.b, rightColor.b);
|
||||
|
||||
lowp float greenByteTally = 1.0 / 255.0 * step(centerColor.g, topRightColor.g);
|
||||
greenByteTally += 2.0 / 255.0 * step(centerColor.g, topColor.g);
|
||||
greenByteTally += 4.0 / 255.0 * step(centerColor.g, topLeftColor.g);
|
||||
greenByteTally += 8.0 / 255.0 * step(centerColor.g, leftColor.g);
|
||||
greenByteTally += 16.0 / 255.0 * step(centerColor.g, bottomLeftColor.g);
|
||||
greenByteTally += 32.0 / 255.0 * step(centerColor.g, bottomColor.g);
|
||||
greenByteTally += 64.0 / 255.0 * step(centerColor.g, bottomRightColor.g);
|
||||
greenByteTally += 128.0 / 255.0 * step(centerColor.g, rightColor.g);
|
||||
|
||||
// TODO: Replace the above with a dot product and two vec4s
|
||||
// TODO: Apply step to a matrix, rather than individually
|
||||
|
||||
gl_FragColor = vec4(redByteTally, blueByteTally, greenByteTally, 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorLocalBinaryPatternFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 centerColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
vec3 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).rgb;
|
||||
vec3 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).rgb;
|
||||
vec3 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).rgb;
|
||||
vec3 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).rgb;
|
||||
vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
|
||||
vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
|
||||
vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
|
||||
vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
|
||||
|
||||
float redByteTally = 1.0 / 255.0 * step(centerColor.r, topRightColor.r);
|
||||
redByteTally += 2.0 / 255.0 * step(centerColor.r, topColor.r);
|
||||
redByteTally += 4.0 / 255.0 * step(centerColor.r, topLeftColor.r);
|
||||
redByteTally += 8.0 / 255.0 * step(centerColor.r, leftColor.r);
|
||||
redByteTally += 16.0 / 255.0 * step(centerColor.r, bottomLeftColor.r);
|
||||
redByteTally += 32.0 / 255.0 * step(centerColor.r, bottomColor.r);
|
||||
redByteTally += 64.0 / 255.0 * step(centerColor.r, bottomRightColor.r);
|
||||
redByteTally += 128.0 / 255.0 * step(centerColor.r, rightColor.r);
|
||||
|
||||
float blueByteTally = 1.0 / 255.0 * step(centerColor.b, topRightColor.b);
|
||||
blueByteTally += 2.0 / 255.0 * step(centerColor.b, topColor.b);
|
||||
blueByteTally += 4.0 / 255.0 * step(centerColor.b, topLeftColor.b);
|
||||
blueByteTally += 8.0 / 255.0 * step(centerColor.b, leftColor.b);
|
||||
blueByteTally += 16.0 / 255.0 * step(centerColor.b, bottomLeftColor.b);
|
||||
blueByteTally += 32.0 / 255.0 * step(centerColor.b, bottomColor.b);
|
||||
blueByteTally += 64.0 / 255.0 * step(centerColor.b, bottomRightColor.b);
|
||||
blueByteTally += 128.0 / 255.0 * step(centerColor.b, rightColor.b);
|
||||
|
||||
float greenByteTally = 1.0 / 255.0 * step(centerColor.g, topRightColor.g);
|
||||
greenByteTally += 2.0 / 255.0 * step(centerColor.g, topColor.g);
|
||||
greenByteTally += 4.0 / 255.0 * step(centerColor.g, topLeftColor.g);
|
||||
greenByteTally += 8.0 / 255.0 * step(centerColor.g, leftColor.g);
|
||||
greenByteTally += 16.0 / 255.0 * step(centerColor.g, bottomLeftColor.g);
|
||||
greenByteTally += 32.0 / 255.0 * step(centerColor.g, bottomColor.g);
|
||||
greenByteTally += 64.0 / 255.0 * step(centerColor.g, bottomRightColor.g);
|
||||
greenByteTally += 128.0 / 255.0 * step(centerColor.g, rightColor.g);
|
||||
|
||||
// TODO: Replace the above with a dot product and two vec4s
|
||||
// TODO: Apply step to a matrix, rather than individually
|
||||
|
||||
gl_FragColor = vec4(redByteTally, blueByteTally, greenByteTally, 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageColorLocalBinaryPatternFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
/** Transforms the colors of an image by applying a matrix to them
|
||||
*/
|
||||
@interface GPUImageColorMatrixFilter : GPUImageFilter
|
||||
{
|
||||
GLint colorMatrixUniform;
|
||||
GLint intensityUniform;
|
||||
}
|
||||
|
||||
/** A 4x4 matrix used to transform each color in an image
|
||||
*/
|
||||
@property(readwrite, nonatomic) GPUMatrix4x4 colorMatrix;
|
||||
|
||||
/** The degree to which the new transformed color replaces the original color for each pixel
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat intensity;
|
||||
|
||||
@end
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
#import "GPUImageColorMatrixFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorMatrixFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform lowp mat4 colorMatrix;
|
||||
uniform lowp float intensity;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
lowp vec4 outputColor = textureColor * colorMatrix;
|
||||
|
||||
gl_FragColor = (intensity * outputColor) + ((1.0 - intensity) * textureColor);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorMatrixFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mat4 colorMatrix;
|
||||
uniform float intensity;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 outputColor = textureColor * colorMatrix;
|
||||
|
||||
gl_FragColor = (intensity * outputColor) + ((1.0 - intensity) * textureColor);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageColorMatrixFilter
|
||||
|
||||
@synthesize intensity = _intensity;
|
||||
@synthesize colorMatrix = _colorMatrix;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageColorMatrixFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
colorMatrixUniform = [filterProgram uniformIndex:@"colorMatrix"];
|
||||
intensityUniform = [filterProgram uniformIndex:@"intensity"];
|
||||
|
||||
self.intensity = 1.f;
|
||||
self.colorMatrix = (GPUMatrix4x4){
|
||||
{1.f, 0.f, 0.f, 0.f},
|
||||
{0.f, 1.f, 0.f, 0.f},
|
||||
{0.f, 0.f, 1.f, 0.f},
|
||||
{0.f, 0.f, 0.f, 1.f}
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setIntensity:(CGFloat)newIntensity;
|
||||
{
|
||||
_intensity = newIntensity;
|
||||
|
||||
[self setFloat:_intensity forUniform:intensityUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setColorMatrix:(GPUMatrix4x4)newColorMatrix;
|
||||
{
|
||||
_colorMatrix = newColorMatrix;
|
||||
|
||||
[self setMatrix4f:_colorMatrix forUniform:colorMatrixUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,10 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageColorPackingFilter : GPUImageFilter
|
||||
{
|
||||
GLint texelWidthUniform, texelHeightUniform;
|
||||
|
||||
CGFloat texelWidth, texelHeight;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,139 @@
|
||||
#import "GPUImageColorPackingFilter.h"
|
||||
|
||||
NSString *const kGPUImageColorPackingVertexShaderString = 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 kGPUImageColorPackingFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mediump mat3 convolutionMatrix;
|
||||
|
||||
varying highp vec2 outputTextureCoordinate;
|
||||
|
||||
varying highp vec2 upperLeftInputTextureCoordinate;
|
||||
varying highp vec2 upperRightInputTextureCoordinate;
|
||||
varying highp vec2 lowerLeftInputTextureCoordinate;
|
||||
varying highp vec2 lowerRightInputTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
float upperLeftIntensity = texture2D(inputImageTexture, upperLeftInputTextureCoordinate).r;
|
||||
float upperRightIntensity = texture2D(inputImageTexture, upperRightInputTextureCoordinate).r;
|
||||
float lowerLeftIntensity = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate).r;
|
||||
float lowerRightIntensity = texture2D(inputImageTexture, lowerRightInputTextureCoordinate).r;
|
||||
|
||||
gl_FragColor = vec4(upperLeftIntensity, upperRightIntensity, lowerLeftIntensity, lowerRightIntensity);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorPackingFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mat3 convolutionMatrix;
|
||||
|
||||
varying vec2 outputTextureCoordinate;
|
||||
|
||||
varying vec2 upperLeftInputTextureCoordinate;
|
||||
varying vec2 upperRightInputTextureCoordinate;
|
||||
varying vec2 lowerLeftInputTextureCoordinate;
|
||||
varying vec2 lowerRightInputTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
float upperLeftIntensity = texture2D(inputImageTexture, upperLeftInputTextureCoordinate).r;
|
||||
float upperRightIntensity = texture2D(inputImageTexture, upperRightInputTextureCoordinate).r;
|
||||
float lowerLeftIntensity = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate).r;
|
||||
float lowerRightIntensity = texture2D(inputImageTexture, lowerRightInputTextureCoordinate).r;
|
||||
|
||||
gl_FragColor = vec4(upperLeftIntensity, upperRightIntensity, lowerLeftIntensity, lowerRightIntensity);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageColorPackingFilter
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithVertexShaderFromString:kGPUImageColorPackingVertexShaderString fragmentShaderFromString:kGPUImageColorPackingFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
|
||||
texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
texelWidth = 0.5 / inputTextureSize.width;
|
||||
texelHeight = 0.5 / inputTextureSize.height;
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
glUniform1f(texelWidthUniform, texelWidth);
|
||||
glUniform1f(texelHeightUniform, texelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Managing the display FBOs
|
||||
|
||||
- (CGSize)sizeOfFBO;
|
||||
{
|
||||
CGSize outputSize = [self maximumOutputSize];
|
||||
if ( (CGSizeEqualToSize(outputSize, CGSizeZero)) || (inputTextureSize.width < outputSize.width) )
|
||||
{
|
||||
CGSize quarterSize;
|
||||
quarterSize.width = inputTextureSize.width / 2.0;
|
||||
quarterSize.height = inputTextureSize.height / 2.0;
|
||||
return quarterSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
return outputSize;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Rendering
|
||||
|
||||
- (CGSize)outputFrameSize;
|
||||
{
|
||||
CGSize quarterSize;
|
||||
quarterSize.width = inputTextureSize.width / 2.0;
|
||||
quarterSize.height = inputTextureSize.height / 2.0;
|
||||
return quarterSize;
|
||||
}
|
||||
|
||||
@end
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
// This generates image-wide feature descriptors using the ColourFAST process, as developed and described in
|
||||
//
|
||||
// A. Ensor and S. Hall. ColourFAST: GPU-based feature point detection and tracking on mobile devices. 28th International Conference of Image and Vision Computing, New Zealand, 2013, p. 124-129.
|
||||
//
|
||||
// Seth Hall, "GPU accelerated feature algorithms for mobile devices", PhD thesis, School of Computing and Mathematical Sciences, Auckland University of Technology 2014.
|
||||
// http://aut.researchgateway.ac.nz/handle/10292/7991
|
||||
|
||||
@class GPUImageColourFASTSamplingOperation;
|
||||
@class GPUImageBoxBlurFilter;
|
||||
|
||||
@interface GPUImageColourFASTFeatureDetector : GPUImageFilterGroup
|
||||
{
|
||||
GPUImageBoxBlurFilter *blurFilter;
|
||||
GPUImageColourFASTSamplingOperation *colourFASTSamplingOperation;
|
||||
}
|
||||
// The blur radius of the underlying box blur. The default is 3.0.
|
||||
@property (readwrite, nonatomic) CGFloat blurRadiusInPixels;
|
||||
|
||||
@end
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#import "GPUImageColourFASTFeatureDetector.h"
|
||||
#import "GPUImageColourFASTSamplingOperation.h"
|
||||
#import "GPUImageBoxBlurFilter.h"
|
||||
|
||||
@implementation GPUImageColourFASTFeatureDetector
|
||||
|
||||
@synthesize blurRadiusInPixels;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// First pass: apply a variable Gaussian blur
|
||||
blurFilter = [[GPUImageBoxBlurFilter alloc] init];
|
||||
[self addFilter:blurFilter];
|
||||
|
||||
// Second pass: combine the blurred image with the original sharp one
|
||||
colourFASTSamplingOperation = [[GPUImageColourFASTSamplingOperation alloc] init];
|
||||
[self addFilter:colourFASTSamplingOperation];
|
||||
|
||||
// Texture location 0 needs to be the sharp image for both the blur and the second stage processing
|
||||
[blurFilter addTarget:colourFASTSamplingOperation atTextureLocation:1];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObjects:blurFilter, colourFASTSamplingOperation, nil];
|
||||
self.terminalFilter = colourFASTSamplingOperation;
|
||||
|
||||
self.blurRadiusInPixels = 3.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBlurRadiusInPixels:(CGFloat)newValue;
|
||||
{
|
||||
blurFilter.blurRadiusInPixels = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)blurRadiusInPixels;
|
||||
{
|
||||
return blurFilter.blurRadiusInPixels;
|
||||
}
|
||||
|
||||
@end
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
// This is the feature extraction phase of the ColourFAST feature detector, as described in:
|
||||
//
|
||||
// A. Ensor and S. Hall. ColourFAST: GPU-based feature point detection and tracking on mobile devices. 28th International Conference of Image and Vision Computing, New Zealand, 2013, p. 124-129.
|
||||
//
|
||||
// Seth Hall, "GPU accelerated feature algorithms for mobile devices", PhD thesis, School of Computing and Mathematical Sciences, Auckland University of Technology 2014.
|
||||
// http://aut.researchgateway.ac.nz/handle/10292/7991
|
||||
|
||||
@interface GPUImageColourFASTSamplingOperation : GPUImageTwoInputFilter
|
||||
{
|
||||
GLint texelWidthUniform, texelHeightUniform;
|
||||
|
||||
CGFloat texelWidth, texelHeight;
|
||||
BOOL hasOverriddenImageSizeFactor;
|
||||
}
|
||||
|
||||
// The texel width and height determines how far out to sample from this texel. By default, this is the normalized width of a pixel, but this can be overridden for different effects.
|
||||
@property(readwrite, nonatomic) CGFloat texelWidth;
|
||||
@property(readwrite, nonatomic) CGFloat texelHeight;
|
||||
|
||||
@end
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
#import "GPUImageColourFASTSamplingOperation.h"
|
||||
|
||||
NSString *const kGPUImageColourFASTSamplingVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec4 inputTextureCoordinate;
|
||||
attribute vec4 inputTextureCoordinate2;
|
||||
|
||||
uniform float texelWidth;
|
||||
uniform float texelHeight;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 pointATextureCoordinate;
|
||||
varying vec2 pointBTextureCoordinate;
|
||||
varying vec2 pointCTextureCoordinate;
|
||||
varying vec2 pointDTextureCoordinate;
|
||||
varying vec2 pointETextureCoordinate;
|
||||
varying vec2 pointFTextureCoordinate;
|
||||
varying vec2 pointGTextureCoordinate;
|
||||
varying vec2 pointHTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
float tripleTexelWidth = 3.0 * texelWidth;
|
||||
float tripleTexelHeight = 3.0 * texelHeight;
|
||||
|
||||
textureCoordinate = inputTextureCoordinate.xy;
|
||||
|
||||
pointATextureCoordinate = vec2(textureCoordinate.x + tripleTexelWidth, textureCoordinate.y + texelHeight);
|
||||
pointBTextureCoordinate = vec2(textureCoordinate.x + texelWidth, textureCoordinate.y + tripleTexelHeight);
|
||||
pointCTextureCoordinate = vec2(textureCoordinate.x - texelWidth, textureCoordinate.y + tripleTexelHeight);
|
||||
pointDTextureCoordinate = vec2(textureCoordinate.x - tripleTexelWidth, textureCoordinate.y + texelHeight);
|
||||
pointETextureCoordinate = vec2(textureCoordinate.x - tripleTexelWidth, textureCoordinate.y - texelHeight);
|
||||
pointFTextureCoordinate = vec2(textureCoordinate.x - texelWidth, textureCoordinate.y - tripleTexelHeight);
|
||||
pointGTextureCoordinate = vec2(textureCoordinate.x + texelWidth, textureCoordinate.y - tripleTexelHeight);
|
||||
pointHTextureCoordinate = vec2(textureCoordinate.x + tripleTexelWidth, textureCoordinate.y - texelHeight);
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColourFASTSamplingFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 pointATextureCoordinate;
|
||||
varying vec2 pointBTextureCoordinate;
|
||||
varying vec2 pointCTextureCoordinate;
|
||||
varying vec2 pointDTextureCoordinate;
|
||||
varying vec2 pointETextureCoordinate;
|
||||
varying vec2 pointFTextureCoordinate;
|
||||
varying vec2 pointGTextureCoordinate;
|
||||
varying vec2 pointHTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
const float PITwo = 6.2832;
|
||||
const float PI = 3.1416;
|
||||
void main()
|
||||
{
|
||||
vec3 centerColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
|
||||
vec3 pointAColor = texture2D(inputImageTexture, pointATextureCoordinate).rgb;
|
||||
vec3 pointBColor = texture2D(inputImageTexture, pointBTextureCoordinate).rgb;
|
||||
vec3 pointCColor = texture2D(inputImageTexture, pointCTextureCoordinate).rgb;
|
||||
vec3 pointDColor = texture2D(inputImageTexture, pointDTextureCoordinate).rgb;
|
||||
vec3 pointEColor = texture2D(inputImageTexture, pointETextureCoordinate).rgb;
|
||||
vec3 pointFColor = texture2D(inputImageTexture, pointFTextureCoordinate).rgb;
|
||||
vec3 pointGColor = texture2D(inputImageTexture, pointGTextureCoordinate).rgb;
|
||||
vec3 pointHColor = texture2D(inputImageTexture, pointHTextureCoordinate).rgb;
|
||||
|
||||
vec3 colorComparison = ((pointAColor + pointBColor + pointCColor + pointDColor + pointEColor + pointFColor + pointGColor + pointHColor) * 0.125) - centerColor;
|
||||
|
||||
// Direction calculation drawn from Appendix B of Seth Hall's Ph.D. thesis
|
||||
|
||||
vec3 dirX = (pointAColor*0.94868) + (pointBColor*0.316227) - (pointCColor*0.316227) - (pointDColor*0.94868) - (pointEColor*0.94868) - (pointFColor*0.316227) + (pointGColor*0.316227) + (pointHColor*0.94868);
|
||||
vec3 dirY = (pointAColor*0.316227) + (pointBColor*0.94868) + (pointCColor*0.94868) + (pointDColor*0.316227) - (pointEColor*0.316227) - (pointFColor*0.94868) - (pointGColor*0.94868) - (pointHColor*0.316227);
|
||||
vec3 absoluteDifference = abs(colorComparison);
|
||||
float componentLength = length(colorComparison);
|
||||
float avgX = dot(absoluteDifference, dirX) / componentLength;
|
||||
float avgY = dot(absoluteDifference, dirY) / componentLength;
|
||||
float angle = atan(avgY, avgX);
|
||||
|
||||
vec3 normalizedColorComparison = (colorComparison + 1.0) * 0.5;
|
||||
|
||||
gl_FragColor = vec4(normalizedColorComparison, (angle+PI)/PITwo);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColourFASTSamplingFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 pointATextureCoordinate;
|
||||
varying vec2 pointBTextureCoordinate;
|
||||
varying vec2 pointCTextureCoordinate;
|
||||
varying vec2 pointDTextureCoordinate;
|
||||
varying vec2 pointETextureCoordinate;
|
||||
varying vec2 pointFTextureCoordinate;
|
||||
varying vec2 pointGTextureCoordinate;
|
||||
varying vec2 pointHTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
const float PITwo = 6.2832;
|
||||
const float PI = 3.1416;
|
||||
void main()
|
||||
{
|
||||
vec3 centerColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
|
||||
vec3 pointAColor = texture2D(inputImageTexture, pointATextureCoordinate).rgb;
|
||||
vec3 pointBColor = texture2D(inputImageTexture, pointBTextureCoordinate).rgb;
|
||||
vec3 pointCColor = texture2D(inputImageTexture, pointCTextureCoordinate).rgb;
|
||||
vec3 pointDColor = texture2D(inputImageTexture, pointDTextureCoordinate).rgb;
|
||||
vec3 pointEColor = texture2D(inputImageTexture, pointETextureCoordinate).rgb;
|
||||
vec3 pointFColor = texture2D(inputImageTexture, pointFTextureCoordinate).rgb;
|
||||
vec3 pointGColor = texture2D(inputImageTexture, pointGTextureCoordinate).rgb;
|
||||
vec3 pointHColor = texture2D(inputImageTexture, pointHTextureCoordinate).rgb;
|
||||
|
||||
vec3 colorComparison = ((pointAColor + pointBColor + pointCColor + pointDColor + pointEColor + pointFColor + pointGColor + pointHColor) * 0.125) - centerColor;
|
||||
|
||||
// Direction calculation drawn from Appendix B of Seth Hall's Ph.D. thesis
|
||||
|
||||
vec3 dirX = (pointAColor*0.94868) + (pointBColor*0.316227) - (pointCColor*0.316227) - (pointDColor*0.94868) - (pointEColor*0.94868) - (pointFColor*0.316227) + (pointGColor*0.316227) + (pointHColor*0.94868);
|
||||
vec3 dirY = (pointAColor*0.316227) + (pointBColor*0.94868) + (pointCColor*0.94868) + (pointDColor*0.316227) - (pointEColor*0.316227) - (pointFColor*0.94868) - (pointGColor*0.94868) - (pointHColor*0.316227);
|
||||
vec3 absoluteDifference = abs(colorComparison);
|
||||
float componentLength = length(colorComparison);
|
||||
float avgX = dot(absoluteDifference, dirX) / componentLength;
|
||||
float avgY = dot(absoluteDifference, dirY) / componentLength;
|
||||
float angle = atan(avgY, avgX);
|
||||
|
||||
vec3 normalizedColorComparison = (colorComparison + 1.0) * 0.5;
|
||||
|
||||
gl_FragColor = vec4(normalizedColorComparison, (angle+PI)/PITwo);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
@implementation GPUImageColourFASTSamplingOperation
|
||||
|
||||
@synthesize texelWidth = _texelWidth;
|
||||
@synthesize texelHeight = _texelHeight;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
{
|
||||
if (!(self = [super initWithVertexShaderFromString:kGPUImageColourFASTSamplingVertexShaderString fragmentShaderFromString:kGPUImageColourFASTSamplingFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
|
||||
texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
if (!hasOverriddenImageSizeFactor)
|
||||
{
|
||||
_texelWidth = 1.0 / filterFrameSize.width;
|
||||
_texelHeight = 1.0 / filterFrameSize.height;
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
if (GPUImageRotationSwapsWidthAndHeight(inputRotation))
|
||||
{
|
||||
glUniform1f(texelWidthUniform, _texelHeight);
|
||||
glUniform1f(texelHeightUniform, _texelWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
glUniform1f(texelWidthUniform, _texelWidth);
|
||||
glUniform1f(texelHeightUniform, _texelHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setTexelWidth:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelWidth = newValue;
|
||||
|
||||
[self setFloat:_texelWidth forUniform:texelWidthUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setTexelHeight:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelHeight = newValue;
|
||||
|
||||
[self setFloat:_texelHeight forUniform:texelHeightUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
/** Adjusts the contrast of the image
|
||||
*/
|
||||
@interface GPUImageContrastFilter : GPUImageFilter
|
||||
{
|
||||
GLint contrastUniform;
|
||||
}
|
||||
|
||||
/** Contrast ranges from 0.0 to 4.0 (max contrast), with 1.0 as the normal level
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat contrast;
|
||||
|
||||
@end
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#import "GPUImageContrastFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageContrastFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform lowp float contrast;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4(((textureColor.rgb - vec3(0.5)) * contrast + vec3(0.5)), textureColor.w);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageContrastFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform float contrast;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4(((textureColor.rgb - vec3(0.5)) * contrast + vec3(0.5)), textureColor.w);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageContrastFilter
|
||||
|
||||
@synthesize contrast = _contrast;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageContrastFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
contrastUniform = [filterProgram uniformIndex:@"contrast"];
|
||||
self.contrast = 1.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setContrast:(CGFloat)newValue;
|
||||
{
|
||||
_contrast = newValue;
|
||||
|
||||
[self setFloat:_contrast forUniform:contrastUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageCropFilter : GPUImageFilter
|
||||
{
|
||||
GLfloat cropTextureCoordinates[8];
|
||||
}
|
||||
|
||||
// The crop region is the rectangle within the image to crop. It is normalized to a coordinate space from 0.0 to 1.0, with 0.0, 0.0 being the upper left corner of the image
|
||||
@property(readwrite, nonatomic) CGRect cropRegion;
|
||||
|
||||
// Initialization and teardown
|
||||
- (id)initWithCropRegion:(CGRect)newCropRegion;
|
||||
|
||||
@end
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
#import "GPUImageCropFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageCropFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageCropFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@interface GPUImageCropFilter ()
|
||||
|
||||
- (void)calculateCropTextureCoordinates;
|
||||
|
||||
@end
|
||||
|
||||
@interface GPUImageCropFilter()
|
||||
{
|
||||
CGSize originallySuppliedInputSize;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GPUImageCropFilter
|
||||
|
||||
@synthesize cropRegion = _cropRegion;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithCropRegion:(CGRect)newCropRegion;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageCropFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.cropRegion = newCropRegion;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithCropRegion:CGRectMake(0.0, 0.0, 1.0, 1.0)]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Rendering
|
||||
|
||||
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
if (self.preventRendering)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if (overrideInputSize)
|
||||
// {
|
||||
// if (CGSizeEqualToSize(forcedMaximumSize, CGSizeZero))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// CGRect insetRect = AVMakeRectWithAspectRatioInsideRect(newSize, CGRectMake(0.0, 0.0, forcedMaximumSize.width, forcedMaximumSize.height));
|
||||
// inputTextureSize = insetRect.size;
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
CGSize rotatedSize = [self rotatedSize:newSize forIndex:textureIndex];
|
||||
originallySuppliedInputSize = rotatedSize;
|
||||
|
||||
CGSize scaledSize;
|
||||
scaledSize.width = rotatedSize.width * _cropRegion.size.width;
|
||||
scaledSize.height = rotatedSize.height * _cropRegion.size.height;
|
||||
|
||||
|
||||
if (CGSizeEqualToSize(scaledSize, CGSizeZero))
|
||||
{
|
||||
inputTextureSize = scaledSize;
|
||||
}
|
||||
else if (!CGSizeEqualToSize(inputTextureSize, scaledSize))
|
||||
{
|
||||
inputTextureSize = scaledSize;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark GPUImageInput
|
||||
|
||||
- (void)calculateCropTextureCoordinates;
|
||||
{
|
||||
CGFloat minX = _cropRegion.origin.x;
|
||||
CGFloat minY = _cropRegion.origin.y;
|
||||
CGFloat maxX = CGRectGetMaxX(_cropRegion);
|
||||
CGFloat maxY = CGRectGetMaxY(_cropRegion);
|
||||
|
||||
switch(inputRotation)
|
||||
{
|
||||
case kGPUImageNoRotation: // Works
|
||||
{
|
||||
cropTextureCoordinates[0] = minX; // 0,0
|
||||
cropTextureCoordinates[1] = minY;
|
||||
|
||||
cropTextureCoordinates[2] = maxX; // 1,0
|
||||
cropTextureCoordinates[3] = minY;
|
||||
|
||||
cropTextureCoordinates[4] = minX; // 0,1
|
||||
cropTextureCoordinates[5] = maxY;
|
||||
|
||||
cropTextureCoordinates[6] = maxX; // 1,1
|
||||
cropTextureCoordinates[7] = maxY;
|
||||
}; break;
|
||||
case kGPUImageRotateLeft: // Fixed
|
||||
{
|
||||
cropTextureCoordinates[0] = maxY; // 1,0
|
||||
cropTextureCoordinates[1] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[2] = maxY; // 1,1
|
||||
cropTextureCoordinates[3] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[4] = minY; // 0,0
|
||||
cropTextureCoordinates[5] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[6] = minY; // 0,1
|
||||
cropTextureCoordinates[7] = 1.0 - minX;
|
||||
}; break;
|
||||
case kGPUImageRotateRight: // Fixed
|
||||
{
|
||||
cropTextureCoordinates[0] = minY; // 0,1
|
||||
cropTextureCoordinates[1] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[2] = minY; // 0,0
|
||||
cropTextureCoordinates[3] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[4] = maxY; // 1,1
|
||||
cropTextureCoordinates[5] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[6] = maxY; // 1,0
|
||||
cropTextureCoordinates[7] = 1.0 - maxX;
|
||||
}; break;
|
||||
case kGPUImageFlipVertical: // Works for me
|
||||
{
|
||||
cropTextureCoordinates[0] = minX; // 0,1
|
||||
cropTextureCoordinates[1] = maxY;
|
||||
|
||||
cropTextureCoordinates[2] = maxX; // 1,1
|
||||
cropTextureCoordinates[3] = maxY;
|
||||
|
||||
cropTextureCoordinates[4] = minX; // 0,0
|
||||
cropTextureCoordinates[5] = minY;
|
||||
|
||||
cropTextureCoordinates[6] = maxX; // 1,0
|
||||
cropTextureCoordinates[7] = minY;
|
||||
}; break;
|
||||
case kGPUImageFlipHorizonal: // Works for me
|
||||
{
|
||||
cropTextureCoordinates[0] = maxX; // 1,0
|
||||
cropTextureCoordinates[1] = minY;
|
||||
|
||||
cropTextureCoordinates[2] = minX; // 0,0
|
||||
cropTextureCoordinates[3] = minY;
|
||||
|
||||
cropTextureCoordinates[4] = maxX; // 1,1
|
||||
cropTextureCoordinates[5] = maxY;
|
||||
|
||||
cropTextureCoordinates[6] = minX; // 0,1
|
||||
cropTextureCoordinates[7] = maxY;
|
||||
}; break;
|
||||
case kGPUImageRotate180: // Fixed
|
||||
{
|
||||
cropTextureCoordinates[0] = maxX; // 1,1
|
||||
cropTextureCoordinates[1] = maxY;
|
||||
|
||||
cropTextureCoordinates[2] = minX; // 0,1
|
||||
cropTextureCoordinates[3] = maxY;
|
||||
|
||||
cropTextureCoordinates[4] = maxX; // 1,0
|
||||
cropTextureCoordinates[5] = minY;
|
||||
|
||||
cropTextureCoordinates[6] = minX; // 0,0
|
||||
cropTextureCoordinates[7] = minY;
|
||||
}; break;
|
||||
case kGPUImageRotateRightFlipVertical: // Fixed
|
||||
{
|
||||
cropTextureCoordinates[0] = minY; // 0,0
|
||||
cropTextureCoordinates[1] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[2] = minY; // 0,1
|
||||
cropTextureCoordinates[3] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[4] = maxY; // 1,0
|
||||
cropTextureCoordinates[5] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[6] = maxY; // 1,1
|
||||
cropTextureCoordinates[7] = 1.0 - minX;
|
||||
}; break;
|
||||
case kGPUImageRotateRightFlipHorizontal: // Fixed
|
||||
{
|
||||
cropTextureCoordinates[0] = maxY; // 1,1
|
||||
cropTextureCoordinates[1] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[2] = maxY; // 1,0
|
||||
cropTextureCoordinates[3] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[4] = minY; // 0,1
|
||||
cropTextureCoordinates[5] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[6] = minY; // 0,0
|
||||
cropTextureCoordinates[7] = 1.0 - maxX;
|
||||
}; break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
static const GLfloat cropSquareVertices[] = {
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
};
|
||||
|
||||
[self renderToTextureWithVertices:cropSquareVertices textureCoordinates:cropTextureCoordinates];
|
||||
|
||||
[self informTargetsAboutNewFrameAtTime:frameTime];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setCropRegion:(CGRect)newValue;
|
||||
{
|
||||
NSParameterAssert(newValue.origin.x >= 0 && newValue.origin.x <= 1 &&
|
||||
newValue.origin.y >= 0 && newValue.origin.y <= 1 &&
|
||||
newValue.size.width >= 0 && newValue.size.width <= 1 &&
|
||||
newValue.size.height >= 0 && newValue.size.height <= 1);
|
||||
|
||||
_cropRegion = newValue;
|
||||
[self calculateCropTextureCoordinates];
|
||||
}
|
||||
|
||||
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
[super setInputRotation:newInputRotation atIndex:textureIndex];
|
||||
[self calculateCropTextureCoordinates];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageCrosshairGenerator : GPUImageFilter
|
||||
{
|
||||
GLint crosshairWidthUniform, crosshairColorUniform;
|
||||
}
|
||||
|
||||
// The width of the displayed crosshairs, in pixels. Currently this only works well for odd widths. The default is 5.
|
||||
@property(readwrite, nonatomic) CGFloat crosshairWidth;
|
||||
|
||||
// The color of the crosshairs is specified using individual red, green, and blue components (normalized to 1.0). The default is green: (0.0, 1.0, 0.0).
|
||||
- (void)setCrosshairColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
|
||||
// Rendering
|
||||
- (void)renderCrosshairsFromArray:(GLfloat *)crosshairCoordinates count:(NSUInteger)numberOfCrosshairs frameTime:(CMTime)frameTime;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,139 @@
|
||||
#import "GPUImageCrosshairGenerator.h"
|
||||
|
||||
NSString *const kGPUImageCrosshairVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
|
||||
uniform float crosshairWidth;
|
||||
|
||||
varying vec2 centerLocation;
|
||||
varying float pointSpacing;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(((position.xy * 2.0) - 1.0), 0.0, 1.0);
|
||||
gl_PointSize = crosshairWidth + 1.0;
|
||||
pointSpacing = 1.0 / crosshairWidth;
|
||||
centerLocation = vec2(pointSpacing * ceil(crosshairWidth / 2.0), pointSpacing * ceil(crosshairWidth / 2.0));
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageCrosshairFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform lowp vec3 crosshairColor;
|
||||
|
||||
varying highp vec2 centerLocation;
|
||||
varying highp float pointSpacing;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec2 distanceFromCenter = abs(centerLocation - gl_PointCoord.xy);
|
||||
lowp float axisTest = step(pointSpacing, gl_PointCoord.y) * step(distanceFromCenter.x, 0.09) + step(pointSpacing, gl_PointCoord.x) * step(distanceFromCenter.y, 0.09);
|
||||
|
||||
gl_FragColor = vec4(crosshairColor * axisTest, axisTest);
|
||||
// gl_FragColor = vec4(distanceFromCenterInX, distanceFromCenterInY, 0.0, 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageCrosshairFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
GPUImageEscapedHashIdentifier(version 120)\n
|
||||
|
||||
uniform vec3 crosshairColor;
|
||||
|
||||
varying vec2 centerLocation;
|
||||
varying float pointSpacing;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 distanceFromCenter = abs(centerLocation - gl_PointCoord.xy);
|
||||
float axisTest = step(pointSpacing, gl_PointCoord.y) * step(distanceFromCenter.x, 0.09) + step(pointSpacing, gl_PointCoord.x) * step(distanceFromCenter.y, 0.09);
|
||||
|
||||
gl_FragColor = vec4(crosshairColor * axisTest, axisTest);
|
||||
// gl_FragColor = vec4(distanceFromCenterInX, distanceFromCenterInY, 0.0, 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageCrosshairGenerator
|
||||
|
||||
@synthesize crosshairWidth = _crosshairWidth;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithVertexShaderFromString:kGPUImageCrosshairVertexShaderString fragmentShaderFromString:kGPUImageCrosshairFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
crosshairWidthUniform = [filterProgram uniformIndex:@"crosshairWidth"];
|
||||
crosshairColorUniform = [filterProgram uniformIndex:@"crosshairColor"];
|
||||
|
||||
self.crosshairWidth = 5.0;
|
||||
[self setCrosshairColorRed:0.0 green:1.0 blue:0.0];
|
||||
});
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Rendering
|
||||
|
||||
- (void)renderCrosshairsFromArray:(GLfloat *)crosshairCoordinates count:(NSUInteger)numberOfCrosshairs frameTime:(CMTime)frameTime;
|
||||
{
|
||||
if (self.preventRendering)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
#else
|
||||
glEnable(GL_POINT_SPRITE);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
#endif
|
||||
|
||||
outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:[self sizeOfFBO] textureOptions:self.outputTextureOptions onlyTexture:NO];
|
||||
[outputFramebuffer activateFramebuffer];
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, crosshairCoordinates);
|
||||
|
||||
glDrawArrays(GL_POINTS, 0, (GLsizei)numberOfCrosshairs);
|
||||
|
||||
[self informTargetsAboutNewFrameAtTime:frameTime];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
|
||||
{
|
||||
// Prevent rendering of the frame by normal means
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setCrosshairWidth:(CGFloat)newValue;
|
||||
{
|
||||
_crosshairWidth = newValue;
|
||||
|
||||
[self setFloat:_crosshairWidth forUniform:crosshairWidthUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setCrosshairColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
{
|
||||
GPUVector3 crosshairColor = {redComponent, greenComponent, blueComponent};
|
||||
|
||||
[self setVec3:crosshairColor forUniform:crosshairColorUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageCrosshatchFilter : GPUImageFilter
|
||||
{
|
||||
GLint crossHatchSpacingUniform, lineWidthUniform;
|
||||
}
|
||||
// The fractional width of the image to use as the spacing for the crosshatch. The default is 0.03.
|
||||
@property(readwrite, nonatomic) CGFloat crossHatchSpacing;
|
||||
|
||||
// A relative width for the crosshatch lines. The default is 0.003.
|
||||
@property(readwrite, nonatomic) CGFloat lineWidth;
|
||||
|
||||
@end
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
#import "GPUImageCrosshatchFilter.h"
|
||||
|
||||
// Shader code based on http://machinesdontcare.wordpress.com/
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageCrosshatchFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform highp float crossHatchSpacing;
|
||||
uniform highp float lineWidth;
|
||||
|
||||
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);
|
||||
|
||||
void main()
|
||||
{
|
||||
highp float luminance = dot(texture2D(inputImageTexture, textureCoordinate).rgb, W);
|
||||
|
||||
lowp vec4 colorToDisplay = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
if (luminance < 1.00)
|
||||
{
|
||||
if (mod(textureCoordinate.x + textureCoordinate.y, crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.75)
|
||||
{
|
||||
if (mod(textureCoordinate.x - textureCoordinate.y, crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.50)
|
||||
{
|
||||
if (mod(textureCoordinate.x + textureCoordinate.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.3)
|
||||
{
|
||||
if (mod(textureCoordinate.x - textureCoordinate.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = colorToDisplay;
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageCrosshatchFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform float crossHatchSpacing;
|
||||
uniform float lineWidth;
|
||||
|
||||
const vec3 W = vec3(0.2125, 0.7154, 0.0721);
|
||||
|
||||
void main()
|
||||
{
|
||||
float luminance = dot(texture2D(inputImageTexture, textureCoordinate).rgb, W);
|
||||
|
||||
vec4 colorToDisplay = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
if (luminance < 1.00)
|
||||
{
|
||||
if (mod(textureCoordinate.x + textureCoordinate.y, crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.75)
|
||||
{
|
||||
if (mod(textureCoordinate.x - textureCoordinate.y, crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.50)
|
||||
{
|
||||
if (mod(textureCoordinate.x + textureCoordinate.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.3)
|
||||
{
|
||||
if (mod(textureCoordinate.x - textureCoordinate.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = colorToDisplay;
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageCrosshatchFilter
|
||||
|
||||
@synthesize crossHatchSpacing = _crossHatchSpacing;
|
||||
@synthesize lineWidth = _lineWidth;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageCrosshatchFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
crossHatchSpacingUniform = [filterProgram uniformIndex:@"crossHatchSpacing"];
|
||||
lineWidthUniform = [filterProgram uniformIndex:@"lineWidth"];
|
||||
|
||||
self.crossHatchSpacing = 0.03;
|
||||
self.lineWidth = 0.003;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setCrossHatchSpacing:(CGFloat)newValue;
|
||||
{
|
||||
CGFloat singlePixelSpacing;
|
||||
if (inputTextureSize.width != 0.0)
|
||||
{
|
||||
singlePixelSpacing = 1.0 / inputTextureSize.width;
|
||||
}
|
||||
else
|
||||
{
|
||||
singlePixelSpacing = 1.0 / 2048.0;
|
||||
}
|
||||
|
||||
if (newValue < singlePixelSpacing)
|
||||
{
|
||||
_crossHatchSpacing = singlePixelSpacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
_crossHatchSpacing = newValue;
|
||||
}
|
||||
|
||||
[self setFloat:_crossHatchSpacing forUniform:crossHatchSpacingUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setLineWidth:(CGFloat)newValue;
|
||||
{
|
||||
_lineWidth = newValue;
|
||||
|
||||
[self setFloat:_lineWidth forUniform:lineWidthUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageDarkenBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,52 @@
|
||||
#import "GPUImageDarkenBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDarkenBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
lowp vec4 overlayer = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(min(overlayer.rgb * base.a, base.rgb * overlayer.a) + overlayer.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlayer.a), 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDarkenBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlayer = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(min(overlayer.rgb * base.a, base.rgb * overlayer.a) + overlayer.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlayer.a), 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageDarkenBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDarkenBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageDifferenceBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
#import "GPUImageDifferenceBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDifferenceBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
mediump vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
gl_FragColor = vec4(abs(textureColor2.rgb - textureColor.rgb), textureColor.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDifferenceBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
gl_FragColor = vec4(abs(textureColor2.rgb - textureColor.rgb), textureColor.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageDifferenceBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDifferenceBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#import "GPUImageTwoPassTextureSamplingFilter.h"
|
||||
|
||||
// For each pixel, this sets it to the maximum value of the red channel in a rectangular neighborhood extending out dilationRadius pixels from the center.
|
||||
// This extends out bright features, and is most commonly used with black-and-white thresholded images.
|
||||
|
||||
extern NSString *const kGPUImageDilationRadiusOneVertexShaderString;
|
||||
extern NSString *const kGPUImageDilationRadiusTwoVertexShaderString;
|
||||
extern NSString *const kGPUImageDilationRadiusThreeVertexShaderString;
|
||||
extern NSString *const kGPUImageDilationRadiusFourVertexShaderString;
|
||||
|
||||
@interface GPUImageDilationFilter : GPUImageTwoPassTextureSamplingFilter
|
||||
|
||||
// Acceptable values for dilationRadius, which sets the distance in pixels to sample out from the center, are 1, 2, 3, and 4.
|
||||
- (id)initWithRadius:(NSUInteger)dilationRadius;
|
||||
|
||||
@end
|
||||
+431
@@ -0,0 +1,431 @@
|
||||
#import "GPUImageDilationFilter.h"
|
||||
|
||||
@implementation GPUImageDilationFilter
|
||||
|
||||
NSString *const kGPUImageDilationRadiusOneVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec2 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidthOffset;
|
||||
uniform float texelHeightOffset;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 offset = vec2(texelWidthOffset, texelHeightOffset);
|
||||
|
||||
centerTextureCoordinate = inputTextureCoordinate;
|
||||
oneStepNegativeTextureCoordinate = inputTextureCoordinate - offset;
|
||||
oneStepPositiveTextureCoordinate = inputTextureCoordinate + offset;
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusTwoVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec2 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidthOffset;
|
||||
uniform float texelHeightOffset;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 offset = vec2(texelWidthOffset, texelHeightOffset);
|
||||
|
||||
centerTextureCoordinate = inputTextureCoordinate;
|
||||
oneStepNegativeTextureCoordinate = inputTextureCoordinate - offset;
|
||||
oneStepPositiveTextureCoordinate = inputTextureCoordinate + offset;
|
||||
twoStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 2.0);
|
||||
twoStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 2.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusThreeVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec2 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidthOffset;
|
||||
uniform float texelHeightOffset;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 offset = vec2(texelWidthOffset, texelHeightOffset);
|
||||
|
||||
centerTextureCoordinate = inputTextureCoordinate;
|
||||
oneStepNegativeTextureCoordinate = inputTextureCoordinate - offset;
|
||||
oneStepPositiveTextureCoordinate = inputTextureCoordinate + offset;
|
||||
twoStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 2.0);
|
||||
twoStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 2.0);
|
||||
threeStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 3.0);
|
||||
threeStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 3.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusFourVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec2 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidthOffset;
|
||||
uniform float texelHeightOffset;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
varying vec2 fourStepsPositiveTextureCoordinate;
|
||||
varying vec2 fourStepsNegativeTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 offset = vec2(texelWidthOffset, texelHeightOffset);
|
||||
|
||||
centerTextureCoordinate = inputTextureCoordinate;
|
||||
oneStepNegativeTextureCoordinate = inputTextureCoordinate - offset;
|
||||
oneStepPositiveTextureCoordinate = inputTextureCoordinate + offset;
|
||||
twoStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 2.0);
|
||||
twoStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 2.0);
|
||||
threeStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 3.0);
|
||||
threeStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 3.0);
|
||||
fourStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 4.0);
|
||||
fourStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 4.0);
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDilationRadiusOneFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusTwoFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusThreeFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, threeStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, threeStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusFourFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
varying vec2 fourStepsPositiveTextureCoordinate;
|
||||
varying vec2 fourStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
float fourStepsPositiveIntensity = texture2D(inputImageTexture, fourStepsPositiveTextureCoordinate).r;
|
||||
float fourStepsNegativeIntensity = texture2D(inputImageTexture, fourStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, threeStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, threeStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, fourStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, fourStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDilationRadiusOneFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
|
||||
float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusTwoFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusThreeFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, threeStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, threeStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusFourFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
varying vec2 fourStepsPositiveTextureCoordinate;
|
||||
varying vec2 fourStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
float fourStepsPositiveIntensity = texture2D(inputImageTexture, fourStepsPositiveTextureCoordinate).r;
|
||||
float fourStepsNegativeIntensity = texture2D(inputImageTexture, fourStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, threeStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, threeStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, fourStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, fourStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithRadius:(NSUInteger)dilationRadius;
|
||||
{
|
||||
NSString *fragmentShaderForThisRadius = nil;
|
||||
NSString *vertexShaderForThisRadius = nil;
|
||||
|
||||
switch (dilationRadius)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusOneVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageDilationRadiusOneFragmentShaderString;
|
||||
}; break;
|
||||
case 2:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusTwoVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageDilationRadiusTwoFragmentShaderString;
|
||||
}; break;
|
||||
case 3:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusThreeVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageDilationRadiusThreeFragmentShaderString;
|
||||
}; break;
|
||||
case 4:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusFourVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageDilationRadiusFourFragmentShaderString;
|
||||
}; break;
|
||||
default:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusFourVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageDilationRadiusFourFragmentShaderString;
|
||||
}; break;
|
||||
}
|
||||
|
||||
if (!(self = [super initWithFirstStageVertexShaderFromString:vertexShaderForThisRadius firstStageFragmentShaderFromString:fragmentShaderForThisRadius secondStageVertexShaderFromString:vertexShaderForThisRadius secondStageFragmentShaderFromString:fragmentShaderForThisRadius]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithRadius:1]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageDirectionalNonMaximumSuppressionFilter : GPUImageFilter
|
||||
{
|
||||
GLint texelWidthUniform, texelHeightUniform;
|
||||
GLint upperThresholdUniform, lowerThresholdUniform;
|
||||
|
||||
BOOL hasOverriddenImageSizeFactor;
|
||||
}
|
||||
|
||||
// The texel width and height determines how far out to sample from this texel. By default, this is the normalized width of a pixel, but this can be overridden for different effects.
|
||||
@property(readwrite, nonatomic) CGFloat texelWidth;
|
||||
@property(readwrite, nonatomic) CGFloat texelHeight;
|
||||
|
||||
// These thresholds set cutoffs for the intensities that definitely get registered (upper threshold) and those that definitely don't (lower threshold)
|
||||
@property(readwrite, nonatomic) CGFloat upperThreshold;
|
||||
@property(readwrite, nonatomic) CGFloat lowerThreshold;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,141 @@
|
||||
#import "GPUImageDirectionalNonMaximumSuppressionFilter.h"
|
||||
|
||||
@implementation GPUImageDirectionalNonMaximumSuppressionFilter
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDirectionalNonmaximumSuppressionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision mediump float;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform highp float texelWidth;
|
||||
uniform highp float texelHeight;
|
||||
uniform mediump float upperThreshold;
|
||||
uniform mediump float lowerThreshold;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 currentGradientAndDirection = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
vec2 gradientDirection = ((currentGradientAndDirection.gb * 2.0) - 1.0) * vec2(texelWidth, texelHeight);
|
||||
|
||||
float firstSampledGradientMagnitude = texture2D(inputImageTexture, textureCoordinate + gradientDirection).r;
|
||||
float secondSampledGradientMagnitude = texture2D(inputImageTexture, textureCoordinate - gradientDirection).r;
|
||||
|
||||
float multiplier = step(firstSampledGradientMagnitude, currentGradientAndDirection.r);
|
||||
multiplier = multiplier * step(secondSampledGradientMagnitude, currentGradientAndDirection.r);
|
||||
|
||||
float thresholdCompliance = smoothstep(lowerThreshold, upperThreshold, currentGradientAndDirection.r);
|
||||
multiplier = multiplier * thresholdCompliance;
|
||||
|
||||
gl_FragColor = vec4(multiplier, multiplier, multiplier, 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDirectionalNonmaximumSuppressionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform float texelWidth;
|
||||
uniform float texelHeight;
|
||||
uniform float upperThreshold;
|
||||
uniform float lowerThreshold;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 currentGradientAndDirection = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
vec2 gradientDirection = ((currentGradientAndDirection.gb * 2.0) - 1.0) * vec2(texelWidth, texelHeight);
|
||||
|
||||
float firstSampledGradientMagnitude = texture2D(inputImageTexture, textureCoordinate + gradientDirection).r;
|
||||
float secondSampledGradientMagnitude = texture2D(inputImageTexture, textureCoordinate - gradientDirection).r;
|
||||
|
||||
float multiplier = step(firstSampledGradientMagnitude, currentGradientAndDirection.r);
|
||||
multiplier = multiplier * step(secondSampledGradientMagnitude, currentGradientAndDirection.r);
|
||||
|
||||
float thresholdCompliance = smoothstep(lowerThreshold, upperThreshold, currentGradientAndDirection.r);
|
||||
multiplier = multiplier * thresholdCompliance;
|
||||
|
||||
gl_FragColor = vec4(multiplier, multiplier, multiplier, 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@synthesize texelWidth = _texelWidth;
|
||||
@synthesize texelHeight = _texelHeight;
|
||||
@synthesize upperThreshold = _upperThreshold;
|
||||
@synthesize lowerThreshold = _lowerThreshold;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDirectionalNonmaximumSuppressionFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
|
||||
texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
|
||||
upperThresholdUniform = [filterProgram uniformIndex:@"upperThreshold"];
|
||||
lowerThresholdUniform = [filterProgram uniformIndex:@"lowerThreshold"];
|
||||
|
||||
self.upperThreshold = 0.5;
|
||||
self.lowerThreshold = 0.1;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
if (!hasOverriddenImageSizeFactor)
|
||||
{
|
||||
_texelWidth = 1.0 / filterFrameSize.width;
|
||||
_texelHeight = 1.0 / filterFrameSize.height;
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
glUniform1f(texelWidthUniform, _texelWidth);
|
||||
glUniform1f(texelHeightUniform, _texelHeight);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setTexelWidth:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelWidth = newValue;
|
||||
|
||||
[self setFloat:_texelWidth forUniform:texelWidthUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setTexelHeight:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelHeight = newValue;
|
||||
|
||||
[self setFloat:_texelHeight forUniform:texelHeightUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setLowerThreshold:(CGFloat)newValue;
|
||||
{
|
||||
_lowerThreshold = newValue;
|
||||
|
||||
[self setFloat:_lowerThreshold forUniform:lowerThresholdUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setUpperThreshold:(CGFloat)newValue;
|
||||
{
|
||||
_upperThreshold = newValue;
|
||||
|
||||
[self setFloat:_upperThreshold forUniform:upperThresholdUniform program:filterProgram];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPUImage3x3TextureSamplingFilter.h"
|
||||
|
||||
@interface GPUImageDirectionalSobelEdgeDetectionFilter : GPUImage3x3TextureSamplingFilter
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,103 @@
|
||||
#import "GPUImageDirectionalSobelEdgeDetectionFilter.h"
|
||||
|
||||
@implementation GPUImageDirectionalSobelEdgeDetectionFilter
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDirectionalSobelEdgeDetectionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision mediump float;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float bottomLeftIntensity = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
|
||||
float topRightIntensity = texture2D(inputImageTexture, topRightTextureCoordinate).r;
|
||||
float topLeftIntensity = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
|
||||
float bottomRightIntensity = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
|
||||
float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
|
||||
float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
|
||||
float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
|
||||
float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
|
||||
|
||||
vec2 gradientDirection;
|
||||
gradientDirection.x = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity;
|
||||
gradientDirection.y = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity;
|
||||
|
||||
float gradientMagnitude = length(gradientDirection);
|
||||
vec2 normalizedDirection = normalize(gradientDirection);
|
||||
normalizedDirection = sign(normalizedDirection) * floor(abs(normalizedDirection) + 0.617316); // Offset by 1-sin(pi/8) to set to 0 if near axis, 1 if away
|
||||
normalizedDirection = (normalizedDirection + 1.0) * 0.5; // Place -1.0 - 1.0 within 0 - 1.0
|
||||
|
||||
gl_FragColor = vec4(gradientMagnitude, normalizedDirection.x, normalizedDirection.y, 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDirectionalSobelEdgeDetectionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float bottomLeftIntensity = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
|
||||
float topRightIntensity = texture2D(inputImageTexture, topRightTextureCoordinate).r;
|
||||
float topLeftIntensity = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
|
||||
float bottomRightIntensity = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
|
||||
float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
|
||||
float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
|
||||
float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
|
||||
float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
|
||||
|
||||
vec2 gradientDirection;
|
||||
gradientDirection.x = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity;
|
||||
gradientDirection.y = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity;
|
||||
|
||||
float gradientMagnitude = length(gradientDirection);
|
||||
vec2 normalizedDirection = normalize(gradientDirection);
|
||||
normalizedDirection = sign(normalizedDirection) * floor(abs(normalizedDirection) + 0.617316); // Offset by 1-sin(pi/8) to set to 0 if near axis, 1 if away
|
||||
normalizedDirection = (normalizedDirection + 1.0) * 0.5; // Place -1.0 - 1.0 within 0 - 1.0
|
||||
|
||||
gl_FragColor = vec4(gradientMagnitude, normalizedDirection.x, normalizedDirection.y, 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDirectionalSobelEdgeDetectionFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageDissolveBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
GLint mixUniform;
|
||||
}
|
||||
|
||||
// Mix ranges from 0.0 (only image 1) to 1.0 (only image 2), with 0.5 (half of either) as the normal level
|
||||
@property(readwrite, nonatomic) CGFloat mix;
|
||||
|
||||
@end
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#import "GPUImageDissolveBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDissolveBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
uniform lowp float mixturePercent;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
lowp vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = mix(textureColor, textureColor2, mixturePercent);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDissolveBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
uniform float mixturePercent;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = mix(textureColor, textureColor2, mixturePercent);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageDissolveBlendFilter
|
||||
|
||||
@synthesize mix = _mix;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDissolveBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
mixUniform = [filterProgram uniformIndex:@"mixturePercent"];
|
||||
self.mix = 0.5;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setMix:(CGFloat)newValue;
|
||||
{
|
||||
_mix = newValue;
|
||||
|
||||
[self setFloat:_mix forUniform:mixUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageDivideBlendFilter : GPUImageTwoInputFilter
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,96 @@
|
||||
#import "GPUImageDivideBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDivideBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
mediump vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
mediump float ra;
|
||||
if (overlay.a == 0.0 || ((base.r / overlay.r) > (base.a / overlay.a)))
|
||||
ra = overlay.a * base.a + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
else
|
||||
ra = (base.r * overlay.a * overlay.a) / overlay.r + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
|
||||
|
||||
mediump float ga;
|
||||
if (overlay.a == 0.0 || ((base.g / overlay.g) > (base.a / overlay.a)))
|
||||
ga = overlay.a * base.a + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
else
|
||||
ga = (base.g * overlay.a * overlay.a) / overlay.g + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
|
||||
|
||||
mediump float ba;
|
||||
if (overlay.a == 0.0 || ((base.b / overlay.b) > (base.a / overlay.a)))
|
||||
ba = overlay.a * base.a + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
else
|
||||
ba = (base.b * overlay.a * overlay.a) / overlay.b + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
|
||||
mediump float a = overlay.a + base.a - overlay.a * base.a;
|
||||
|
||||
gl_FragColor = vec4(ra, ga, ba, a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDivideBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
float ra;
|
||||
if (overlay.a == 0.0 || ((base.r / overlay.r) > (base.a / overlay.a)))
|
||||
ra = overlay.a * base.a + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
else
|
||||
ra = (base.r * overlay.a * overlay.a) / overlay.r + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
|
||||
|
||||
float ga;
|
||||
if (overlay.a == 0.0 || ((base.g / overlay.g) > (base.a / overlay.a)))
|
||||
ga = overlay.a * base.a + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
else
|
||||
ga = (base.g * overlay.a * overlay.a) / overlay.g + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
|
||||
|
||||
float ba;
|
||||
if (overlay.a == 0.0 || ((base.b / overlay.b) > (base.a / overlay.a)))
|
||||
ba = overlay.a * base.a + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
else
|
||||
ba = (base.b * overlay.a * overlay.a) / overlay.b + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
|
||||
float a = overlay.a + base.a - overlay.a * base.a;
|
||||
|
||||
gl_FragColor = vec4(ra, ga, ba, a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageDivideBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDivideBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#import "GPUImage3x3ConvolutionFilter.h"
|
||||
|
||||
@interface GPUImageEmbossFilter : GPUImage3x3ConvolutionFilter
|
||||
|
||||
// The strength of the embossing, from 0.0 to 4.0, with 1.0 as the normal level
|
||||
@property(readwrite, nonatomic) CGFloat intensity;
|
||||
|
||||
@end
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
#import "GPUImageEmbossFilter.h"
|
||||
|
||||
@implementation GPUImageEmbossFilter
|
||||
|
||||
@synthesize intensity = _intensity;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.intensity = 1.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setIntensity:(CGFloat)newValue;
|
||||
{
|
||||
// [(GPUImage3x3ConvolutionFilter *)filter setConvolutionMatrix:(GPUMatrix3x3){
|
||||
// {-2.0f, -1.0f, 0.0f},
|
||||
// {-1.0f, 1.0f, 1.0f},
|
||||
// { 0.0f, 1.0f, 2.0f}
|
||||
// }];
|
||||
|
||||
_intensity = newValue;
|
||||
|
||||
GPUMatrix3x3 newConvolutionMatrix;
|
||||
newConvolutionMatrix.one.one = _intensity * (-2.0);
|
||||
newConvolutionMatrix.one.two = -_intensity;
|
||||
newConvolutionMatrix.one.three = 0.0f;
|
||||
|
||||
newConvolutionMatrix.two.one = -_intensity;
|
||||
newConvolutionMatrix.two.two = 1.0;
|
||||
newConvolutionMatrix.two.three = _intensity;
|
||||
|
||||
newConvolutionMatrix.three.one = 0.0f;
|
||||
newConvolutionMatrix.three.two = _intensity;
|
||||
newConvolutionMatrix.three.three = _intensity * 2.0;
|
||||
|
||||
self.convolutionKernel = newConvolutionMatrix;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,11 @@
|
||||
#import "GPUImageTwoPassTextureSamplingFilter.h"
|
||||
|
||||
// For each pixel, this sets it to the minimum value of the red channel in a rectangular neighborhood extending out dilationRadius pixels from the center.
|
||||
// This extends out dark features, and is most commonly used with black-and-white thresholded images.
|
||||
|
||||
@interface GPUImageErosionFilter : GPUImageTwoPassTextureSamplingFilter
|
||||
|
||||
// Acceptable values for erosionRadius, which sets the distance in pixels to sample out from the center, are 1, 2, 3, and 4.
|
||||
- (id)initWithRadius:(NSUInteger)erosionRadius;
|
||||
|
||||
@end
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
#import "GPUImageErosionFilter.h"
|
||||
#import "GPUImageDilationFilter.h"
|
||||
|
||||
@implementation GPUImageErosionFilter
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageErosionRadiusOneFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusTwoFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusThreeFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
minValue = min(minValue, threeStepsPositiveIntensity);
|
||||
minValue = min(minValue, threeStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusFourFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
varying vec2 fourStepsPositiveTextureCoordinate;
|
||||
varying vec2 fourStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
float fourStepsPositiveIntensity = texture2D(inputImageTexture, fourStepsPositiveTextureCoordinate).r;
|
||||
float fourStepsNegativeIntensity = texture2D(inputImageTexture, fourStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
minValue = min(minValue, threeStepsPositiveIntensity);
|
||||
minValue = min(minValue, threeStepsNegativeIntensity);
|
||||
minValue = min(minValue, fourStepsPositiveIntensity);
|
||||
minValue = min(minValue, fourStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageErosionRadiusOneFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
|
||||
float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusTwoFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusThreeFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
minValue = min(minValue, threeStepsPositiveIntensity);
|
||||
minValue = min(minValue, threeStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusFourFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
varying vec2 fourStepsPositiveTextureCoordinate;
|
||||
varying vec2 fourStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
float fourStepsPositiveIntensity = texture2D(inputImageTexture, fourStepsPositiveTextureCoordinate).r;
|
||||
float fourStepsNegativeIntensity = texture2D(inputImageTexture, fourStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
minValue = min(minValue, threeStepsPositiveIntensity);
|
||||
minValue = min(minValue, threeStepsNegativeIntensity);
|
||||
minValue = min(minValue, fourStepsPositiveIntensity);
|
||||
minValue = min(minValue, fourStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithRadius:(NSUInteger)dilationRadius;
|
||||
{
|
||||
NSString *fragmentShaderForThisRadius = nil;
|
||||
NSString *vertexShaderForThisRadius = nil;
|
||||
|
||||
switch (dilationRadius)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusOneVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageErosionRadiusOneFragmentShaderString;
|
||||
}; break;
|
||||
case 2:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusTwoVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageErosionRadiusTwoFragmentShaderString;
|
||||
}; break;
|
||||
case 3:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusThreeVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageErosionRadiusThreeFragmentShaderString;
|
||||
}; break;
|
||||
case 4:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusFourVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageErosionRadiusFourFragmentShaderString;
|
||||
}; break;
|
||||
default:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusFourVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageErosionRadiusFourFragmentShaderString;
|
||||
}; break;
|
||||
}
|
||||
|
||||
if (!(self = [super initWithFirstStageVertexShaderFromString:vertexShaderForThisRadius firstStageFragmentShaderFromString:fragmentShaderForThisRadius secondStageVertexShaderFromString:vertexShaderForThisRadius secondStageFragmentShaderFromString:fragmentShaderForThisRadius]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithRadius:1]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageExclusionBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
#import "GPUImageExclusionBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageExclusionBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
mediump vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
// Dca = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
|
||||
|
||||
gl_FragColor = vec4((overlay.rgb * base.a + base.rgb * overlay.a - 2.0 * overlay.rgb * base.rgb) + overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a), base.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageExclusionBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
// Dca = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
|
||||
|
||||
gl_FragColor = vec4((overlay.rgb * base.a + base.rgb * overlay.a - 2.0 * overlay.rgb * base.rgb) + overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a), base.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageExclusionBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageExclusionBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageExposureFilter : GPUImageFilter
|
||||
{
|
||||
GLint exposureUniform;
|
||||
}
|
||||
|
||||
// Exposure ranges from -10.0 to 10.0, with 0.0 as the normal level
|
||||
@property(readwrite, nonatomic) CGFloat exposure;
|
||||
|
||||
@end
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#import "GPUImageExposureFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageExposureFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform highp float exposure;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4(textureColor.rgb * pow(2.0, exposure), textureColor.w);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageExposureFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform float exposure;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4(textureColor.rgb * pow(2.0, exposure), textureColor.w);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageExposureFilter
|
||||
|
||||
@synthesize exposure = _exposure;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageExposureFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
exposureUniform = [filterProgram uniformIndex:@"exposure"];
|
||||
self.exposure = 0.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setExposure:(CGFloat)newValue;
|
||||
{
|
||||
_exposure = newValue;
|
||||
|
||||
[self setFloat:_exposure forUniform:exposureUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@class GPUImageGrayscaleFilter;
|
||||
@class GPUImage3x3TextureSamplingFilter;
|
||||
@class GPUImageNonMaximumSuppressionFilter;
|
||||
|
||||
/*
|
||||
An implementation of the Features from Accelerated Segment Test (FAST) feature detector as described in the following publications:
|
||||
|
||||
E. Rosten and T. Drummond. Fusing points and lines for high performance tracking. IEEE International Conference on Computer Vision, 2005.
|
||||
E. Rosten and T. Drummond. Machine learning for high-speed corner detection. European Conference on Computer Vision, 2006.
|
||||
|
||||
For more about the FAST feature detector, see the resources here:
|
||||
http://www.edwardrosten.com/work/fast.html
|
||||
*/
|
||||
|
||||
typedef enum { kGPUImageFAST12Contiguous, kGPUImageFAST12ContiguousNonMaximumSuppressed} GPUImageFASTDetectorType;
|
||||
|
||||
@interface GPUImageFASTCornerDetectionFilter : GPUImageFilterGroup
|
||||
{
|
||||
GPUImageGrayscaleFilter *luminanceReductionFilter;
|
||||
GPUImage3x3TextureSamplingFilter *featureDetectionFilter;
|
||||
GPUImageNonMaximumSuppressionFilter *nonMaximumSuppressionFilter;
|
||||
// Generate a lookup texture based on the bit patterns
|
||||
|
||||
// Step 1: convert to monochrome if necessary
|
||||
// Step 2: do a lookup at each pixel based on the Bresenham circle, encode comparison in two color components
|
||||
// Step 3: do non-maximum suppression of close corner points
|
||||
}
|
||||
|
||||
- (id)initWithFASTDetectorVariant:(GPUImageFASTDetectorType)detectorType;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,89 @@
|
||||
#import "GPUImageFASTCornerDetectionFilter.h"
|
||||
|
||||
#import "GPUImageGrayscaleFilter.h"
|
||||
#import "GPUImage3x3TextureSamplingFilter.h"
|
||||
#import "GPUImageNonMaximumSuppressionFilter.h"
|
||||
|
||||
// 14 total texture coordinates from vertex shader for non-dependent reads
|
||||
// 3 texture coordinates for dependent reads, then
|
||||
|
||||
NSString *const kGPUImageFASTDetectorFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D lookupTable;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp float centerIntensity = texture2D(inputImageTexture, textureCoordinate).r;
|
||||
lowp float bottomLeftIntensity = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
|
||||
lowp float topRightIntensity = texture2D(inputImageTexture, topRightTextureCoordinate).r;
|
||||
lowp float topLeftIntensity = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
|
||||
lowp float bottomRightIntensity = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
|
||||
lowp float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
|
||||
lowp float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
|
||||
lowp float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
|
||||
lowp float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
|
||||
|
||||
lowp float byteTally = 1.0 / 255.0 * step(centerIntensity, topRightIntensity);
|
||||
byteTally += 2.0 / 255.0 * step(centerIntensity, topIntensity);
|
||||
byteTally += 4.0 / 255.0 * step(centerIntensity, topLeftIntensity);
|
||||
byteTally += 8.0 / 255.0 * step(centerIntensity, leftIntensity);
|
||||
byteTally += 16.0 / 255.0 * step(centerIntensity, bottomLeftIntensity);
|
||||
byteTally += 32.0 / 255.0 * step(centerIntensity, bottomIntensity);
|
||||
byteTally += 64.0 / 255.0 * step(centerIntensity, bottomRightIntensity);
|
||||
byteTally += 128.0 / 255.0 * step(centerIntensity, rightIntensity);
|
||||
|
||||
// TODO: Replace the above with a dot product and two vec4s
|
||||
// TODO: Apply step to a matrix, rather than individually
|
||||
|
||||
gl_FragColor = vec4(byteTally, byteTally, byteTally, 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@implementation GPUImageFASTCornerDetectionFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithFASTDetectorVariant:kGPUImageFAST12ContiguousNonMaximumSuppressed]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFASTDetectorVariant:(GPUImageFASTDetectorType)detectorType;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// [derivativeFilter addTarget:blurFilter];
|
||||
// [blurFilter addTarget:harrisCornerDetectionFilter];
|
||||
// [harrisCornerDetectionFilter addTarget:nonMaximumSuppressionFilter];
|
||||
// [simpleThresholdFilter addTarget:colorPackingFilter];
|
||||
|
||||
// self.initialFilters = [NSArray arrayWithObjects:derivativeFilter, nil];
|
||||
// self.terminalFilter = colorPackingFilter;
|
||||
// self.terminalFilter = nonMaximumSuppressionFilter;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageFalseColorFilter : GPUImageFilter
|
||||
{
|
||||
GLint firstColorUniform, secondColorUniform;
|
||||
}
|
||||
|
||||
// The first and second colors specify what colors replace the dark and light areas of the image, respectively. The defaults are (0.0, 0.0, 0.5) amd (1.0, 0.0, 0.0).
|
||||
@property(readwrite, nonatomic) GPUVector4 firstColor;
|
||||
@property(readwrite, nonatomic) GPUVector4 secondColor;
|
||||
|
||||
- (void)setFirstColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
- (void)setSecondColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,101 @@
|
||||
#import "GPUImageFalseColorFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUFalseColorFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform float intensity;
|
||||
uniform vec3 firstColor;
|
||||
uniform vec3 secondColor;
|
||||
|
||||
const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
float luminance = dot(textureColor.rgb, luminanceWeighting);
|
||||
|
||||
gl_FragColor = vec4( mix(firstColor.rgb, secondColor.rgb, luminance), textureColor.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUFalseColorFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform float intensity;
|
||||
uniform vec3 firstColor;
|
||||
uniform vec3 secondColor;
|
||||
|
||||
const vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
float luminance = dot(textureColor.rgb, luminanceWeighting);
|
||||
|
||||
gl_FragColor = vec4( mix(firstColor.rgb, secondColor.rgb, luminance), textureColor.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
@implementation GPUImageFalseColorFilter
|
||||
|
||||
@synthesize secondColor = _secondColor;
|
||||
@synthesize firstColor = _firstColor;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUFalseColorFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
firstColorUniform = [filterProgram uniformIndex:@"firstColor"];
|
||||
secondColorUniform = [filterProgram uniformIndex:@"secondColor"];
|
||||
|
||||
self.firstColor = (GPUVector4){0.0f, 0.0f, 0.5f, 1.0f};
|
||||
self.secondColor = (GPUVector4){1.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setFirstColor:(GPUVector4)newValue;
|
||||
{
|
||||
_firstColor = newValue;
|
||||
|
||||
[self setFirstColorRed:_firstColor.one green:_firstColor.two blue:_firstColor.three];
|
||||
}
|
||||
|
||||
- (void)setSecondColor:(GPUVector4)newValue;
|
||||
{
|
||||
_secondColor = newValue;
|
||||
|
||||
[self setSecondColorRed:_secondColor.one green:_secondColor.two blue:_secondColor.three];
|
||||
}
|
||||
|
||||
- (void)setFirstColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
{
|
||||
GPUVector3 firstColor = {redComponent, greenComponent, blueComponent};
|
||||
|
||||
[self setVec3:firstColor forUniform:firstColorUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setSecondColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
{
|
||||
GPUVector3 secondColor = {redComponent, greenComponent, blueComponent};
|
||||
|
||||
[self setVec3:secondColor forUniform:secondColorUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
#import "GPUImageOutput.h"
|
||||
|
||||
#define STRINGIZE(x) #x
|
||||
#define STRINGIZE2(x) STRINGIZE(x)
|
||||
#define SHADER_STRING(text) @ STRINGIZE2(text)
|
||||
|
||||
#define GPUImageHashIdentifier #
|
||||
#define GPUImageWrappedLabel(x) x
|
||||
#define GPUImageEscapedHashIdentifier(a) GPUImageWrappedLabel(GPUImageHashIdentifier)a
|
||||
|
||||
extern NSString *const kGPUImageVertexShaderString;
|
||||
extern NSString *const kGPUImagePassthroughFragmentShaderString;
|
||||
|
||||
struct GPUVector4 {
|
||||
GLfloat one;
|
||||
GLfloat two;
|
||||
GLfloat three;
|
||||
GLfloat four;
|
||||
};
|
||||
typedef struct GPUVector4 GPUVector4;
|
||||
|
||||
struct GPUVector3 {
|
||||
GLfloat one;
|
||||
GLfloat two;
|
||||
GLfloat three;
|
||||
};
|
||||
typedef struct GPUVector3 GPUVector3;
|
||||
|
||||
struct GPUMatrix4x4 {
|
||||
GPUVector4 one;
|
||||
GPUVector4 two;
|
||||
GPUVector4 three;
|
||||
GPUVector4 four;
|
||||
};
|
||||
typedef struct GPUMatrix4x4 GPUMatrix4x4;
|
||||
|
||||
struct GPUMatrix3x3 {
|
||||
GPUVector3 one;
|
||||
GPUVector3 two;
|
||||
GPUVector3 three;
|
||||
};
|
||||
typedef struct GPUMatrix3x3 GPUMatrix3x3;
|
||||
|
||||
/** GPUImage's base filter class
|
||||
|
||||
Filters and other subsequent elements in the chain conform to the GPUImageInput protocol, which lets them take in the supplied or processed texture from the previous link in the chain and do something with it. Objects one step further down the chain are considered targets, and processing can be branched by adding multiple targets to a single output or filter.
|
||||
*/
|
||||
@interface GPUImageFilter : GPUImageOutput <GPUImageInput>
|
||||
{
|
||||
GPUImageFramebuffer *firstInputFramebuffer;
|
||||
|
||||
GLProgram *filterProgram;
|
||||
GLint filterPositionAttribute, filterTextureCoordinateAttribute;
|
||||
GLint filterInputTextureUniform;
|
||||
GLfloat backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha;
|
||||
|
||||
BOOL isEndProcessing;
|
||||
|
||||
CGSize currentFilterSize;
|
||||
GPUImageRotationMode inputRotation;
|
||||
|
||||
BOOL currentlyReceivingMonochromeInput;
|
||||
|
||||
NSMutableDictionary *uniformStateRestorationBlocks;
|
||||
dispatch_semaphore_t imageCaptureSemaphore;
|
||||
}
|
||||
|
||||
@property(readonly) CVPixelBufferRef renderTarget;
|
||||
@property(readwrite, nonatomic) BOOL preventRendering;
|
||||
@property(readwrite, nonatomic) BOOL currentlyReceivingMonochromeInput;
|
||||
|
||||
/// @name Initialization and teardown
|
||||
|
||||
/**
|
||||
Initialize with vertex and fragment shaders
|
||||
|
||||
You make take advantage of the SHADER_STRING macro to write your shaders in-line.
|
||||
@param vertexShaderString Source code of the vertex shader to use
|
||||
@param fragmentShaderString Source code of the fragment shader to use
|
||||
*/
|
||||
- (id)initWithVertexShaderFromString:(NSString *)vertexShaderString fragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
|
||||
/**
|
||||
Initialize with a fragment shader
|
||||
|
||||
You may take advantage of the SHADER_STRING macro to write your shader in-line.
|
||||
@param fragmentShaderString Source code of fragment shader to use
|
||||
*/
|
||||
- (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
/**
|
||||
Initialize with a fragment shader
|
||||
@param fragmentShaderFilename Filename of fragment shader to load
|
||||
*/
|
||||
- (id)initWithFragmentShaderFromFile:(NSString *)fragmentShaderFilename;
|
||||
- (void)initializeAttributes;
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
- (CGSize)rotatedSize:(CGSize)sizeToRotate forIndex:(NSInteger)textureIndex;
|
||||
- (CGPoint)rotatedPoint:(CGPoint)pointToRotate forRotation:(GPUImageRotationMode)rotation;
|
||||
|
||||
/// @name Managing the display FBOs
|
||||
/** Size of the frame buffer object
|
||||
*/
|
||||
- (CGSize)sizeOfFBO;
|
||||
|
||||
/// @name Rendering
|
||||
+ (const GLfloat *)textureCoordinatesForRotation:(GPUImageRotationMode)rotationMode;
|
||||
- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
|
||||
- (void)informTargetsAboutNewFrameAtTime:(CMTime)frameTime;
|
||||
- (CGSize)outputFrameSize;
|
||||
|
||||
/// @name Input parameters
|
||||
- (void)setBackgroundColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent alpha:(GLfloat)alphaComponent;
|
||||
- (void)setInteger:(GLint)newInteger forUniformName:(NSString *)uniformName;
|
||||
- (void)setFloat:(GLfloat)newFloat forUniformName:(NSString *)uniformName;
|
||||
- (void)setSize:(CGSize)newSize forUniformName:(NSString *)uniformName;
|
||||
- (void)setPoint:(CGPoint)newPoint forUniformName:(NSString *)uniformName;
|
||||
- (void)setFloatVec3:(GPUVector3)newVec3 forUniformName:(NSString *)uniformName;
|
||||
- (void)setFloatVec4:(GPUVector4)newVec4 forUniform:(NSString *)uniformName;
|
||||
- (void)setFloatArray:(GLfloat *)array length:(GLsizei)count forUniform:(NSString*)uniformName;
|
||||
|
||||
- (void)setMatrix3f:(GPUMatrix3x3)matrix forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
- (void)setMatrix4f:(GPUMatrix4x4)matrix forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
- (void)setFloat:(GLfloat)floatValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
- (void)setPoint:(CGPoint)pointValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
- (void)setSize:(CGSize)sizeValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
- (void)setVec3:(GPUVector3)vectorValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
- (void)setVec4:(GPUVector4)vectorValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
- (void)setFloatArray:(GLfloat *)arrayValue length:(GLsizei)arrayLength forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
- (void)setInteger:(GLint)intValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
|
||||
- (void)setAndExecuteUniformStateCallbackAtIndex:(GLint)uniform forProgram:(GLProgram *)shaderProgram toBlock:(dispatch_block_t)uniformStateBlock;
|
||||
- (void)setUniformsForProgramAtIndex:(NSUInteger)programIndex;
|
||||
|
||||
@end
|
||||
+753
@@ -0,0 +1,753 @@
|
||||
#import "GPUImageFilter.h"
|
||||
#import "GPUImagePicture.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
// Hardcode the vertex shader for standard filters, but this can be overridden
|
||||
NSString *const kGPUImageVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec4 inputTextureCoordinate;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
textureCoordinate = inputTextureCoordinate.xy;
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
|
||||
NSString *const kGPUImagePassthroughFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
}
|
||||
);
|
||||
|
||||
#else
|
||||
|
||||
NSString *const kGPUImagePassthroughFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
@implementation GPUImageFilter
|
||||
|
||||
@synthesize preventRendering = _preventRendering;
|
||||
@synthesize currentlyReceivingMonochromeInput;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithVertexShaderFromString:(NSString *)vertexShaderString fragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
uniformStateRestorationBlocks = [NSMutableDictionary dictionaryWithCapacity:10];
|
||||
_preventRendering = NO;
|
||||
currentlyReceivingMonochromeInput = NO;
|
||||
inputRotation = kGPUImageNoRotation;
|
||||
backgroundColorRed = 0.0;
|
||||
backgroundColorGreen = 0.0;
|
||||
backgroundColorBlue = 0.0;
|
||||
backgroundColorAlpha = 0.0;
|
||||
imageCaptureSemaphore = dispatch_semaphore_create(0);
|
||||
dispatch_semaphore_signal(imageCaptureSemaphore);
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext useImageProcessingContext];
|
||||
|
||||
filterProgram = [[GPUImageContext sharedImageProcessingContext] programForVertexShaderString:vertexShaderString fragmentShaderString:fragmentShaderString];
|
||||
|
||||
if (!filterProgram.initialized)
|
||||
{
|
||||
[self initializeAttributes];
|
||||
|
||||
if (![filterProgram link])
|
||||
{
|
||||
NSString *progLog = [filterProgram programLog];
|
||||
NSLog(@"Program link log: %@", progLog);
|
||||
NSString *fragLog = [filterProgram fragmentShaderLog];
|
||||
NSLog(@"Fragment shader compile log: %@", fragLog);
|
||||
NSString *vertLog = [filterProgram vertexShaderLog];
|
||||
NSLog(@"Vertex shader compile log: %@", vertLog);
|
||||
filterProgram = nil;
|
||||
NSAssert(NO, @"Filter shader link failed");
|
||||
}
|
||||
}
|
||||
|
||||
filterPositionAttribute = [filterProgram attributeIndex:@"position"];
|
||||
filterTextureCoordinateAttribute = [filterProgram attributeIndex:@"inputTextureCoordinate"];
|
||||
filterInputTextureUniform = [filterProgram uniformIndex:@"inputImageTexture"]; // This does assume a name of "inputImageTexture" for the fragment shader
|
||||
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
|
||||
glEnableVertexAttribArray(filterPositionAttribute);
|
||||
glEnableVertexAttribArray(filterTextureCoordinateAttribute);
|
||||
});
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
{
|
||||
if (!(self = [self initWithVertexShaderFromString:kGPUImageVertexShaderString fragmentShaderFromString:fragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFragmentShaderFromFile:(NSString *)fragmentShaderFilename;
|
||||
{
|
||||
NSString *fragmentShaderPathname = [[NSBundle mainBundle] pathForResource:fragmentShaderFilename ofType:@"fsh"];
|
||||
NSString *fragmentShaderString = [NSString stringWithContentsOfFile:fragmentShaderPathname encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
if (!(self = [self initWithFragmentShaderFromString:fragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithFragmentShaderFromString:kGPUImagePassthroughFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initializeAttributes;
|
||||
{
|
||||
[filterProgram addAttribute:@"position"];
|
||||
[filterProgram addAttribute:@"inputTextureCoordinate"];
|
||||
|
||||
// Override this, calling back to this super method, in order to add new attributes to your vertex shader
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
// This is where you can override to provide some custom setup, if your filter has a size-dependent element
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
if (imageCaptureSemaphore != NULL)
|
||||
{
|
||||
dispatch_release(imageCaptureSemaphore);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Still image processing
|
||||
|
||||
- (void)useNextFrameForImageCapture;
|
||||
{
|
||||
usingNextFrameForImageCapture = YES;
|
||||
|
||||
// Set the semaphore high, if it isn't already
|
||||
if (dispatch_semaphore_wait(imageCaptureSemaphore, DISPATCH_TIME_NOW) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (CGImageRef)newCGImageFromCurrentlyProcessedOutput
|
||||
{
|
||||
// Give it three seconds to process, then abort if they forgot to set up the image capture properly
|
||||
double timeoutForImageCapture = 3.0;
|
||||
dispatch_time_t convertedTimeout = dispatch_time(DISPATCH_TIME_NOW, timeoutForImageCapture * NSEC_PER_SEC);
|
||||
|
||||
if (dispatch_semaphore_wait(imageCaptureSemaphore, convertedTimeout) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GPUImageFramebuffer* framebuffer = [self framebufferForOutput];
|
||||
|
||||
usingNextFrameForImageCapture = NO;
|
||||
dispatch_semaphore_signal(imageCaptureSemaphore);
|
||||
|
||||
CGImageRef image = [framebuffer newCGImageFromFramebufferContents];
|
||||
return image;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Managing the display FBOs
|
||||
|
||||
- (CGSize)sizeOfFBO;
|
||||
{
|
||||
CGSize outputSize = [self maximumOutputSize];
|
||||
if ( (CGSizeEqualToSize(outputSize, CGSizeZero)) || (inputTextureSize.width < outputSize.width) )
|
||||
{
|
||||
return inputTextureSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
return outputSize;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Rendering
|
||||
|
||||
+ (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 rotateLeftTextureCoordinates[] = {
|
||||
1.0f, 0.0f,
|
||||
1.0f, 1.0f,
|
||||
0.0f, 0.0f,
|
||||
0.0f, 1.0f,
|
||||
};
|
||||
|
||||
static const GLfloat rotateRightTextureCoordinates[] = {
|
||||
0.0f, 1.0f,
|
||||
0.0f, 0.0f,
|
||||
1.0f, 1.0f,
|
||||
1.0f, 0.0f,
|
||||
};
|
||||
|
||||
static const GLfloat verticalFlipTextureCoordinates[] = {
|
||||
0.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
0.0f, 0.0f,
|
||||
1.0f, 0.0f,
|
||||
};
|
||||
|
||||
static const GLfloat horizontalFlipTextureCoordinates[] = {
|
||||
1.0f, 0.0f,
|
||||
0.0f, 0.0f,
|
||||
1.0f, 1.0f,
|
||||
0.0f, 1.0f,
|
||||
};
|
||||
|
||||
static const GLfloat rotateRightVerticalFlipTextureCoordinates[] = {
|
||||
0.0f, 0.0f,
|
||||
0.0f, 1.0f,
|
||||
1.0f, 0.0f,
|
||||
1.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, 1.0f,
|
||||
0.0f, 1.0f,
|
||||
1.0f, 0.0f,
|
||||
0.0f, 0.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;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
|
||||
{
|
||||
if (self.preventRendering)
|
||||
{
|
||||
[firstInputFramebuffer unlock];
|
||||
return;
|
||||
}
|
||||
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
|
||||
outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:[self sizeOfFBO] textureOptions:self.outputTextureOptions onlyTexture:NO];
|
||||
[outputFramebuffer activateFramebuffer];
|
||||
if (usingNextFrameForImageCapture)
|
||||
{
|
||||
[outputFramebuffer lock];
|
||||
}
|
||||
|
||||
[self setUniformsForProgramAtIndex:0];
|
||||
|
||||
glClearColor(backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, [firstInputFramebuffer texture]);
|
||||
|
||||
glUniform1i(filterInputTextureUniform, 2);
|
||||
|
||||
glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
|
||||
glVertexAttribPointer(filterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
[firstInputFramebuffer unlock];
|
||||
|
||||
if (usingNextFrameForImageCapture)
|
||||
{
|
||||
dispatch_semaphore_signal(imageCaptureSemaphore);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)informTargetsAboutNewFrameAtTime:(CMTime)frameTime;
|
||||
{
|
||||
if (self.frameProcessingCompletionBlock != NULL)
|
||||
{
|
||||
self.frameProcessingCompletionBlock(self, frameTime);
|
||||
}
|
||||
|
||||
// Get all targets the framebuffer so they can grab a lock on it
|
||||
for (id<GPUImageInput> currentTarget in targets)
|
||||
{
|
||||
if (currentTarget != self.targetToIgnoreForUpdates)
|
||||
{
|
||||
NSInteger indexOfObject = [targets indexOfObject:currentTarget];
|
||||
NSInteger textureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
|
||||
|
||||
[self setInputFramebufferForTarget:currentTarget atIndex:textureIndex];
|
||||
[currentTarget setInputSize:[self outputFrameSize] atIndex:textureIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// Release our hold so it can return to the cache immediately upon processing
|
||||
[[self framebufferForOutput] unlock];
|
||||
|
||||
if (usingNextFrameForImageCapture)
|
||||
{
|
||||
// usingNextFrameForImageCapture = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self removeOutputFramebuffer];
|
||||
}
|
||||
|
||||
// Trigger processing last, so that our unlock comes first in serial execution, avoiding the need for a callback
|
||||
for (id<GPUImageInput> currentTarget in targets)
|
||||
{
|
||||
if (currentTarget != self.targetToIgnoreForUpdates)
|
||||
{
|
||||
NSInteger indexOfObject = [targets indexOfObject:currentTarget];
|
||||
NSInteger textureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
|
||||
[currentTarget newFrameReadyAtTime:frameTime atIndex:textureIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CGSize)outputFrameSize;
|
||||
{
|
||||
return inputTextureSize;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Input parameters
|
||||
|
||||
- (void)setBackgroundColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent alpha:(GLfloat)alphaComponent;
|
||||
{
|
||||
backgroundColorRed = redComponent;
|
||||
backgroundColorGreen = greenComponent;
|
||||
backgroundColorBlue = blueComponent;
|
||||
backgroundColorAlpha = alphaComponent;
|
||||
}
|
||||
|
||||
- (void)setInteger:(GLint)newInteger forUniformName:(NSString *)uniformName;
|
||||
{
|
||||
GLint uniformIndex = [filterProgram uniformIndex:uniformName];
|
||||
[self setInteger:newInteger forUniform:uniformIndex program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setFloat:(GLfloat)newFloat forUniformName:(NSString *)uniformName;
|
||||
{
|
||||
GLint uniformIndex = [filterProgram uniformIndex:uniformName];
|
||||
[self setFloat:newFloat forUniform:uniformIndex program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setSize:(CGSize)newSize forUniformName:(NSString *)uniformName;
|
||||
{
|
||||
GLint uniformIndex = [filterProgram uniformIndex:uniformName];
|
||||
[self setSize:newSize forUniform:uniformIndex program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setPoint:(CGPoint)newPoint forUniformName:(NSString *)uniformName;
|
||||
{
|
||||
GLint uniformIndex = [filterProgram uniformIndex:uniformName];
|
||||
[self setPoint:newPoint forUniform:uniformIndex program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setFloatVec3:(GPUVector3)newVec3 forUniformName:(NSString *)uniformName;
|
||||
{
|
||||
GLint uniformIndex = [filterProgram uniformIndex:uniformName];
|
||||
[self setVec3:newVec3 forUniform:uniformIndex program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setFloatVec4:(GPUVector4)newVec4 forUniform:(NSString *)uniformName;
|
||||
{
|
||||
GLint uniformIndex = [filterProgram uniformIndex:uniformName];
|
||||
[self setVec4:newVec4 forUniform:uniformIndex program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setFloatArray:(GLfloat *)array length:(GLsizei)count forUniform:(NSString*)uniformName
|
||||
{
|
||||
GLint uniformIndex = [filterProgram uniformIndex:uniformName];
|
||||
|
||||
[self setFloatArray:array length:count forUniform:uniformIndex program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setMatrix3f:(GPUMatrix3x3)matrix forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
{
|
||||
runAsynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:shaderProgram];
|
||||
[self setAndExecuteUniformStateCallbackAtIndex:uniform forProgram:shaderProgram toBlock:^{
|
||||
glUniformMatrix3fv(uniform, 1, GL_FALSE, (GLfloat *)&matrix);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setMatrix4f:(GPUMatrix4x4)matrix forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
{
|
||||
runAsynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:shaderProgram];
|
||||
[self setAndExecuteUniformStateCallbackAtIndex:uniform forProgram:shaderProgram toBlock:^{
|
||||
glUniformMatrix4fv(uniform, 1, GL_FALSE, (GLfloat *)&matrix);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setFloat:(GLfloat)floatValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
{
|
||||
runAsynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:shaderProgram];
|
||||
[self setAndExecuteUniformStateCallbackAtIndex:uniform forProgram:shaderProgram toBlock:^{
|
||||
glUniform1f(uniform, floatValue);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setPoint:(CGPoint)pointValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
{
|
||||
runAsynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:shaderProgram];
|
||||
[self setAndExecuteUniformStateCallbackAtIndex:uniform forProgram:shaderProgram toBlock:^{
|
||||
GLfloat positionArray[2];
|
||||
positionArray[0] = pointValue.x;
|
||||
positionArray[1] = pointValue.y;
|
||||
|
||||
glUniform2fv(uniform, 1, positionArray);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setSize:(CGSize)sizeValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
{
|
||||
runAsynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:shaderProgram];
|
||||
|
||||
[self setAndExecuteUniformStateCallbackAtIndex:uniform forProgram:shaderProgram toBlock:^{
|
||||
GLfloat sizeArray[2];
|
||||
sizeArray[0] = sizeValue.width;
|
||||
sizeArray[1] = sizeValue.height;
|
||||
|
||||
glUniform2fv(uniform, 1, sizeArray);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setVec3:(GPUVector3)vectorValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
{
|
||||
runAsynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:shaderProgram];
|
||||
|
||||
[self setAndExecuteUniformStateCallbackAtIndex:uniform forProgram:shaderProgram toBlock:^{
|
||||
glUniform3fv(uniform, 1, (GLfloat *)&vectorValue);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setVec4:(GPUVector4)vectorValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
{
|
||||
runAsynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:shaderProgram];
|
||||
|
||||
[self setAndExecuteUniformStateCallbackAtIndex:uniform forProgram:shaderProgram toBlock:^{
|
||||
glUniform4fv(uniform, 1, (GLfloat *)&vectorValue);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setFloatArray:(GLfloat *)arrayValue length:(GLsizei)arrayLength forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
{
|
||||
// Make a copy of the data, so it doesn't get overwritten before async call executes
|
||||
NSData* arrayData = [NSData dataWithBytes:arrayValue length:arrayLength * sizeof(arrayValue[0])];
|
||||
|
||||
runAsynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:shaderProgram];
|
||||
|
||||
[self setAndExecuteUniformStateCallbackAtIndex:uniform forProgram:shaderProgram toBlock:^{
|
||||
glUniform1fv(uniform, arrayLength, [arrayData bytes]);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setInteger:(GLint)intValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
|
||||
{
|
||||
runAsynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:shaderProgram];
|
||||
|
||||
[self setAndExecuteUniformStateCallbackAtIndex:uniform forProgram:shaderProgram toBlock:^{
|
||||
glUniform1i(uniform, intValue);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setAndExecuteUniformStateCallbackAtIndex:(GLint)uniform forProgram:(GLProgram *)shaderProgram toBlock:(dispatch_block_t)uniformStateBlock;
|
||||
{
|
||||
[uniformStateRestorationBlocks setObject:[uniformStateBlock copy] forKey:[NSNumber numberWithInt:uniform]];
|
||||
uniformStateBlock();
|
||||
}
|
||||
|
||||
- (void)setUniformsForProgramAtIndex:(NSUInteger)programIndex;
|
||||
{
|
||||
[uniformStateRestorationBlocks enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
|
||||
dispatch_block_t currentBlock = obj;
|
||||
currentBlock();
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark GPUImageInput
|
||||
|
||||
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
static const GLfloat imageVertices[] = {
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
};
|
||||
|
||||
[self renderToTextureWithVertices:imageVertices textureCoordinates:[[self class] textureCoordinatesForRotation:inputRotation]];
|
||||
|
||||
[self informTargetsAboutNewFrameAtTime:frameTime];
|
||||
}
|
||||
|
||||
- (NSInteger)nextAvailableTextureIndex;
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
firstInputFramebuffer = newInputFramebuffer;
|
||||
[firstInputFramebuffer lock];
|
||||
}
|
||||
|
||||
- (CGSize)rotatedSize:(CGSize)sizeToRotate forIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
CGSize rotatedSize = sizeToRotate;
|
||||
|
||||
if (GPUImageRotationSwapsWidthAndHeight(inputRotation))
|
||||
{
|
||||
rotatedSize.width = sizeToRotate.height;
|
||||
rotatedSize.height = sizeToRotate.width;
|
||||
}
|
||||
|
||||
return rotatedSize;
|
||||
}
|
||||
|
||||
- (CGPoint)rotatedPoint:(CGPoint)pointToRotate forRotation:(GPUImageRotationMode)rotation;
|
||||
{
|
||||
CGPoint rotatedPoint;
|
||||
switch(rotation)
|
||||
{
|
||||
case kGPUImageNoRotation: return pointToRotate; break;
|
||||
case kGPUImageFlipHorizonal:
|
||||
{
|
||||
rotatedPoint.x = 1.0 - pointToRotate.x;
|
||||
rotatedPoint.y = pointToRotate.y;
|
||||
}; break;
|
||||
case kGPUImageFlipVertical:
|
||||
{
|
||||
rotatedPoint.x = pointToRotate.x;
|
||||
rotatedPoint.y = 1.0 - pointToRotate.y;
|
||||
}; break;
|
||||
case kGPUImageRotateLeft:
|
||||
{
|
||||
rotatedPoint.x = 1.0 - pointToRotate.y;
|
||||
rotatedPoint.y = pointToRotate.x;
|
||||
}; break;
|
||||
case kGPUImageRotateRight:
|
||||
{
|
||||
rotatedPoint.x = pointToRotate.y;
|
||||
rotatedPoint.y = 1.0 - pointToRotate.x;
|
||||
}; break;
|
||||
case kGPUImageRotateRightFlipVertical:
|
||||
{
|
||||
rotatedPoint.x = pointToRotate.y;
|
||||
rotatedPoint.y = pointToRotate.x;
|
||||
}; break;
|
||||
case kGPUImageRotateRightFlipHorizontal:
|
||||
{
|
||||
rotatedPoint.x = 1.0 - pointToRotate.y;
|
||||
rotatedPoint.y = 1.0 - pointToRotate.x;
|
||||
}; break;
|
||||
case kGPUImageRotate180:
|
||||
{
|
||||
rotatedPoint.x = 1.0 - pointToRotate.x;
|
||||
rotatedPoint.y = 1.0 - pointToRotate.y;
|
||||
}; break;
|
||||
}
|
||||
|
||||
return rotatedPoint;
|
||||
}
|
||||
|
||||
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
if (self.preventRendering)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (overrideInputSize)
|
||||
{
|
||||
if (CGSizeEqualToSize(forcedMaximumSize, CGSizeZero))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
CGRect insetRect = AVMakeRectWithAspectRatioInsideRect(newSize, CGRectMake(0.0, 0.0, forcedMaximumSize.width, forcedMaximumSize.height));
|
||||
inputTextureSize = insetRect.size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CGSize rotatedSize = [self rotatedSize:newSize forIndex:textureIndex];
|
||||
|
||||
if (CGSizeEqualToSize(rotatedSize, CGSizeZero))
|
||||
{
|
||||
inputTextureSize = rotatedSize;
|
||||
}
|
||||
else if (!CGSizeEqualToSize(inputTextureSize, rotatedSize))
|
||||
{
|
||||
inputTextureSize = rotatedSize;
|
||||
}
|
||||
}
|
||||
|
||||
[self setupFilterForSize:[self sizeOfFBO]];
|
||||
}
|
||||
|
||||
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
inputRotation = newInputRotation;
|
||||
}
|
||||
|
||||
- (void)forceProcessingAtSize:(CGSize)frameSize;
|
||||
{
|
||||
if (CGSizeEqualToSize(frameSize, CGSizeZero))
|
||||
{
|
||||
overrideInputSize = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
overrideInputSize = YES;
|
||||
inputTextureSize = frameSize;
|
||||
forcedMaximumSize = CGSizeZero;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forceProcessingAtSizeRespectingAspectRatio:(CGSize)frameSize;
|
||||
{
|
||||
if (CGSizeEqualToSize(frameSize, CGSizeZero))
|
||||
{
|
||||
overrideInputSize = NO;
|
||||
inputTextureSize = CGSizeZero;
|
||||
forcedMaximumSize = CGSizeZero;
|
||||
}
|
||||
else
|
||||
{
|
||||
overrideInputSize = YES;
|
||||
forcedMaximumSize = frameSize;
|
||||
}
|
||||
}
|
||||
|
||||
- (CGSize)maximumOutputSize;
|
||||
{
|
||||
// I'm temporarily disabling adjustments for smaller output sizes until I figure out how to make this work better
|
||||
return CGSizeZero;
|
||||
|
||||
/*
|
||||
if (CGSizeEqualToSize(cachedMaximumOutputSize, CGSizeZero))
|
||||
{
|
||||
for (id<GPUImageInput> currentTarget in targets)
|
||||
{
|
||||
if ([currentTarget maximumOutputSize].width > cachedMaximumOutputSize.width)
|
||||
{
|
||||
cachedMaximumOutputSize = [currentTarget maximumOutputSize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cachedMaximumOutputSize;
|
||||
*/
|
||||
}
|
||||
|
||||
- (void)endProcessing
|
||||
{
|
||||
if (!isEndProcessing)
|
||||
{
|
||||
isEndProcessing = YES;
|
||||
|
||||
for (id<GPUImageInput> currentTarget in targets)
|
||||
{
|
||||
[currentTarget endProcessing];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)wantsMonochromeInput;
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
@end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user