package jp.co.cyberagent.android.gpuimage;
import android.opengl.GLES20;
/**
* Created by edward_chiang on 13/10/16.
*/
public class GPUImageColorBalanceFilter extends GPUImageFilter {
public static final String GPU_IMAGE_COLOR_BALANCE_FRAGMENT_SHADER = "" +
"varying highp vec2 textureCoordinate;\n" +
"uniform sampler2D inputImageTexture;\n" +
"uniform lowp vec3 shadowsShift;\n" +
"uniform lowp vec3 midtonesShift;\n" +
"uniform lowp vec3 highlightsShift;\n" +
"uniform int preserveLuminosity;\n" +
"lowp vec3 RGBToHSL(lowp vec3 color)\n" +
"{\n" +
"lowp vec3 hsl; // init to 0 to avoid warnings ? (and reverse if + remove first part)\n" +
"lowp float fmin = min(min(color.r, color.g), color.b); //Min. value of RGB\n" +
"lowp float fmax = max(max(color.r, color.g), color.b); //Max. value of RGB\n" +
"lowp float delta = fmax - fmin; //Delta RGB value\n" +
"hsl.z = (fmax + fmin) / 2.0; // Luminance\n" +
"if (delta == 0.0) //This is a gray, no chroma...\n" +
"{\n" +
" hsl.x = 0.0; // Hue\n" +
" hsl.y = 0.0; // Saturation\n" +
"}\n" +
"else //Chromatic data...\n" +
"{\n" +
" if (hsl.z < 0.5)\n" +
" hsl.y = delta / (fmax + fmin); // Saturation\n" +
" else\n"+
" hsl.y = delta / (2.0 - fmax - fmin); // Saturation\n" +
"\n" +
" lowp float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta;\n" +
" lowp float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta;\n" +
" lowp float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta;\n" +
"\n" +
" if (color.r == fmax )\n" +
" hsl.x = deltaB - deltaG; // Hue\n" +
" else if (color.g == fmax)\n" +
" hsl.x = (1.0 / 3.0) + deltaR - deltaB; // Hue\n" +
" else if (color.b == fmax)\n" +
" hsl.x = (2.0 / 3.0) + deltaG - deltaR; // Hue\n" +
" if (hsl.x < 0.0)\n" +
" hsl.x += 1.0; // Hue\n" +
" else if (hsl.x > 1.0)\n" +
" hsl.x -= 1.0; // Hue\n" +
"}\n" +
"\n" +
"return hsl;\n" +
"}\n" +
"lowp float HueToRGB(lowp float f1, lowp float f2, lowp float hue)\n" +
"{\n"+
" if (hue < 0.0)\n"+
" hue += 1.0;\n"+
" else if (hue > 1.0)\n"+
" hue -= 1.0;\n"+
" lowp float res;\n"+
" if ((6.0 * hue) < 1.0)\n"+
" res = f1 + (f2 - f1) * 6.0 * hue;\n"+
" else if ((2.0 * hue) < 1.0)\n"+
" res = f2;\n"+
" else if ((3.0 * hue) < 2.0)\n"+
" res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;\n"+
" else\n"+
" res = f1;\n"+
" return res;\n"+
"}\n"+
"lowp vec3 HSLToRGB(lowp vec3 hsl)\n"+
"{\n" +
" lowp vec3 rgb;\n" +
" if (hsl.y == 0.0)\n" +
" rgb = vec3(hsl.z); // Luminance\n" +
" else\n" +
" {\n" +
" lowp float f2;\n" +
" if (hsl.z < 0.5)\n" +
" f2 = hsl.z * (1.0 + hsl.y);\n" +
" else\n" +
" f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z);\n" +
" lowp float f1 = 2.0 * hsl.z - f2;\n" +
" rgb.r = HueToRGB(f1, f2, hsl.x + (1.0/3.0));\n" +
" rgb.g = HueToRGB(f1, f2, hsl.x);\n" +
" rgb.b= HueToRGB(f1, f2, hsl.x - (1.0/3.0));\n" +
" }\n" +
" return rgb;\n "+
"}\n" +
"lowp float RGBToL(lowp vec3 color)\n" +
"{\n" +
" lowp float fmin = min(min(color.r, color.g), color.b); //Min. value of RGB\n" +
" lowp float fmax = max(max(color.r, color.g), color.b); //Max. value of RGB\n" +
" return (fmax + fmin) / 2.0; // Luminance\n" +
"}\n" +
"void main()\n"+
"{\n"+
" lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);\n" +
" // Alternative way:\n" +
" //lowp vec3 lightness = RGBToL(textureColor.rgb);\n" +
" lowp vec3 lightness = textureColor.rgb;\n" +
" const lowp float a = 0.25;\n" +
" const lowp float b = 0.333;\n" +
" const lowp float scale = 0.7;\n" +
" lowp vec3 shadows = shadowsShift * (clamp((lightness - b) / -a + 0.5, 0.0, 1.0) * scale);\n" +
" lowp vec3 midtones = midtonesShift * (clamp((lightness - b) / a + 0.5, 0.0, 1.0) *\n" +
" clamp((lightness + b - 1.0) / -a + 0.5, 0.0, 1.0) * scale);\n" +
" lowp vec3 highlights = highlightsShift * (clamp((lightness + b - 1.0) / a + 0.5, 0.0, 1.0) * scale);\n" +
" mediump vec3 newColor = textureColor.rgb + shadows + midtones + highlights;\n"+
" newColor = clamp(newColor, 0.0, 1.0);\n "+
" if (preserveLuminosity != 0) {\n "+
" lowp vec3 newHSL = RGBToHSL(newColor);\n" +
" lowp float oldLum = RGBToL(textureColor.rgb);\n" +
" textureColor.rgb = HSLToRGB(vec3(newHSL.x, newHSL.y, oldLum));\n" +
" gl_FragColor = textureColor;\n" +
" } else {\n" +
" gl_FragColor = vec4(newColor.rgb, textureColor.w);\n" +
" }\n" +
"}\n";
private int mShadowsLocation;
private int mMidtonesLocation;
private int mHighlightsLocation;
private int mPreserveLuminosityLocation;
private float[] showdows;
private float[] midtones;
private float[] highlights;
private boolean preserveLuminosity;
public GPUImageColorBalanceFilter() {
super(NO_FILTER_VERTEX_SHADER, GPU_IMAGE_COLOR_BALANCE_FRAGMENT_SHADER);
this.showdows = new float[]{0.0f, 0.0f, 0.0f};
this.midtones = new float[]{0.0f, 0.0f, 0.0f};
this.highlights = new float[]{0.0f, 0.0f, 0.0f};
this.preserveLuminosity = true;
}
@Override
public void onInit() {
super.onInit();
mShadowsLocation = GLES20.glGetUniformLocation(getProgram(), "shadowsShift");
mMidtonesLocation = GLES20.glGetUniformLocation(getProgram(), "midtonesShift");
mHighlightsLocation = GLES20.glGetUniformLocation(getProgram(), "highlightsShift");
mPreserveLuminosityLocation = GLES20.glGetUniformLocation(getProgram(), "preserveLuminosity");
}
@Override
public void onInitialized() {
super.onInitialized();
setMidtones(this.midtones);
setShowdows(this.showdows);
setHighlights(this.highlights);
setPreserveLuminosity(this.preserveLuminosity);
}
public void setShowdows(float[] showdows) {
this.showdows = showdows;
setFloatVec3(mShadowsLocation, showdows);
}
public void setMidtones(float[] midtones) {
this.midtones = midtones;
setFloatVec3(mMidtonesLocation, midtones);
}
public void setHighlights(float[] highlights) {
this.highlights = highlights;
setFloatVec3(mHighlightsLocation, highlights);
}
public void setPreserveLuminosity(boolean preserveLuminosity) {
this.preserveLuminosity = preserveLuminosity;
setInteger(mPreserveLuminosityLocation, preserveLuminosity ? 1: 0);
}
}