content: 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);
      }
  }
  