package org.telegram.ui.Components; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.PointF; import android.opengl.GLES11Ext; import android.opengl.GLES20; import android.opengl.GLUtils; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLog; import org.telegram.messenger.MediaController; import org.telegram.messenger.Utilities; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.Locale; import javax.microedition.khronos.opengles.GL10; public class FilterShaders { private static final String YUCIHighPassSkinSmoothingMaskBoostFilterFragmentShaderCode = "precision lowp float;" + "varying highp vec2 texCoord;" + "uniform sampler2D sourceImage;" + "void main() {" + "vec4 color = texture2D(sourceImage, texCoord);" + "float hardLightColor = color.b;" + "for (int i = 0; i < 3; ++i)" + "{" + "if (hardLightColor < 0.5) {" + "hardLightColor = hardLightColor * hardLightColor * 2.0;" + "} else {" + "hardLightColor = 1.0 - (1.0 - hardLightColor) * (1.0 - hardLightColor) * 2.0;" + "}" + "}" + "float k = 255.0 / (164.0 - 75.0);" + "hardLightColor = (hardLightColor - 75.0 / 255.0) * k;" + "gl_FragColor = vec4(vec3(hardLightColor), color.a);" + "}"; private static final String highpassSkinSmoothingCompositingFilterFragmentShaderString = "%1$s\n" + "precision lowp float;" + "varying highp vec2 texCoord;" + "varying highp vec2 texCoord2;" + "uniform %2$s sourceImage;" + "uniform sampler2D toneCurveTexture;" + "uniform sampler2D inputImageTexture3;" + "uniform lowp float mixturePercent;" + "void main() {" + "vec4 image = texture2D(sourceImage, texCoord);" + "vec4 mask = texture2D(inputImageTexture3, texCoord2);" + "float redCurveValue = texture2D(toneCurveTexture, vec2(image.r, 0.0)).r;" + "float greenCurveValue = texture2D(toneCurveTexture, vec2(image.g, 0.0)).g;" + "float blueCurveValue = texture2D(toneCurveTexture, vec2(image.b, 0.0)).b;" + "vec4 result = vec4(redCurveValue, greenCurveValue, blueCurveValue, image.a);" + "vec4 tone = mix(image, result, mixturePercent);" + "gl_FragColor = vec4(mix(image.rgb, tone.rgb, 1.0 - mask.b), 1.0);" + "}"; private static final String greenAndBlueChannelOverlayFragmentShaderCode = "%1$s\n" + "precision lowp float;" + "varying highp vec2 texCoord;" + "uniform %2$s sourceImage;" + "void main() {" + "vec4 inp = texture2D(sourceImage, texCoord);" + "vec4 image = vec4(inp.rgb * pow(2.0, -1.0), inp.w);" + "vec4 base = vec4(image.g, image.g, image.g, 1.0);" + "vec4 overlay = vec4(image.b, image.b, image.b, 1.0);" + "float ba = 2.0 * overlay.b * base.b + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);" + "gl_FragColor = vec4(ba,ba,ba,image.a);" + "}"; private static final String stillImageHighPassFilterFragmentShaderCode = "precision lowp float;" + "varying highp vec2 texCoord;" + "varying highp vec2 texCoord2;" + "uniform sampler2D sourceImage;" + "uniform sampler2D inputImageTexture2;" + "void main() {" + "vec4 image = texture2D(sourceImage, texCoord);" + "vec4 blurredImage = texture2D(inputImageTexture2, texCoord2);" + "gl_FragColor = vec4((image.rgb - blurredImage.rgb + vec3(0.5,0.5,0.5)), image.a);" + "}"; private static final String radialBlurFragmentShaderCode = "varying highp vec2 texCoord;" + "uniform sampler2D sourceImage;" + "uniform sampler2D inputImageTexture2;" + "uniform lowp float excludeSize;" + "uniform lowp vec2 excludePoint;" + "uniform lowp float excludeBlurSize;" + "uniform highp float aspectRatio;" + "void main() {" + "lowp vec4 sharpImageColor = texture2D(sourceImage, texCoord);" + "lowp vec4 blurredImageColor = texture2D(inputImageTexture2, texCoord);" + "highp vec2 texCoordToUse = vec2(texCoord.x, (texCoord.y * aspectRatio + 0.5 - 0.5 * aspectRatio));" + "highp float distanceFromCenter = distance(excludePoint, texCoordToUse);" + "gl_FragColor = mix(sharpImageColor, blurredImageColor, smoothstep(excludeSize - excludeBlurSize, excludeSize, distanceFromCenter));" + "}"; private static final String linearBlurFragmentShaderCode = "varying highp vec2 texCoord;" + "uniform sampler2D sourceImage;" + "uniform sampler2D inputImageTexture2;" + "uniform lowp float excludeSize;" + "uniform lowp vec2 excludePoint;" + "uniform lowp float excludeBlurSize;" + "uniform highp float angle;" + "uniform highp float aspectRatio;" + "void main() {" + "lowp vec4 sharpImageColor = texture2D(sourceImage, texCoord);" + "lowp vec4 blurredImageColor = texture2D(inputImageTexture2, texCoord);" + "highp vec2 texCoordToUse = vec2(texCoord.x, (texCoord.y * aspectRatio + 0.5 - 0.5 * aspectRatio));" + "highp float distanceFromCenter = abs((texCoordToUse.x - excludePoint.x) * aspectRatio * cos(angle) + (texCoordToUse.y - excludePoint.y) * sin(angle));" + "gl_FragColor = mix(sharpImageColor, blurredImageColor, smoothstep(excludeSize - excludeBlurSize, excludeSize, distanceFromCenter));" + "}"; private static String vertexShaderForOptimizedBlurOfRadius(int blurRadius, float sigma) { float[] standardGaussianWeights = new float[blurRadius * 2 + 1]; float sumOfWeights = 0.0f; for (int currentGaussianWeightIndex = 0; currentGaussianWeightIndex < blurRadius + 1; currentGaussianWeightIndex++) { standardGaussianWeights[currentGaussianWeightIndex] = (float) ((1.0 / Math.sqrt(2.0 * Math.PI * Math.pow(sigma, 2.0))) * Math.exp(-Math.pow(currentGaussianWeightIndex, 2.0) / (2.0 * Math.pow(sigma, 2.0)))); if (currentGaussianWeightIndex == 0) { sumOfWeights += standardGaussianWeights[currentGaussianWeightIndex]; } else { sumOfWeights += 2.0 * standardGaussianWeights[currentGaussianWeightIndex]; } } for (int currentGaussianWeightIndex = 0; currentGaussianWeightIndex < blurRadius + 1; currentGaussianWeightIndex++) { standardGaussianWeights[currentGaussianWeightIndex] = standardGaussianWeights[currentGaussianWeightIndex] / sumOfWeights; } int numberOfOptimizedOffsets = Math.min(blurRadius / 2 + (blurRadius % 2), 7); float[] optimizedGaussianOffsets = new float[numberOfOptimizedOffsets]; for (int currentOptimizedOffset = 0; currentOptimizedOffset < numberOfOptimizedOffsets; currentOptimizedOffset++) { float firstWeight = standardGaussianWeights[currentOptimizedOffset * 2 + 1]; float secondWeight = standardGaussianWeights[currentOptimizedOffset * 2 + 2]; float optimizedWeight = firstWeight + secondWeight; optimizedGaussianOffsets[currentOptimizedOffset] = (firstWeight * (currentOptimizedOffset * 2 + 1) + secondWeight * (currentOptimizedOffset * 2 + 2)) / optimizedWeight; } StringBuilder shaderString = new StringBuilder(); shaderString.append("attribute vec4 position;\n"); shaderString.append("attribute vec4 inputTexCoord;\n"); shaderString.append("uniform float texelWidthOffset;\n"); shaderString.append("uniform float texelHeightOffset;\n"); shaderString.append(String.format(Locale.US, "varying vec2 blurCoordinates[%d];\n", 1 + (numberOfOptimizedOffsets * 2))); shaderString.append("void main()\n"); shaderString.append("{\n"); shaderString.append("gl_Position = position;\n"); shaderString.append("vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);\n"); shaderString.append("blurCoordinates[0] = inputTexCoord.xy;\n"); for (int currentOptimizedOffset = 0; currentOptimizedOffset < numberOfOptimizedOffsets; currentOptimizedOffset++) { shaderString.append(String.format(Locale.US, "blurCoordinates[%d] = inputTexCoord.xy + singleStepOffset * %f;\n" + "blurCoordinates[%d] = inputTexCoord.xy - singleStepOffset * %f;\n", ((currentOptimizedOffset * 2) + 1), optimizedGaussianOffsets[currentOptimizedOffset], ((currentOptimizedOffset * 2) + 2), optimizedGaussianOffsets[currentOptimizedOffset])); } shaderString.append("}"); return shaderString.toString(); } private static String fragmentShaderForOptimizedBlurOfRadius(int blurRadius, float sigma) { float[] standardGaussianWeights = new float[blurRadius * 2 + 1]; float sumOfWeights = 0.0f; for (int currentGaussianWeightIndex = 0; currentGaussianWeightIndex < blurRadius + 1; currentGaussianWeightIndex++) { standardGaussianWeights[currentGaussianWeightIndex] = (float) ((1.0 / Math.sqrt(2.0 * Math.PI * Math.pow(sigma, 2.0))) * Math.exp(-Math.pow(currentGaussianWeightIndex, 2.0) / (2.0 * Math.pow(sigma, 2.0)))); if (currentGaussianWeightIndex == 0) { sumOfWeights += standardGaussianWeights[currentGaussianWeightIndex]; } else { sumOfWeights += 2.0 * standardGaussianWeights[currentGaussianWeightIndex]; } } for (int currentGaussianWeightIndex = 0; currentGaussianWeightIndex < blurRadius + 1; currentGaussianWeightIndex++) { standardGaussianWeights[currentGaussianWeightIndex] = standardGaussianWeights[currentGaussianWeightIndex] / sumOfWeights; } int numberOfOptimizedOffsets = Math.min(blurRadius / 2 + (blurRadius % 2), 7); int trueNumberOfOptimizedOffsets = blurRadius / 2 + (blurRadius % 2); StringBuilder shaderString = new StringBuilder(); shaderString.append("uniform sampler2D sourceImage;\n"); shaderString.append("uniform highp float texelWidthOffset;\n"); shaderString.append("uniform highp float texelHeightOffset;\n"); shaderString.append(String.format(Locale.US, "varying highp vec2 blurCoordinates[%d];\n", 1 + (numberOfOptimizedOffsets * 2))); shaderString.append("void main()\n"); shaderString.append("{\n"); shaderString.append("lowp vec4 sum = vec4(0.0);\n"); shaderString.append(String.format(Locale.US, "sum += texture2D(sourceImage, blurCoordinates[0]) * %f;\n", standardGaussianWeights[0])); for (int currentBlurCoordinateIndex = 0; currentBlurCoordinateIndex < numberOfOptimizedOffsets; currentBlurCoordinateIndex++) { float firstWeight = standardGaussianWeights[currentBlurCoordinateIndex * 2 + 1]; float secondWeight = standardGaussianWeights[currentBlurCoordinateIndex * 2 + 2]; float optimizedWeight = firstWeight + secondWeight; shaderString.append(String.format(Locale.US, "sum += texture2D(sourceImage, blurCoordinates[%d]) * %f;\n", ((currentBlurCoordinateIndex * 2) + 1), optimizedWeight)); shaderString.append(String.format(Locale.US, "sum += texture2D(sourceImage, blurCoordinates[%d]) * %f;\n", ((currentBlurCoordinateIndex * 2) + 2), optimizedWeight)); } if (trueNumberOfOptimizedOffsets > numberOfOptimizedOffsets) { shaderString.append("highp vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);\n"); for (int currentOverlowTextureRead = numberOfOptimizedOffsets; currentOverlowTextureRead < trueNumberOfOptimizedOffsets; currentOverlowTextureRead++) { float firstWeight = standardGaussianWeights[currentOverlowTextureRead * 2 + 1]; float secondWeight = standardGaussianWeights[currentOverlowTextureRead * 2 + 2]; float optimizedWeight = firstWeight + secondWeight; float optimizedOffset = (firstWeight * (currentOverlowTextureRead * 2 + 1) + secondWeight * (currentOverlowTextureRead * 2 + 2)) / optimizedWeight; shaderString.append(String.format(Locale.US, "sum += texture2D(sourceImage, blurCoordinates[0] + singleStepOffset * %f) * %f;\n", optimizedOffset, optimizedWeight)); shaderString.append(String.format(Locale.US, "sum += texture2D(sourceImage, blurCoordinates[0] - singleStepOffset * %f) * %f;\n", optimizedOffset, optimizedWeight)); } } shaderString.append("gl_FragColor = sum;\n"); shaderString.append("}\n"); return shaderString.toString(); } public void drawBoxBlur() { } private static class BlurProgram { private String vertexShaderCode; private String fragmentShaderCode; public int blurShaderProgram; public int blurPositionHandle; public int blurInputTexCoordHandle; public int blurSourceImageHandle; public int blurWidthHandle; public int blurHeightHandle; public BlurProgram(float radius, float sigma, boolean calc) { int calculatedSampleRadius; if (calc) { sigma = Math.round(radius); calculatedSampleRadius = 0; if (sigma >= 1) { float minimumWeightToFindEdgeOfSamplingArea = 1.0f / 256.0f; calculatedSampleRadius = (int) Math.floor(Math.sqrt(-2.0 * Math.pow(sigma, 2.0) * Math.log(minimumWeightToFindEdgeOfSamplingArea * Math.sqrt(2.0 * Math.PI * Math.pow(sigma, 2.0))))); calculatedSampleRadius += calculatedSampleRadius % 2; } } else { calculatedSampleRadius = (int) radius; } fragmentShaderCode = fragmentShaderForOptimizedBlurOfRadius(calculatedSampleRadius, sigma); vertexShaderCode = vertexShaderForOptimizedBlurOfRadius(calculatedSampleRadius, sigma); } public void destroy() { if (blurShaderProgram != 0) { GLES20.glDeleteProgram(blurShaderProgram); blurShaderProgram = 0; } } public boolean create() { int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); if (vertexShader == 0 || fragmentShader == 0) { return false; } blurShaderProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(blurShaderProgram, vertexShader); GLES20.glAttachShader(blurShaderProgram, fragmentShader); GLES20.glBindAttribLocation(blurShaderProgram, 0, "position"); GLES20.glBindAttribLocation(blurShaderProgram, 1, "inputTexCoord"); GLES20.glLinkProgram(blurShaderProgram); int[] linkStatus = new int[1]; GLES20.glGetProgramiv(blurShaderProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(blurShaderProgram); blurShaderProgram = 0; } else { blurPositionHandle = GLES20.glGetAttribLocation(blurShaderProgram, "position"); blurInputTexCoordHandle = GLES20.glGetAttribLocation(blurShaderProgram, "inputTexCoord"); blurSourceImageHandle = GLES20.glGetUniformLocation(blurShaderProgram, "sourceImage"); blurWidthHandle = GLES20.glGetUniformLocation(blurShaderProgram, "texelWidthOffset"); blurHeightHandle = GLES20.glGetUniformLocation(blurShaderProgram, "texelHeightOffset"); } return true; } } private static final String simpleVertexVideoShaderCode = "attribute vec4 position;" + "uniform mat4 videoMatrix;" + "attribute vec4 inputTexCoord;" + "varying vec2 texCoord;" + "void main() {" + "gl_Position = position;" + "texCoord = vec2(videoMatrix * inputTexCoord).xy;" + "}"; private static final String simpleTwoTextureVertexVideoShaderCode = "attribute vec4 position;" + "uniform mat4 videoMatrix;" + "attribute vec4 inputTexCoord;" + "varying vec2 texCoord;" + "varying vec2 texCoord2;" + "void main() {" + "gl_Position = position;" + "texCoord = vec2(videoMatrix * inputTexCoord).xy;" + "texCoord2 = inputTexCoord.xy;" + "}"; private static final String rgbToHsvFragmentShaderCode = "%1$s\n" + "precision highp float;" + "varying vec2 texCoord;" + "uniform %2$s sourceImage;" + "vec3 rgb_to_hsv(vec3 c) {" + "vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);" + "vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);" + "vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);" + "float d = q.x - min(q.w, q.y);" + "float e = 1.0e-10;" + "return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);" + "}" + "void main() {" + "vec4 texel = texture2D(sourceImage, texCoord);" + "gl_FragColor = vec4(rgb_to_hsv(texel.rgb), texel.a);" + "}"; private static final String enhanceFragmentShaderCode = "precision highp float;" + "varying vec2 texCoord;" + "uniform sampler2D sourceImage;" + "uniform sampler2D inputImageTexture2;" + "uniform float intensity;" + "float enhance(float value) {" + "const vec2 offset = vec2(0.001953125, 0.03125);" + "value = value + offset.x;" + "vec2 coord = (clamp(texCoord, 0.125, 1.0 - 0.125001) - 0.125) * 4.0;" + "vec2 frac = fract(coord);" + "coord = floor(coord);" + "float p00 = float(coord.y * 4.0 + coord.x) * 0.0625 + offset.y;" + "float p01 = float(coord.y * 4.0 + coord.x + 1.0) * 0.0625 + offset.y;" + "float p10 = float((coord.y + 1.0) * 4.0 + coord.x) * 0.0625 + offset.y;" + "float p11 = float((coord.y + 1.0) * 4.0 + coord.x + 1.0) * 0.0625 + offset.y;" + "vec3 c00 = texture2D(inputImageTexture2, vec2(value, p00)).rgb;" + "vec3 c01 = texture2D(inputImageTexture2, vec2(value, p01)).rgb;" + "vec3 c10 = texture2D(inputImageTexture2, vec2(value, p10)).rgb;" + "vec3 c11 = texture2D(inputImageTexture2, vec2(value, p11)).rgb;" + "float c1 = ((c00.r - c00.g) / (c00.b - c00.g));" + "float c2 = ((c01.r - c01.g) / (c01.b - c01.g));" + "float c3 = ((c10.r - c10.g) / (c10.b - c10.g));" + "float c4 = ((c11.r - c11.g) / (c11.b - c11.g));" + "float c1_2 = mix(c1, c2, frac.x);" + "float c3_4 = mix(c3, c4, frac.x);" + "return mix(c1_2, c3_4, frac.y);" + "}" + "vec3 hsv_to_rgb(vec3 c) {" + "vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);" + "vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);" + "return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);" + "}" + "void main() {" + "vec4 texel = texture2D(sourceImage, texCoord);" + "vec4 hsv = texel;" + "hsv.y = min(1.0, hsv.y * 1.2);" + "hsv.z = min(1.0, enhance(hsv.z) * 1.1);" + "gl_FragColor = vec4(hsv_to_rgb(mix(texel.xyz, hsv.xyz, intensity)), texel.w);" + "}"; public static final String simpleVertexShaderCode = "attribute vec4 position;" + "attribute vec2 inputTexCoord;" + "varying vec2 texCoord;" + "void main() {" + "gl_Position = position;" + "texCoord = inputTexCoord;" + "}"; public static final String simpleTwoTextureVertexShaderCode = "attribute vec4 position;" + "attribute vec2 inputTexCoord;" + "varying vec2 texCoord;" + "varying vec2 texCoord2;" + "void main() {" + "gl_Position = position;" + "texCoord = inputTexCoord;" + "texCoord2 = inputTexCoord;" + "}"; public static final String simpleFragmentShaderCode = "varying highp vec2 texCoord;" + "uniform sampler2D sourceImage;" + "void main() {" + "gl_FragColor = texture2D(sourceImage, texCoord);" + "}"; private static final String sharpenVertexShaderCode = "attribute vec4 position;" + "attribute vec2 inputTexCoord;" + "varying vec2 texCoord;" + "uniform highp float inputWidth;" + "uniform highp float inputHeight;" + "varying vec2 leftTexCoord;" + "varying vec2 rightTexCoord;" + "varying vec2 topTexCoord;" + "varying vec2 bottomTexCoord;" + "void main() {" + "gl_Position = position;" + "texCoord = inputTexCoord;" + "highp vec2 widthStep = vec2(1.0 / inputWidth, 0.0);" + "highp vec2 heightStep = vec2(0.0, 1.0 / inputHeight);" + "leftTexCoord = inputTexCoord - widthStep;" + "rightTexCoord = inputTexCoord + widthStep;" + "topTexCoord = inputTexCoord + heightStep;" + "bottomTexCoord = inputTexCoord - heightStep;" + "}"; private static final String sharpenFragmentShaderCode = "precision highp float;" + "varying vec2 texCoord;" + "varying vec2 leftTexCoord;" + "varying vec2 rightTexCoord;" + "varying vec2 topTexCoord;" + "varying vec2 bottomTexCoord;" + "uniform sampler2D sourceImage;" + "uniform float sharpen;" + "void main() {" + "vec4 result = texture2D(sourceImage, texCoord);" + "vec3 leftTextureColor = texture2D(sourceImage, leftTexCoord).rgb;" + "vec3 rightTextureColor = texture2D(sourceImage, rightTexCoord).rgb;" + "vec3 topTextureColor = texture2D(sourceImage, topTexCoord).rgb;" + "vec3 bottomTextureColor = texture2D(sourceImage, bottomTexCoord).rgb;" + "result.rgb = result.rgb * (1.0 + 4.0 * sharpen) - (leftTextureColor + rightTextureColor + topTextureColor + bottomTextureColor) * sharpen;" + "gl_FragColor = result;" + "}"; private static final String toolsFragmentShaderCode = "varying highp vec2 texCoord;" + "uniform sampler2D sourceImage;" + "uniform highp float width;" + "uniform highp float height;" + "uniform sampler2D curvesImage;" + "uniform lowp float skipTone;" + "uniform lowp float shadows;" + "const mediump vec3 hsLuminanceWeighting = vec3(0.3, 0.3, 0.3);" + "uniform lowp float highlights;" + "uniform lowp float contrast;" + "uniform lowp float fadeAmount;" + "const mediump vec3 satLuminanceWeighting = vec3(0.2126, 0.7152, 0.0722);" + "uniform lowp float saturation;" + "uniform lowp float shadowsTintIntensity;" + "uniform lowp float highlightsTintIntensity;" + "uniform lowp vec3 shadowsTintColor;" + "uniform lowp vec3 highlightsTintColor;" + "uniform lowp float exposure;" + "uniform lowp float warmth;" + "uniform lowp float grain;" + "const lowp float permTexUnit = 1.0 / 256.0;" + "const lowp float permTexUnitHalf = 0.5 / 256.0;" + "const lowp float grainsize = 2.3;" + "uniform lowp float vignette;" + "highp float getLuma(highp vec3 rgbP) {" + "return (0.299 * rgbP.r) + (0.587 * rgbP.g) + (0.114 * rgbP.b);" + "}" + "lowp vec3 rgbToHsv(lowp vec3 c) {" + "highp vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);" + "highp vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);" + "highp vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);" + "highp float d = q.x - min(q.w, q.y);" + "highp float e = 1.0e-10;" + "return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);" + "}" + "lowp vec3 hsvToRgb(lowp vec3 c) {" + "highp vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);" + "highp vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);" + "return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);" + "}" + "highp vec3 rgbToHsl(highp vec3 color) {" + "highp vec3 hsl;" + "highp float fmin = min(min(color.r, color.g), color.b);" + "highp float fmax = max(max(color.r, color.g), color.b);" + "highp float delta = fmax - fmin;" + "hsl.z = (fmax + fmin) / 2.0;" + "if (delta == 0.0) {" + "hsl.x = 0.0;" + "hsl.y = 0.0;" + "} else {" + "if (hsl.z < 0.5) {" + "hsl.y = delta / (fmax + fmin);" + "} else {" + "hsl.y = delta / (2.0 - fmax - fmin);" + "}" + "highp float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta;" + "highp float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta;" + "highp float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta;" + "if (color.r == fmax) {" + "hsl.x = deltaB - deltaG;" + "} else if (color.g == fmax) {" + "hsl.x = (1.0 / 3.0) + deltaR - deltaB;" + "} else if (color.b == fmax) {" + "hsl.x = (2.0 / 3.0) + deltaG - deltaR;" + "}" + "if (hsl.x < 0.0) {" + "hsl.x += 1.0;" + "} else if (hsl.x > 1.0) {" + "hsl.x -= 1.0;" + "}" + "}" + "return hsl;" + "}" + "highp float hueToRgb(highp float f1, highp float f2, highp float hue) {" + "if (hue < 0.0) {" + "hue += 1.0;" + "} else if (hue > 1.0) {" + "hue -= 1.0;" + "}" + "highp float res;" + "if ((6.0 * hue) < 1.0) {" + "res = f1 + (f2 - f1) * 6.0 * hue;" + "} else if ((2.0 * hue) < 1.0) {" + "res = f2;" + "} else if ((3.0 * hue) < 2.0) {" + "res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;" + "} else {" + "res = f1;" + "}" + "return res;" + "}" + "highp vec3 hslToRgb(highp vec3 hsl) {" + "if (hsl.y == 0.0) {" + "return vec3(hsl.z);" + "} else {" + "highp float f2;" + "if (hsl.z < 0.5) {" + "f2 = hsl.z * (1.0 + hsl.y);" + "} else {" + "f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z);" + "}" + "highp float f1 = 2.0 * hsl.z - f2;" + "return vec3(hueToRgb(f1, f2, hsl.x + (1.0/3.0)), hueToRgb(f1, f2, hsl.x), hueToRgb(f1, f2, hsl.x - (1.0/3.0)));" + "}" + "}" + "highp vec3 rgbToYuv(highp vec3 inP) {" + "highp float luma = getLuma(inP);" + "return vec3(luma, (1.0 / 1.772) * (inP.b - luma), (1.0 / 1.402) * (inP.r - luma));" + "}" + "lowp vec3 yuvToRgb(highp vec3 inP) {" + "return vec3(1.402 * inP.b + inP.r, (inP.r - (0.299 * 1.402 / 0.587) * inP.b - (0.114 * 1.772 / 0.587) * inP.g), 1.772 * inP.g + inP.r);" + "}" + "lowp float easeInOutSigmoid(lowp float value, lowp float strength) {" + "if (value > 0.5) {" + "return 1.0 - pow(2.0 - 2.0 * value, 1.0 / (1.0 - strength)) * 0.5;" + "} else {" + "return pow(2.0 * value, 1.0 / (1.0 - strength)) * 0.5;" + "}" + "}" + "lowp vec3 applyLuminanceCurve(lowp vec3 pixel) {" + "highp float index = floor(clamp(pixel.z / (1.0 / 200.0), 0.0, 199.0));" + "pixel.y = mix(0.0, pixel.y, smoothstep(0.0, 0.1, pixel.z) * (1.0 - smoothstep(0.8, 1.0, pixel.z)));" + "pixel.z = texture2D(curvesImage, vec2(1.0 / 200.0 * index, 0)).a;" + "return pixel;" + "}" + "lowp vec3 applyRGBCurve(lowp vec3 pixel) {" + "highp float index = floor(clamp(pixel.r / (1.0 / 200.0), 0.0, 199.0));" + "pixel.r = texture2D(curvesImage, vec2(1.0 / 200.0 * index, 0)).r;" + "index = floor(clamp(pixel.g / (1.0 / 200.0), 0.0, 199.0));" + "pixel.g = clamp(texture2D(curvesImage, vec2(1.0 / 200.0 * index, 0)).g, 0.0, 1.0);" + "index = floor(clamp(pixel.b / (1.0 / 200.0), 0.0, 199.0));" + "pixel.b = clamp(texture2D(curvesImage, vec2(1.0 / 200.0 * index, 0)).b, 0.0, 1.0);" + "return pixel;" + "}" + "highp vec3 fadeAdjust(highp vec3 color, highp float fadeVal) {" + "return (color * (1.0 - fadeVal)) + ((color + (vec3(-0.9772) * pow(vec3(color), vec3(3.0)) + vec3(1.708) * pow(vec3(color), vec3(2.0)) + vec3(-0.1603) * vec3(color) + vec3(0.2878) - color * vec3(0.9))) * fadeVal);" + "}" + "lowp vec3 tintRaiseShadowsCurve(lowp vec3 color) {" + "return vec3(-0.003671) * pow(color, vec3(3.0)) + vec3(0.3842) * pow(color, vec3(2.0)) + vec3(0.3764) * color + vec3(0.2515);" + "}" + "lowp vec3 tintShadows(lowp vec3 texel, lowp vec3 tintColor, lowp float tintAmount) {" + "return clamp(mix(texel, mix(texel, tintRaiseShadowsCurve(texel), tintColor), tintAmount), 0.0, 1.0);" + "} " + "lowp vec3 tintHighlights(lowp vec3 texel, lowp vec3 tintColor, lowp float tintAmount) {" + "return clamp(mix(texel, mix(texel, vec3(1.0) - tintRaiseShadowsCurve(vec3(1.0) - texel), (vec3(1.0) - tintColor)), tintAmount), 0.0, 1.0);" + "}" + "highp vec4 rnm(in highp vec2 tc) {" + "highp float noise = sin(dot(tc, vec2(12.9898, 78.233))) * 43758.5453;" + "return vec4(fract(noise), fract(noise * 1.2154), fract(noise * 1.3453), fract(noise * 1.3647)) * 2.0 - 1.0;" + "}" + "highp float fade(in highp float t) {" + "return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);" + "}" + "highp float pnoise3D(in highp vec3 p) {" + "highp vec3 pi = permTexUnit * floor(p) + permTexUnitHalf;" + "highp vec3 pf = fract(p);" + "highp float perm = rnm(pi.xy).a;" + "highp float n000 = dot(rnm(vec2(perm, pi.z)).rgb * 4.0 - 1.0, pf);" + "highp float n001 = dot(rnm(vec2(perm, pi.z + permTexUnit)).rgb * 4.0 - 1.0, pf - vec3(0.0, 0.0, 1.0));" + "perm = rnm(pi.xy + vec2(0.0, permTexUnit)).a;" + "highp float n010 = dot(rnm(vec2(perm, pi.z)).rgb * 4.0 - 1.0, pf - vec3(0.0, 1.0, 0.0));" + "highp float n011 = dot(rnm(vec2(perm, pi.z + permTexUnit)).rgb * 4.0 - 1.0, pf - vec3(0.0, 1.0, 1.0));" + "perm = rnm(pi.xy + vec2(permTexUnit, 0.0)).a;" + "highp float n100 = dot(rnm(vec2(perm, pi.z)).rgb * 4.0 - 1.0, pf - vec3(1.0, 0.0, 0.0));" + "highp float n101 = dot(rnm(vec2(perm, pi.z + permTexUnit)).rgb * 4.0 - 1.0, pf - vec3(1.0, 0.0, 1.0));" + "perm = rnm(pi.xy + vec2(permTexUnit, permTexUnit)).a;" + "highp float n110 = dot(rnm(vec2(perm, pi.z)).rgb * 4.0 - 1.0, pf - vec3(1.0, 1.0, 0.0));" + "highp float n111 = dot(rnm(vec2(perm, pi.z + permTexUnit)).rgb * 4.0 - 1.0, pf - vec3(1.0, 1.0, 1.0));" + "highp vec4 n_x = mix(vec4(n000, n001, n010, n011), vec4(n100, n101, n110, n111), fade(pf.x));" + "highp vec2 n_xy = mix(n_x.xy, n_x.zw, fade(pf.y));" + "return mix(n_xy.x, n_xy.y, fade(pf.z));" + "}" + "lowp vec2 coordRot(in lowp vec2 tc, in lowp float angle) {" + "return vec2(((tc.x * 2.0 - 1.0) * cos(angle) - (tc.y * 2.0 - 1.0) * sin(angle)) * 0.5 + 0.5, ((tc.y * 2.0 - 1.0) * cos(angle) + (tc.x * 2.0 - 1.0) * sin(angle)) * 0.5 + 0.5);" + "}" + "void main() {" + "lowp vec4 source = texture2D(sourceImage, texCoord);" + "lowp vec4 result = source;" + "const lowp float toolEpsilon = 0.005;" + "if (skipTone < toolEpsilon) {" + "result = vec4(applyRGBCurve(hslToRgb(applyLuminanceCurve(rgbToHsl(result.rgb)))), result.a);" + "}" + "mediump float hsLuminance = dot(result.rgb, hsLuminanceWeighting);" + "mediump float shadow = clamp((pow(hsLuminance, 1.0 / shadows) + (-0.76) * pow(hsLuminance, 2.0 / shadows)) - hsLuminance, 0.0, 1.0);" + "mediump float highlight = clamp((1.0 - (pow(1.0 - hsLuminance, 1.0 / (2.0 - highlights)) + (-0.8) * pow(1.0 - hsLuminance, 2.0 / (2.0 - highlights)))) - hsLuminance, -1.0, 0.0);" + "lowp vec3 hsresult = vec3(0.0, 0.0, 0.0) + ((hsLuminance + shadow + highlight) - 0.0) * ((result.rgb - vec3(0.0, 0.0, 0.0)) / (hsLuminance - 0.0));" + "mediump float contrastedLuminance = ((hsLuminance - 0.5) * 1.5) + 0.5;" + "mediump float whiteInterp = contrastedLuminance * contrastedLuminance * contrastedLuminance;" + "mediump float whiteTarget = clamp(highlights, 1.0, 2.0) - 1.0;" + "hsresult = mix(hsresult, vec3(1.0), whiteInterp * whiteTarget);" + "mediump float invContrastedLuminance = 1.0 - contrastedLuminance;" + "mediump float blackInterp = invContrastedLuminance * invContrastedLuminance * invContrastedLuminance;" + "mediump float blackTarget = 1.0 - clamp(shadows, 0.0, 1.0);" + "hsresult = mix(hsresult, vec3(0.0), blackInterp * blackTarget);" + "result = vec4(hsresult.rgb, result.a);" + "result = vec4(clamp(((result.rgb - vec3(0.5)) * contrast + vec3(0.5)), 0.0, 1.0), result.a);" + "if (abs(fadeAmount) > toolEpsilon) {" + "result.rgb = fadeAdjust(result.rgb, fadeAmount);" + "}" + "lowp float satLuminance = dot(result.rgb, satLuminanceWeighting);" + "lowp vec3 greyScaleColor = vec3(satLuminance);" + "result = vec4(clamp(mix(greyScaleColor, result.rgb, saturation), 0.0, 1.0), result.a);" + "if (abs(shadowsTintIntensity) > toolEpsilon) {" + "result.rgb = tintShadows(result.rgb, shadowsTintColor, shadowsTintIntensity * 2.0);" + "}" + "if (abs(highlightsTintIntensity) > toolEpsilon) {" + "result.rgb = tintHighlights(result.rgb, highlightsTintColor, highlightsTintIntensity * 2.0);" + "}" + "if (abs(exposure) > toolEpsilon) {" + "mediump float mag = exposure * 1.045;" + "mediump float exppower = 1.0 + abs(mag);" + "if (mag < 0.0) {" + "exppower = 1.0 / exppower;" + "}" + "result.r = 1.0 - pow((1.0 - result.r), exppower);" + "result.g = 1.0 - pow((1.0 - result.g), exppower);" + "result.b = 1.0 - pow((1.0 - result.b), exppower);" + "}" + "if (abs(warmth) > toolEpsilon) {" + "highp vec3 yuvVec;" + "if (warmth > 0.0 ) {" + "yuvVec = vec3(0.1765, -0.1255, 0.0902);" + "} else {" + "yuvVec = -vec3(0.0588, 0.1569, -0.1255);" + "}" + "highp vec3 yuvColor = rgbToYuv(result.rgb);" + "highp float luma = yuvColor.r;" + "highp float curveScale = sin(luma * 3.14159);" + "yuvColor += 0.375 * warmth * curveScale * yuvVec;" + "result.rgb = yuvToRgb(yuvColor);" + "}" + "if (abs(grain) > toolEpsilon) {" + "highp vec3 rotOffset = vec3(1.425, 3.892, 5.835);" + "highp vec2 rotCoordsR = coordRot(texCoord, rotOffset.x);" + "highp vec3 noise = vec3(pnoise3D(vec3(rotCoordsR * vec2(width / grainsize, height / grainsize),0.0)));" + "lowp vec3 lumcoeff = vec3(0.299,0.587,0.114);" + "lowp float luminance = dot(result.rgb, lumcoeff);" + "lowp float lum = smoothstep(0.2, 0.0, luminance);" + "lum += luminance;" + "noise = mix(noise,vec3(0.0),pow(lum,4.0));" + "result.rgb = result.rgb + noise * grain;" + "}" + "if (abs(vignette) > toolEpsilon) {" + "const lowp float midpoint = 0.7;" + "const lowp float fuzziness = 0.62;" + "lowp float radDist = length(texCoord - 0.5) / sqrt(0.5);" + "lowp float mag = easeInOutSigmoid(radDist * midpoint, fuzziness) * vignette * 0.645;" + "result.rgb = mix(pow(result.rgb, vec3(1.0 / (1.0 - mag))), vec3(0.0), mag * mag);" + "}" + "gl_FragColor = result;" + "}"; public interface FilterShadersDelegate { boolean shouldShowOriginal(); float getShadowsValue(); float getSoftenSkinValue(); float getHighlightsValue(); float getEnhanceValue(); float getExposureValue(); float getContrastValue(); float getWarmthValue(); float getVignetteValue(); float getSharpenValue(); float getGrainValue(); float getFadeValue(); float getTintHighlightsIntensityValue(); float getTintShadowsIntensityValue(); float getSaturationValue(); int getTintHighlightsColor(); int getTintShadowsColor(); int getBlurType(); float getBlurExcludeSize(); float getBlurExcludeBlurSize(); float getBlurAngle(); Point getBlurExcludePoint(); boolean shouldDrawCurvesPass(); ByteBuffer fillAndGetCurveBuffer(); } private static class ToneCurve { private float[] rgbCompositeCurve; private float[] redCurve; private float[] greenCurve; private float[] blueCurve; private int[] curveTexture = new int[1]; public ToneCurve() { ArrayList defaultCurve = new ArrayList<>(); defaultCurve.add(new PointF(0.0f, 0.0f)); defaultCurve.add(new PointF(0.5f, 0.5f)); defaultCurve.add(new PointF(1.0f, 1.0f)); ArrayList rgbDefaultCurve = new ArrayList<>(); rgbDefaultCurve.add(new PointF(0.0f, 0.0f)); rgbDefaultCurve.add(new PointF(0.47f, 0.57f)); rgbDefaultCurve.add(new PointF(1.0f, 1.0f)); rgbCompositeCurve = getPreparedSplineCurve(rgbDefaultCurve); redCurve = greenCurve = blueCurve = getPreparedSplineCurve(defaultCurve); updateToneCurveTexture(); } private float[] getPreparedSplineCurve(ArrayList points) { for (int i = 0, N = points.size(); i < N; i++) { PointF point = points.get(i); point.x *= 255; point.y *= 255; } ArrayList splinePoints = splineCurve(points); PointF firstSplinePoint = splinePoints.get(0); if (firstSplinePoint.x > 0) { for (int i = (int) firstSplinePoint.x; i >= 0; i--) { splinePoints.add(0, new PointF(i, 0)); } } PointF lastSplinePoint = splinePoints.get(splinePoints.size() - 1); if (lastSplinePoint.x < 255) { for (int i = (int) lastSplinePoint.x + 1; i <= 255; i++) { splinePoints.add(new PointF(i, 255)); } } float[] preparedSplinePoints = new float[splinePoints.size()]; for (int i = 0, N = splinePoints.size(); i < N; i++) { PointF newPoint = splinePoints.get(i); float distance = (float) Math.sqrt(Math.pow((newPoint.x - newPoint.y), 2.0)); if (newPoint.x > newPoint.y) { distance = -distance; } preparedSplinePoints[i] = distance; } return preparedSplinePoints; } private ArrayList splineCurve(ArrayList points) { double[] sd = secondDerivative(points); int n = sd.length; if (n < 1) { return null; } ArrayList output = new ArrayList<>(n + 1); for (int i = 0; i < n - 1; i++) { PointF cur = points.get(i); PointF next = points.get(i + 1); for (int x = (int) cur.x; x < (int) next.x; x++) { double t = (double) (x - cur.x) / (next.x - cur.x); double a = 1 - t; double h = next.x - cur.x; float y = (float) (a * cur.y + t * next.y + (h * h / 6) * ((a * a * a - a) * sd[i] + (t * t * t - t) * sd[i + 1])); if (y > 255.0f) { y = 255.0f; } else if (y < 0.0f) { y = 0.0f; } output.add(new PointF(x, y)); } } output.add(points.get(points.size() - 1)); return output; } private double[] secondDerivative(ArrayList points) { int n = points.size(); if ((n <= 0) || (n == 1)) { return null; } double[][] matrix = new double[n][3]; double[] result = new double[n]; matrix[0][1] = 1; matrix[0][0] = 0; matrix[0][2] = 0; for (int i = 1; i < n - 1; i++) { PointF P1 = points.get(i - 1); PointF P2 = points.get(i); PointF P3 = points.get(i + 1); matrix[i][0] = (double) (P2.x - P1.x) / 6; matrix[i][1] = (double) (P3.x - P1.x) / 3; matrix[i][2] = (double) (P3.x - P2.x) / 6; result[i] = (double) (P3.y - P2.y) / (P3.x - P2.x) - (double) (P2.y - P1.y) / (P2.x - P1.x); } result[0] = 0; result[n - 1] = 0; matrix[n - 1][1] = 1; matrix[n - 1][0] = 0; matrix[n - 1][2] = 0; for (int i = 1; i < n; i++) { double k = matrix[i][0] / matrix[i - 1][1]; matrix[i][1] -= k * matrix[i - 1][2]; matrix[i][0] = 0; result[i] -= k * result[i - 1]; } for (int i = n - 2; i >= 0; i--) { double k = matrix[i][2] / matrix[i + 1][1]; matrix[i][1] -= k * matrix[i + 1][0]; matrix[i][2] = 0; result[i] -= k * result[i + 1]; } double[] output = new double[n]; for (int i = 0; i < n; i++) { output[i] = result[i] / matrix[i][1]; } return output; } private void updateToneCurveTexture() { GLES20.glGenTextures(1, curveTexture, 0); GLES20.glBindTexture(GL10.GL_TEXTURE_2D, curveTexture[0]); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); ByteBuffer curveBuffer = ByteBuffer.allocateDirect(256 * 4); curveBuffer.order(ByteOrder.LITTLE_ENDIAN); if (redCurve.length >= 256 && greenCurve.length >= 256 && blueCurve.length >= 256 && rgbCompositeCurve.length >= 256) { for (int currentCurveIndex = 0; currentCurveIndex < 256; currentCurveIndex++) { int r = (int) Math.min(Math.max(currentCurveIndex + redCurve[currentCurveIndex], 0), 255); int g = (int) Math.min(Math.max(currentCurveIndex + greenCurve[currentCurveIndex], 0), 255); int b = (int) Math.min(Math.max(currentCurveIndex + blueCurve[currentCurveIndex], 0), 255); curveBuffer.put((byte) Math.min(Math.max(b + rgbCompositeCurve[b], 0), 255)); curveBuffer.put((byte) Math.min(Math.max(g + rgbCompositeCurve[g], 0), 255)); curveBuffer.put((byte) Math.min(Math.max(r + rgbCompositeCurve[r], 0), 255)); curveBuffer.put((byte) 255); } curveBuffer.position(0); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 256, 1, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, curveBuffer); } } public int getCurveTexture() { return curveTexture[0]; } } private boolean needUpdateBlurTexture = true; private boolean needUpdateSkinTexture = true; private boolean blurTextureCreated; private boolean skinTextureCreated; private BlurProgram skinBlurProgram; private float lastRadius; private BlurProgram blurProgram; private boolean skinPassDrawn; private int greenAndBlueChannelOverlayProgram; private int greenAndBlueChannelOverlayPositionHandle; private int greenAndBlueChannelOverlayInputTexCoordHandle; private int greenAndBlueChannelOverlaySourceImageHandle; private int greenAndBlueChannelOverlayMatrixHandle; private ToneCurve toneCurve; private int highPassProgram; private int highPassPositionHandle; private int highPassInputTexCoordHandle; private int highPassSourceImageHandle; private int highPassInputImageHandle; private int compositeProgram; private int compositePositionHandle; private int compositeInputTexCoordHandle; private int compositeSourceImageHandle; private int compositeInputImageHandle; private int compositeCurveImageHandle; private int compositeMixtureHandle; private int compositeMatrixHandle; private int boostProgram; private int boostPositionHandle; private int boostInputTexCoordHandle; private int boostSourceImageHandle; private int[] rgbToHsvShaderProgram = new int[2]; private int[] rgbToHsvPositionHandle = new int[2]; private int[] rgbToHsvInputTexCoordHandle = new int[2]; private int[] rgbToHsvSourceImageHandle = new int[2]; private int rgbToHsvMatrixHandle; private int enhanceShaderProgram; private int enhancePositionHandle; private int enhanceInputTexCoordHandle; private int enhanceSourceImageHandle; private int enhanceIntensityHandle; private int enhanceInputImageTexture2Handle; private int toolsShaderProgram; private int positionHandle; private int inputTexCoordHandle; private int sourceImageHandle; private int shadowsHandle; private int highlightsHandle; private int exposureHandle; private int contrastHandle; private int saturationHandle; private int warmthHandle; private int vignetteHandle; private int grainHandle; private int widthHandle; private int heightHandle; private int curvesImageHandle; private int skipToneHandle; private int fadeAmountHandle; private int shadowsTintIntensityHandle; private int highlightsTintIntensityHandle; private int shadowsTintColorHandle; private int highlightsTintColorHandle; private int linearBlurShaderProgram; private int linearBlurPositionHandle; private int linearBlurInputTexCoordHandle; private int linearBlurSourceImageHandle; private int linearBlurSourceImage2Handle; private int linearBlurExcludeSizeHandle; private int linearBlurExcludePointHandle; private int linearBlurExcludeBlurSizeHandle; private int linearBlurAngleHandle; private int linearBlurAspectRatioHandle; private int radialBlurShaderProgram; private int radialBlurPositionHandle; private int radialBlurInputTexCoordHandle; private int radialBlurSourceImageHandle; private int radialBlurSourceImage2Handle; private int radialBlurExcludeSizeHandle; private int radialBlurExcludePointHandle; private int radialBlurExcludeBlurSizeHandle; private int radialBlurAspectRatioHandle; private int sharpenShaderProgram; private int sharpenHandle; private int sharpenWidthHandle; private int sharpenHeightHandle; private int sharpenPositionHandle; private int sharpenInputTexCoordHandle; private int sharpenSourceImageHandle; private int videoTexture; private float[] videoMatrix; private int videoFramesCount; private int[] enhanceTextures = new int[2]; private int[] enhanceFrameBuffer = new int[1]; private int[] renderTexture = new int[4]; private int[] bitmapTextre = new int[1]; private int[] renderFrameBuffer; private int[] curveTextures = new int[1]; private boolean hsvGenerated; private int renderBufferWidth; private int renderBufferHeight; public FloatBuffer vertexBuffer; public FloatBuffer textureBuffer; public FloatBuffer vertexInvertBuffer; private ByteBuffer hsvBuffer; private ByteBuffer cdtBuffer; private ByteBuffer calcBuffer; private final static int PGPhotoEnhanceHistogramBins = 256; private final static int PGPhotoEnhanceSegments = 4; private FilterShadersDelegate delegate; private boolean isVideo; public FilterShaders(boolean video) { isVideo = video; float[] squareCoordinates = { -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f}; ByteBuffer bb = ByteBuffer.allocateDirect(squareCoordinates.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(squareCoordinates); vertexBuffer.position(0); float[] squareCoordinates2 = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f}; bb = ByteBuffer.allocateDirect(squareCoordinates2.length * 4); bb.order(ByteOrder.nativeOrder()); vertexInvertBuffer = bb.asFloatBuffer(); vertexInvertBuffer.put(squareCoordinates2); vertexInvertBuffer.position(0); float[] textureCoordinates = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, }; bb = ByteBuffer.allocateDirect(textureCoordinates.length * 4); bb.order(ByteOrder.nativeOrder()); textureBuffer = bb.asFloatBuffer(); textureBuffer.put(textureCoordinates); textureBuffer.position(0); } public void setDelegate(FilterShadersDelegate filterShadersDelegate) { delegate = filterShadersDelegate; } public boolean create() { GLES20.glGenTextures(1, curveTextures, 0); GLES20.glGenTextures(2, enhanceTextures, 0); GLES20.glGenFramebuffers(1, enhanceFrameBuffer, 0); GLES20.glBindTexture(GL10.GL_TEXTURE_2D, enhanceTextures[1]); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); GLES20.glBindTexture(GL10.GL_TEXTURE_2D, curveTextures[0]); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); int[] linkStatus = new int[1]; int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, toolsFragmentShaderCode); if (vertexShader != 0 && fragmentShader != 0) { toolsShaderProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(toolsShaderProgram, vertexShader); GLES20.glAttachShader(toolsShaderProgram, fragmentShader); GLES20.glBindAttribLocation(toolsShaderProgram, 0, "position"); GLES20.glBindAttribLocation(toolsShaderProgram, 1, "inputTexCoord"); GLES20.glLinkProgram(toolsShaderProgram); GLES20.glGetProgramiv(toolsShaderProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(toolsShaderProgram); toolsShaderProgram = 0; } else { positionHandle = GLES20.glGetAttribLocation(toolsShaderProgram, "position"); inputTexCoordHandle = GLES20.glGetAttribLocation(toolsShaderProgram, "inputTexCoord"); sourceImageHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "sourceImage"); shadowsHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "shadows"); highlightsHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "highlights"); exposureHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "exposure"); contrastHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "contrast"); saturationHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "saturation"); warmthHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "warmth"); vignetteHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "vignette"); grainHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "grain"); widthHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "width"); heightHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "height"); curvesImageHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "curvesImage"); skipToneHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "skipTone"); fadeAmountHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "fadeAmount"); shadowsTintIntensityHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "shadowsTintIntensity"); highlightsTintIntensityHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "highlightsTintIntensity"); shadowsTintColorHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "shadowsTintColor"); highlightsTintColorHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "highlightsTintColor"); } } else { return false; } vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, sharpenVertexShaderCode); fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, sharpenFragmentShaderCode); if (vertexShader != 0 && fragmentShader != 0) { sharpenShaderProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(sharpenShaderProgram, vertexShader); GLES20.glAttachShader(sharpenShaderProgram, fragmentShader); GLES20.glBindAttribLocation(sharpenShaderProgram, 0, "position"); GLES20.glBindAttribLocation(sharpenShaderProgram, 1, "inputTexCoord"); GLES20.glLinkProgram(sharpenShaderProgram); GLES20.glGetProgramiv(sharpenShaderProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(sharpenShaderProgram); sharpenShaderProgram = 0; } else { sharpenPositionHandle = GLES20.glGetAttribLocation(sharpenShaderProgram, "position"); sharpenInputTexCoordHandle = GLES20.glGetAttribLocation(sharpenShaderProgram, "inputTexCoord"); sharpenSourceImageHandle = GLES20.glGetUniformLocation(sharpenShaderProgram, "sourceImage"); sharpenWidthHandle = GLES20.glGetUniformLocation(sharpenShaderProgram, "inputWidth"); sharpenHeightHandle = GLES20.glGetUniformLocation(sharpenShaderProgram, "inputHeight"); sharpenHandle = GLES20.glGetUniformLocation(sharpenShaderProgram, "sharpen"); } } else { return false; } blurProgram = new BlurProgram(8, 3.0f, false); if (!blurProgram.create()) { return false; } String extension = isVideo ? "#extension GL_OES_EGL_image_external : require" : ""; String sampler2D = isVideo ? "samplerExternalOES" : "sampler2D"; vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, linearBlurFragmentShaderCode); if (vertexShader != 0 && fragmentShader != 0) { linearBlurShaderProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(linearBlurShaderProgram, vertexShader); GLES20.glAttachShader(linearBlurShaderProgram, fragmentShader); GLES20.glBindAttribLocation(linearBlurShaderProgram, 0, "position"); GLES20.glBindAttribLocation(linearBlurShaderProgram, 1, "inputTexCoord"); GLES20.glLinkProgram(linearBlurShaderProgram); GLES20.glGetProgramiv(linearBlurShaderProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(linearBlurShaderProgram); linearBlurShaderProgram = 0; } else { linearBlurPositionHandle = GLES20.glGetAttribLocation(linearBlurShaderProgram, "position"); linearBlurInputTexCoordHandle = GLES20.glGetAttribLocation(linearBlurShaderProgram, "inputTexCoord"); linearBlurSourceImageHandle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "sourceImage"); linearBlurSourceImage2Handle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "inputImageTexture2"); linearBlurExcludeSizeHandle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "excludeSize"); linearBlurExcludePointHandle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "excludePoint"); linearBlurExcludeBlurSizeHandle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "excludeBlurSize"); linearBlurAngleHandle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "angle"); linearBlurAspectRatioHandle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "aspectRatio"); } } else { return false; } vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, radialBlurFragmentShaderCode); if (vertexShader != 0 && fragmentShader != 0) { radialBlurShaderProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(radialBlurShaderProgram, vertexShader); GLES20.glAttachShader(radialBlurShaderProgram, fragmentShader); GLES20.glBindAttribLocation(radialBlurShaderProgram, 0, "position"); GLES20.glBindAttribLocation(radialBlurShaderProgram, 1, "inputTexCoord"); GLES20.glLinkProgram(radialBlurShaderProgram); GLES20.glGetProgramiv(radialBlurShaderProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(radialBlurShaderProgram); radialBlurShaderProgram = 0; } else { radialBlurPositionHandle = GLES20.glGetAttribLocation(radialBlurShaderProgram, "position"); radialBlurInputTexCoordHandle = GLES20.glGetAttribLocation(radialBlurShaderProgram, "inputTexCoord"); radialBlurSourceImageHandle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "sourceImage"); radialBlurSourceImage2Handle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "inputImageTexture2"); radialBlurExcludeSizeHandle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "excludeSize"); radialBlurExcludePointHandle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "excludePoint"); radialBlurExcludeBlurSizeHandle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "excludeBlurSize"); radialBlurAspectRatioHandle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "aspectRatio"); } } else { return false; } for (int a = 0; a < (isVideo ? 2 : 1); a++) { if (a == 1 && isVideo) { vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexVideoShaderCode); fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, rgbToHsvFragmentShaderCode, extension, sampler2D)); } else { vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, rgbToHsvFragmentShaderCode, "", "sampler2D")); } if (vertexShader != 0 && fragmentShader != 0) { rgbToHsvShaderProgram[a] = GLES20.glCreateProgram(); GLES20.glAttachShader(rgbToHsvShaderProgram[a], vertexShader); GLES20.glAttachShader(rgbToHsvShaderProgram[a], fragmentShader); GLES20.glBindAttribLocation(rgbToHsvShaderProgram[a], 0, "position"); GLES20.glBindAttribLocation(rgbToHsvShaderProgram[a], 1, "inputTexCoord"); GLES20.glLinkProgram(rgbToHsvShaderProgram[a]); GLES20.glGetProgramiv(rgbToHsvShaderProgram[a], GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(rgbToHsvShaderProgram[a]); rgbToHsvShaderProgram[a] = 0; } else { rgbToHsvPositionHandle[a] = GLES20.glGetAttribLocation(rgbToHsvShaderProgram[a], "position"); rgbToHsvInputTexCoordHandle[a] = GLES20.glGetAttribLocation(rgbToHsvShaderProgram[a], "inputTexCoord"); rgbToHsvSourceImageHandle[a] = GLES20.glGetUniformLocation(rgbToHsvShaderProgram[a], "sourceImage"); if (a == 1) { rgbToHsvMatrixHandle = GLES20.glGetUniformLocation(rgbToHsvShaderProgram[a], "videoMatrix"); } } } else { return false; } } vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, enhanceFragmentShaderCode); if (vertexShader != 0 && fragmentShader != 0) { enhanceShaderProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(enhanceShaderProgram, vertexShader); GLES20.glAttachShader(enhanceShaderProgram, fragmentShader); GLES20.glBindAttribLocation(enhanceShaderProgram, 0, "position"); GLES20.glBindAttribLocation(enhanceShaderProgram, 1, "inputTexCoord"); GLES20.glLinkProgram(enhanceShaderProgram); GLES20.glGetProgramiv(enhanceShaderProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(enhanceShaderProgram); enhanceShaderProgram = 0; } else { enhancePositionHandle = GLES20.glGetAttribLocation(enhanceShaderProgram, "position"); enhanceInputTexCoordHandle = GLES20.glGetAttribLocation(enhanceShaderProgram, "inputTexCoord"); enhanceSourceImageHandle = GLES20.glGetUniformLocation(enhanceShaderProgram, "sourceImage"); enhanceIntensityHandle = GLES20.glGetUniformLocation(enhanceShaderProgram, "intensity"); enhanceInputImageTexture2Handle = GLES20.glGetUniformLocation(enhanceShaderProgram, "inputImageTexture2"); } } else { return false; } if (isVideo) { vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexVideoShaderCode); } else { vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); } fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, greenAndBlueChannelOverlayFragmentShaderCode, extension, sampler2D)); if (vertexShader != 0 && fragmentShader != 0) { greenAndBlueChannelOverlayProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(greenAndBlueChannelOverlayProgram, vertexShader); GLES20.glAttachShader(greenAndBlueChannelOverlayProgram, fragmentShader); GLES20.glBindAttribLocation(greenAndBlueChannelOverlayProgram, 0, "position"); GLES20.glBindAttribLocation(greenAndBlueChannelOverlayProgram, 1, "inputTexCoord"); GLES20.glLinkProgram(greenAndBlueChannelOverlayProgram); GLES20.glGetProgramiv(greenAndBlueChannelOverlayProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(greenAndBlueChannelOverlayProgram); greenAndBlueChannelOverlayProgram = 0; } else { greenAndBlueChannelOverlayPositionHandle = GLES20.glGetAttribLocation(greenAndBlueChannelOverlayProgram, "position"); greenAndBlueChannelOverlayInputTexCoordHandle = GLES20.glGetAttribLocation(greenAndBlueChannelOverlayProgram, "inputTexCoord"); greenAndBlueChannelOverlaySourceImageHandle = GLES20.glGetUniformLocation(greenAndBlueChannelOverlayProgram, "sourceImage"); if (isVideo) { greenAndBlueChannelOverlayMatrixHandle = GLES20.glGetUniformLocation(greenAndBlueChannelOverlayProgram, "videoMatrix"); } } } else { return false; } fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, stillImageHighPassFilterFragmentShaderCode); vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleTwoTextureVertexShaderCode); if (vertexShader != 0 && fragmentShader != 0) { highPassProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(highPassProgram, vertexShader); GLES20.glAttachShader(highPassProgram, fragmentShader); GLES20.glBindAttribLocation(highPassProgram, 0, "position"); GLES20.glBindAttribLocation(highPassProgram, 1, "inputTexCoord"); GLES20.glLinkProgram(highPassProgram); GLES20.glGetProgramiv(highPassProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(highPassProgram); highPassProgram = 0; } else { highPassPositionHandle = GLES20.glGetAttribLocation(highPassProgram, "position"); highPassInputTexCoordHandle = GLES20.glGetAttribLocation(highPassProgram, "inputTexCoord"); highPassSourceImageHandle = GLES20.glGetUniformLocation(highPassProgram, "sourceImage"); highPassInputImageHandle = GLES20.glGetUniformLocation(highPassProgram, "inputImageTexture2"); } } else { return false; } fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, YUCIHighPassSkinSmoothingMaskBoostFilterFragmentShaderCode); vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); if (vertexShader != 0 && fragmentShader != 0) { boostProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(boostProgram, vertexShader); GLES20.glAttachShader(boostProgram, fragmentShader); GLES20.glBindAttribLocation(boostProgram, 0, "position"); GLES20.glBindAttribLocation(boostProgram, 1, "inputTexCoord"); GLES20.glLinkProgram(boostProgram); GLES20.glGetProgramiv(boostProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(boostProgram); boostProgram = 0; } else { boostPositionHandle = GLES20.glGetAttribLocation(boostProgram, "position"); boostInputTexCoordHandle = GLES20.glGetAttribLocation(boostProgram, "inputTexCoord"); boostSourceImageHandle = GLES20.glGetUniformLocation(boostProgram, "sourceImage"); } } else { return false; } if (isVideo) { vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleTwoTextureVertexVideoShaderCode); } else { vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleTwoTextureVertexShaderCode); } fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, highpassSkinSmoothingCompositingFilterFragmentShaderString, extension, sampler2D)); if (vertexShader != 0 && fragmentShader != 0) { compositeProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(compositeProgram, vertexShader); GLES20.glAttachShader(compositeProgram, fragmentShader); GLES20.glBindAttribLocation(compositeProgram, 0, "position"); GLES20.glBindAttribLocation(compositeProgram, 1, "inputTexCoord"); GLES20.glLinkProgram(compositeProgram); GLES20.glGetProgramiv(compositeProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(compositeProgram); compositeProgram = 0; } else { compositePositionHandle = GLES20.glGetAttribLocation(compositeProgram, "position"); compositeInputTexCoordHandle = GLES20.glGetAttribLocation(compositeProgram, "inputTexCoord"); compositeSourceImageHandle = GLES20.glGetUniformLocation(compositeProgram, "sourceImage"); compositeInputImageHandle = GLES20.glGetUniformLocation(compositeProgram, "inputImageTexture3"); compositeCurveImageHandle = GLES20.glGetUniformLocation(compositeProgram, "toneCurveTexture"); compositeMixtureHandle = GLES20.glGetUniformLocation(compositeProgram, "mixturePercent"); if (isVideo) { compositeMatrixHandle = GLES20.glGetUniformLocation(compositeProgram, "videoMatrix"); } } } else { return false; } toneCurve = new ToneCurve(); return true; } public void setRenderData(Bitmap currentBitmap, int orientation, int videoTex, int w, int h) { loadTexture(currentBitmap, orientation, w, h); videoTexture = videoTex; GLES20.glBindTexture(GL10.GL_TEXTURE_2D, enhanceTextures[0]); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, renderBufferWidth, renderBufferHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); } public static int loadShader(int type, String shaderCode) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); int[] compileStatus = new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); if (compileStatus[0] == 0) { if (BuildVars.LOGS_ENABLED) { FileLog.e(GLES20.glGetShaderInfoLog(shader)); FileLog.e("shader code:\n " + shaderCode); } GLES20.glDeleteShader(shader); shader = 0; } return shader; } public void drawEnhancePass() { boolean updateFrame; if (isVideo) { updateFrame = true; } else { updateFrame = !hsvGenerated; } if (updateFrame) { int index; if (isVideo && !skinPassDrawn) { GLES20.glUseProgram(rgbToHsvShaderProgram[1]); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, videoTexture); GLES20.glUniformMatrix4fv(rgbToHsvMatrixHandle, 1, false, videoMatrix, 0); index = 1; } else { GLES20.glUseProgram(rgbToHsvShaderProgram[0]); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, skinPassDrawn ? renderTexture[1] : bitmapTextre[0]); index = 0; } GLES20.glUniform1i(rgbToHsvSourceImageHandle[index], 0); GLES20.glEnableVertexAttribArray(rgbToHsvInputTexCoordHandle[index]); GLES20.glVertexAttribPointer(rgbToHsvInputTexCoordHandle[index], 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(rgbToHsvPositionHandle[index]); GLES20.glVertexAttribPointer(rgbToHsvPositionHandle[index], 2, GLES20.GL_FLOAT, false, 8, isVideo ? vertexInvertBuffer : vertexBuffer); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, enhanceFrameBuffer[0]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, enhanceTextures[0], 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } if (!hsvGenerated) { int newCapacity = renderBufferWidth * renderBufferHeight * 4; if (hsvBuffer == null || newCapacity > hsvBuffer.capacity()) { hsvBuffer = ByteBuffer.allocateDirect(newCapacity); } if (cdtBuffer == null) { cdtBuffer = ByteBuffer.allocateDirect(PGPhotoEnhanceSegments * PGPhotoEnhanceSegments * PGPhotoEnhanceHistogramBins * 4); } if (calcBuffer == null) { calcBuffer = ByteBuffer.allocateDirect(PGPhotoEnhanceSegments * PGPhotoEnhanceSegments * 2 * 4 * (1 + PGPhotoEnhanceHistogramBins)); } GLES20.glReadPixels(0, 0, renderBufferWidth, renderBufferHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, hsvBuffer); Utilities.calcCDT(hsvBuffer, renderBufferWidth, renderBufferHeight, cdtBuffer, calcBuffer); GLES20.glBindTexture(GL10.GL_TEXTURE_2D, enhanceTextures[1]); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 256, 16, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, cdtBuffer); if (!isVideo) { hsvBuffer = null; cdtBuffer = null; calcBuffer = null; } hsvGenerated = true; } GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[1]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[1], 0); GLES20.glUseProgram(enhanceShaderProgram); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, enhanceTextures[0]); GLES20.glUniform1i(enhanceSourceImageHandle, 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE1); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, enhanceTextures[1]); GLES20.glUniform1i(enhanceInputImageTexture2Handle, 1); if (delegate == null || delegate.shouldShowOriginal()) { GLES20.glUniform1f(enhanceIntensityHandle, 0); } else { GLES20.glUniform1f(enhanceIntensityHandle, delegate.getEnhanceValue()); } GLES20.glEnableVertexAttribArray(enhanceInputTexCoordHandle); GLES20.glVertexAttribPointer(enhanceInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(enhancePositionHandle); GLES20.glVertexAttribPointer(enhancePositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } public void drawSharpenPass() { if (isVideo) { return; } GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[0]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[0], 0); GLES20.glUseProgram(sharpenShaderProgram); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[1]); GLES20.glUniform1i(sharpenSourceImageHandle, 0); if (delegate == null || delegate.shouldShowOriginal()) { GLES20.glUniform1f(sharpenHandle, 0); } else { GLES20.glUniform1f(sharpenHandle, delegate.getSharpenValue()); } GLES20.glUniform1f(sharpenWidthHandle, renderBufferWidth); GLES20.glUniform1f(sharpenHeightHandle, renderBufferHeight); GLES20.glEnableVertexAttribArray(sharpenInputTexCoordHandle); GLES20.glVertexAttribPointer(sharpenInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(sharpenPositionHandle); GLES20.glVertexAttribPointer(sharpenPositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } public void drawCustomParamsPass() { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[isVideo ? 0 : 1]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[isVideo ? 0 : 1], 0); GLES20.glUseProgram(toolsShaderProgram); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[isVideo ? 1 : 0]); GLES20.glUniform1i(sourceImageHandle, 0); if (delegate == null || delegate.shouldShowOriginal()) { GLES20.glUniform1f(shadowsHandle, 1); GLES20.glUniform1f(highlightsHandle, 1); GLES20.glUniform1f(exposureHandle, 0); GLES20.glUniform1f(contrastHandle, 1); GLES20.glUniform1f(saturationHandle, 1); GLES20.glUniform1f(warmthHandle, 0); GLES20.glUniform1f(vignetteHandle, 0); GLES20.glUniform1f(grainHandle, 0); GLES20.glUniform1f(fadeAmountHandle, 0); GLES20.glUniform3f(highlightsTintColorHandle, 0, 0, 0); GLES20.glUniform1f(highlightsTintIntensityHandle, 0); GLES20.glUniform3f(shadowsTintColorHandle, 0, 0, 0); GLES20.glUniform1f(shadowsTintIntensityHandle, 0); GLES20.glUniform1f(skipToneHandle, 1); } else { GLES20.glUniform1f(shadowsHandle, delegate.getShadowsValue()); GLES20.glUniform1f(highlightsHandle, delegate.getHighlightsValue()); GLES20.glUniform1f(exposureHandle, delegate.getExposureValue()); GLES20.glUniform1f(contrastHandle, delegate.getContrastValue()); GLES20.glUniform1f(saturationHandle, delegate.getSaturationValue()); GLES20.glUniform1f(warmthHandle, delegate.getWarmthValue()); GLES20.glUniform1f(vignetteHandle, delegate.getVignetteValue()); GLES20.glUniform1f(grainHandle, delegate.getGrainValue()); GLES20.glUniform1f(fadeAmountHandle, delegate.getFadeValue()); int tintHighlightsColor = delegate.getTintHighlightsColor(); int tintShadowsColor = delegate.getTintShadowsColor(); GLES20.glUniform3f(highlightsTintColorHandle, (tintHighlightsColor >> 16 & 0xff) / 255.0f, (tintHighlightsColor >> 8 & 0xff) / 255.0f, (tintHighlightsColor & 0xff) / 255.0f); GLES20.glUniform1f(highlightsTintIntensityHandle, delegate.getTintHighlightsIntensityValue()); GLES20.glUniform3f(shadowsTintColorHandle, (tintShadowsColor >> 16 & 0xff) / 255.0f, (tintShadowsColor >> 8 & 0xff) / 255.0f, (tintShadowsColor & 0xff) / 255.0f); GLES20.glUniform1f(shadowsTintIntensityHandle, delegate.getTintShadowsIntensityValue()); boolean shouldDrawCurvesPass = delegate.shouldDrawCurvesPass(); GLES20.glUniform1f(skipToneHandle, shouldDrawCurvesPass ? 0.0f : 1.0f); if (shouldDrawCurvesPass) { ByteBuffer curveBuffer = delegate.fillAndGetCurveBuffer(); GLES20.glActiveTexture(GLES20.GL_TEXTURE1); GLES20.glBindTexture(GL10.GL_TEXTURE_2D, curveTextures[0]); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 200, 1, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, curveBuffer); GLES20.glUniform1i(curvesImageHandle, 1); } } GLES20.glUniform1f(widthHandle, renderBufferWidth); GLES20.glUniform1f(heightHandle, renderBufferHeight); GLES20.glEnableVertexAttribArray(inputTexCoordHandle); GLES20.glVertexAttribPointer(inputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(positionHandle); GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } public boolean drawSkinSmoothPass() { if (delegate == null || delegate.shouldShowOriginal() || delegate.getSoftenSkinValue() <= 0.0f || renderBufferWidth <= 0 || renderBufferHeight <= 0) { if (skinPassDrawn) { hsvGenerated = false; skinPassDrawn = false; } return false; } if (needUpdateSkinTexture || isVideo) { float rad = renderBufferWidth * 0.006f; if (skinBlurProgram == null || Math.abs(lastRadius - rad) > 0.0001) { if (skinBlurProgram != null) { skinBlurProgram.destroy(); } lastRadius = rad; skinBlurProgram = new BlurProgram(lastRadius, 2.0f, true); skinBlurProgram.create(); } if (!skinTextureCreated) { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[3]); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, renderBufferWidth, renderBufferHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); skinTextureCreated = true; } //green blue pass GLES20.glUseProgram(greenAndBlueChannelOverlayProgram); GLES20.glUniform1i(greenAndBlueChannelOverlaySourceImageHandle, 0); GLES20.glEnableVertexAttribArray(greenAndBlueChannelOverlayInputTexCoordHandle); GLES20.glVertexAttribPointer(greenAndBlueChannelOverlayInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(greenAndBlueChannelOverlayPositionHandle); if (isVideo) { GLES20.glUniformMatrix4fv(greenAndBlueChannelOverlayMatrixHandle, 1, false, videoMatrix, 0); } GLES20.glVertexAttribPointer(greenAndBlueChannelOverlayPositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[0]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[0], 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); if (isVideo) { GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, videoTexture); } else { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapTextre[0]); } GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); //blur pass GLES20.glUseProgram(skinBlurProgram.blurShaderProgram); GLES20.glUniform1i(skinBlurProgram.blurSourceImageHandle, 0); GLES20.glEnableVertexAttribArray(skinBlurProgram.blurInputTexCoordHandle); GLES20.glVertexAttribPointer(skinBlurProgram.blurInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(skinBlurProgram.blurPositionHandle); GLES20.glVertexAttribPointer(skinBlurProgram.blurPositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[1]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[1], 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[0]); GLES20.glUniform1f(skinBlurProgram.blurWidthHandle, 0.0f); GLES20.glUniform1f(skinBlurProgram.blurHeightHandle, 1.0f / renderBufferHeight); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[3]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[3], 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[1]); GLES20.glUniform1f(skinBlurProgram.blurWidthHandle, 1.0f / renderBufferWidth); GLES20.glUniform1f(skinBlurProgram.blurHeightHandle, 0.0f); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); //high pass GLES20.glUseProgram(highPassProgram); GLES20.glUniform1i(highPassSourceImageHandle, 0); GLES20.glUniform1i(highPassInputImageHandle, 1); GLES20.glEnableVertexAttribArray(highPassInputTexCoordHandle); GLES20.glVertexAttribPointer(highPassInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(highPassPositionHandle); GLES20.glVertexAttribPointer(highPassPositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[1]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[1], 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[0]); GLES20.glActiveTexture(GLES20.GL_TEXTURE1); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[3]); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); //boost pass GLES20.glUseProgram(boostProgram); GLES20.glUniform1i(boostSourceImageHandle, 0); GLES20.glEnableVertexAttribArray(boostInputTexCoordHandle); GLES20.glVertexAttribPointer(boostInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(boostPositionHandle); GLES20.glVertexAttribPointer(boostPositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[3]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[3], 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[1]); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); needUpdateSkinTexture = false; } skinPassDrawn = true; hsvGenerated = false; GLES20.glUseProgram(compositeProgram); GLES20.glUniform1i(compositeSourceImageHandle, 0); GLES20.glUniform1i(compositeInputImageHandle, 1); GLES20.glUniform1i(compositeCurveImageHandle, 2); GLES20.glUniform1f(compositeMixtureHandle, delegate.getSoftenSkinValue()); GLES20.glEnableVertexAttribArray(compositeInputTexCoordHandle); GLES20.glVertexAttribPointer(compositeInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(compositePositionHandle); GLES20.glVertexAttribPointer(compositePositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); if (isVideo) { GLES20.glUniformMatrix4fv(compositeMatrixHandle, 1, false, videoMatrix, 0); } GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[1]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[1], 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); if (isVideo) { GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, videoTexture); } else { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapTextre[0]); } GLES20.glActiveTexture(GLES20.GL_TEXTURE1); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[3]); GLES20.glActiveTexture(GLES20.GL_TEXTURE2); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, toneCurve.getCurveTexture()); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); return true; } public boolean drawBlurPass() { int blurType = delegate != null ? delegate.getBlurType() : 0; if (isVideo || delegate == null || delegate.shouldShowOriginal() || blurType == 0) { return false; } if (needUpdateBlurTexture) { if (!blurTextureCreated) { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[2]); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, renderBufferWidth, renderBufferHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); blurTextureCreated = true; } GLES20.glUseProgram(blurProgram.blurShaderProgram); GLES20.glUniform1i(blurProgram.blurSourceImageHandle, 0); GLES20.glEnableVertexAttribArray(blurProgram.blurInputTexCoordHandle); GLES20.glVertexAttribPointer(blurProgram.blurInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(blurProgram.blurPositionHandle); GLES20.glVertexAttribPointer(blurProgram.blurPositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[0]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[0], 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[1]); GLES20.glUniform1f(blurProgram.blurWidthHandle, 0.0f); GLES20.glUniform1f(blurProgram.blurHeightHandle, 1.0f / renderBufferHeight); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[2]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[2], 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[0]); GLES20.glUniform1f(blurProgram.blurWidthHandle, 1.0f / renderBufferWidth); GLES20.glUniform1f(blurProgram.blurHeightHandle, 0.0f); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); needUpdateBlurTexture = false; } GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[0]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTexture[0], 0); if (blurType == 1) { GLES20.glUseProgram(radialBlurShaderProgram); GLES20.glUniform1i(radialBlurSourceImageHandle, 0); GLES20.glUniform1i(radialBlurSourceImage2Handle, 1); GLES20.glUniform1f(radialBlurExcludeSizeHandle, delegate.getBlurExcludeSize()); GLES20.glUniform1f(radialBlurExcludeBlurSizeHandle, delegate.getBlurExcludeBlurSize()); Point blurExcludePoint = delegate.getBlurExcludePoint(); GLES20.glUniform2f(radialBlurExcludePointHandle, blurExcludePoint.x, blurExcludePoint.y); GLES20.glUniform1f(radialBlurAspectRatioHandle, (float) renderBufferHeight / (float) renderBufferWidth); GLES20.glEnableVertexAttribArray(radialBlurInputTexCoordHandle); GLES20.glVertexAttribPointer(radialBlurInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(radialBlurPositionHandle); GLES20.glVertexAttribPointer(radialBlurPositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); } else if (blurType == 2) { GLES20.glUseProgram(linearBlurShaderProgram); GLES20.glUniform1i(linearBlurSourceImageHandle, 0); GLES20.glUniform1i(linearBlurSourceImage2Handle, 1); GLES20.glUniform1f(linearBlurExcludeSizeHandle, delegate.getBlurExcludeSize()); GLES20.glUniform1f(linearBlurExcludeBlurSizeHandle, delegate.getBlurExcludeBlurSize()); GLES20.glUniform1f(linearBlurAngleHandle, delegate.getBlurAngle()); Point blurExcludePoint = delegate.getBlurExcludePoint(); GLES20.glUniform2f(linearBlurExcludePointHandle, blurExcludePoint.x, blurExcludePoint.y); GLES20.glUniform1f(linearBlurAspectRatioHandle, (float) renderBufferHeight / (float) renderBufferWidth); GLES20.glEnableVertexAttribArray(linearBlurInputTexCoordHandle); GLES20.glVertexAttribPointer(linearBlurInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(linearBlurPositionHandle); GLES20.glVertexAttribPointer(linearBlurPositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); } GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[1]); GLES20.glActiveTexture(GLES20.GL_TEXTURE1); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTexture[2]); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); return true; } public void onVideoFrameUpdate(float[] m) { videoMatrix = m; //videoFramesCount++; hsvGenerated = false; /*if (videoFramesCount >= 30) { hsvGenerated = false; videoFramesCount = 0; }*/ } private Bitmap createBitmap(Bitmap bitmap, int orientation, float scale) { Matrix matrix = new Matrix(); matrix.setScale(scale, scale); matrix.postRotate(orientation); return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } private void loadTexture(Bitmap bitmap, int orientation, int w, int h) { renderBufferWidth = w; renderBufferHeight = h; if (renderFrameBuffer == null) { renderFrameBuffer = new int[4]; GLES20.glGenFramebuffers(4, renderFrameBuffer, 0); GLES20.glGenTextures(4, renderTexture, 0); } if (bitmap != null && !bitmap.isRecycled()) { GLES20.glGenTextures(1, bitmapTextre, 0); float maxSize = AndroidUtilities.getPhotoSize(); if (renderBufferWidth > maxSize || renderBufferHeight > maxSize || orientation % 360 != 0) { float scale = 1; if (renderBufferWidth > maxSize || renderBufferHeight > maxSize) { float scaleX = maxSize / bitmap.getWidth(); float scaleY = maxSize / bitmap.getHeight(); if (scaleX < scaleY) { renderBufferWidth = (int) maxSize; renderBufferHeight = (int) (bitmap.getHeight() * scaleX); scale = scaleX; } else { renderBufferHeight = (int) maxSize; renderBufferWidth = (int) (bitmap.getWidth() * scaleY); scale = scaleY; } } if (orientation % 360 == 90 || orientation % 360 == 270) { int temp = renderBufferWidth; renderBufferWidth = renderBufferHeight; renderBufferHeight = temp; } bitmap = createBitmap(bitmap, orientation, scale); } GLES20.glBindTexture(GL10.GL_TEXTURE_2D, bitmapTextre[0]); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); } for (int a = 0; a < 2; a++) { GLES20.glBindTexture(GL10.GL_TEXTURE_2D, renderTexture[a]); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, renderBufferWidth, renderBufferHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); } } public FloatBuffer getTextureBuffer() { return textureBuffer; } public FloatBuffer getVertexBuffer() { return vertexBuffer; } public FloatBuffer getVertexInvertBuffer() { return vertexInvertBuffer; } public int getRenderBufferWidth() { return renderBufferWidth; } public int getRenderBufferHeight() { return renderBufferHeight; } public int getRenderTexture(int index) { if (isVideo) { return renderTexture[index == 0 ? 1 : 0]; } return renderTexture[index]; } public int getRenderFrameBuffer() { return renderFrameBuffer != null ? renderFrameBuffer[isVideo ? 0 : 1] : 0; } public void requestUpdateBlurTexture() { needUpdateBlurTexture = true; needUpdateSkinTexture = true; } public static FilterShadersDelegate getFilterShadersDelegate(MediaController.SavedFilterState lastState) { return new FilterShadersDelegate() { @Override public boolean shouldShowOriginal() { return false; } @Override public float getSoftenSkinValue() { return (lastState.softenSkinValue / 100.0f); } @Override public float getShadowsValue() { return (lastState.shadowsValue * 0.55f + 100.0f) / 100.0f; } @Override public float getHighlightsValue() { return (lastState.highlightsValue * 0.75f + 100.0f) / 100.0f; } @Override public float getEnhanceValue() { return (lastState.enhanceValue / 100.0f); } @Override public float getExposureValue() { return (lastState.exposureValue / 100.0f); } @Override public float getContrastValue() { return (lastState.contrastValue / 100.0f) * 0.3f + 1; } @Override public float getWarmthValue() { return lastState.warmthValue / 100.0f; } @Override public float getVignetteValue() { return lastState.vignetteValue / 100.0f; } @Override public float getSharpenValue() { return 0.11f + lastState.sharpenValue / 100.0f * 0.6f; } @Override public float getGrainValue() { return lastState.grainValue / 100.0f * 0.04f; } @Override public float getFadeValue() { return lastState.fadeValue / 100.0f; } @Override public float getTintHighlightsIntensityValue() { float tintHighlightsIntensity = 50.0f; return lastState.tintHighlightsColor == 0 ? 0 : tintHighlightsIntensity / 100.0f; } @Override public float getTintShadowsIntensityValue() { float tintShadowsIntensity = 50.0f; return lastState.tintShadowsColor == 0 ? 0 : tintShadowsIntensity / 100.0f; } @Override public float getSaturationValue() { float parameterValue = (lastState.saturationValue / 100.0f); if (parameterValue > 0) { parameterValue *= 1.05f; } return parameterValue + 1; } @Override public int getTintHighlightsColor() { return lastState.tintHighlightsColor; } @Override public int getTintShadowsColor() { return lastState.tintShadowsColor; } @Override public int getBlurType() { return lastState.blurType; } @Override public float getBlurExcludeSize() { return lastState.blurExcludeSize; } @Override public float getBlurExcludeBlurSize() { return lastState.blurExcludeBlurSize; } @Override public float getBlurAngle() { return lastState.blurAngle; } @Override public Point getBlurExcludePoint() { return lastState.blurExcludePoint; } @Override public boolean shouldDrawCurvesPass() { return !lastState.curvesToolValue.shouldBeSkipped(); } @Override public ByteBuffer fillAndGetCurveBuffer() { lastState.curvesToolValue.fillBuffer(); return lastState.curvesToolValue.curveBuffer; } }; } }