|
| 1 | +/** |
| 2 | + * @author wysaid |
| 3 | + * @mail admin@wysaid.org |
| 4 | + * @refer https://github.com/wysaid/android-gpuimage-plus |
| 5 | + */ |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +package jp.co.cyberagent.android.gpuimage; |
| 10 | + |
| 11 | + |
| 12 | +import android.opengl.GLES20; |
| 13 | + |
| 14 | +import java.nio.ByteBuffer; |
| 15 | + |
| 16 | +public class GPUImageCurveFilter extends GPUImageFilter { |
| 17 | + public static final String CURVE_ADJUST_FRAGMENT_SHADER = "" + |
| 18 | + "precision mediump float;" + |
| 19 | + "varying vec2 textureCoordinate;\n" + |
| 20 | + "uniform sampler2D inputImageTexture;\n" + |
| 21 | + "uniform sampler2D curveTexture; //We do not use sampler1D because GLES dosenot support that.\n" + |
| 22 | + |
| 23 | + "void main()\n" + |
| 24 | + "{\n" + |
| 25 | + " vec4 src = texture2D(inputImageTexture, textureCoordinate);\n" + |
| 26 | + " gl_FragColor = vec4(texture2D(curveTexture, vec2(src.r, 0.0)).r," + |
| 27 | + " texture2D(curveTexture, vec2(src.g, 0.0)).g," + |
| 28 | + " texture2D(curveTexture, vec2(src.b, 0.0)).b," + |
| 29 | + " src.a);\n" + |
| 30 | +// "gl_FragColor = texture2D(curveTexture, textureCoordinate);" + //for testing |
| 31 | + "}"; |
| 32 | + private static int CURVE_PRECISION = 256; |
| 33 | + |
| 34 | + private int mCurveTexLocation; |
| 35 | + private int mCurveTextureID = 0; |
| 36 | + ByteBuffer mCurveBuffer; |
| 37 | + |
| 38 | + public GPUImageCurveFilter() { |
| 39 | + super(NO_FILTER_VERTEX_SHADER, CURVE_ADJUST_FRAGMENT_SHADER); |
| 40 | + } |
| 41 | + |
| 42 | + @Override |
| 43 | + public void onInit() { |
| 44 | + super.onInit(); |
| 45 | + GLES20.glUseProgram(getProgram()); |
| 46 | + mCurveTexLocation = GLES20.glGetUniformLocation(getProgram(), "curveTexture"); |
| 47 | + GLES20.glUniform1i(mCurveTexLocation, 1); //Curve Texture Location is never changed! |
| 48 | + |
| 49 | + //Generate curve texture |
| 50 | + int[] texID = {0}; |
| 51 | + GLES20.glGenTextures(1, texID, 0); |
| 52 | + mCurveTextureID = texID[0]; |
| 53 | + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mCurveTextureID); |
| 54 | + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); |
| 55 | + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); |
| 56 | + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); |
| 57 | + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); |
| 58 | + |
| 59 | + mCurveBuffer = ByteBuffer.allocate(CURVE_PRECISION * 3); |
| 60 | + resetCurve(); |
| 61 | + |
| 62 | + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mCurveTextureID); |
| 63 | + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, 256, 1, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, mCurveBuffer); |
| 64 | + } |
| 65 | + |
| 66 | + @Override |
| 67 | + public void onDestroy() { |
| 68 | + super.onDestroy(); |
| 69 | + GLES20.glDeleteTextures(1, new int[]{mCurveTextureID}, 0); |
| 70 | + } |
| 71 | + |
| 72 | + @Override |
| 73 | + public void onDrawArraysPre() { |
| 74 | + |
| 75 | + //As mCurveTexLocation is already set to 1, just bind the tex id to GL_TEXTURE1. |
| 76 | + GLES20.glActiveTexture(GLES20.GL_TEXTURE1); |
| 77 | + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mCurveTextureID); |
| 78 | + } |
| 79 | + |
| 80 | + // 'points[RGB]' are paired with x and y, and both range from 0 to 1. |
| 81 | + // If any channel shall stay, pass 'null'. |
| 82 | + public void setCurveByPoints(float[] pointsR, float[] pointsG, float[] pointsB) { |
| 83 | + if(pointsR != null && pointsR.length >= 4) |
| 84 | + setSingleCurve(pointsR, 0); |
| 85 | + |
| 86 | + if(pointsG != null && pointsG.length >= 4) |
| 87 | + setSingleCurve(pointsG, 1); |
| 88 | + |
| 89 | + if(pointsB != null && pointsB.length >= 4) |
| 90 | + setSingleCurve(pointsB, 2); |
| 91 | + |
| 92 | + assignCurve(); |
| 93 | + } |
| 94 | + |
| 95 | + |
| 96 | + // Algorithm from: https://github.com/wysaid/android-gpuimage-plus |
| 97 | + private void setSingleCurve(float[] pnts, int stride) { |
| 98 | + |
| 99 | + mCurveBuffer.position(0); |
| 100 | + |
| 101 | + int cnt = pnts.length / 2; |
| 102 | + float[] u = new float[cnt - 1]; |
| 103 | + float[] ypp = new float[cnt]; |
| 104 | + ypp[0] = u[0] = 0.0f; |
| 105 | + |
| 106 | + for(int i=1; i != cnt - 1; ++i) |
| 107 | + { |
| 108 | + int pre = (i - 1) * 2, cur = i * 2, nxt = (i + 1) * 2; |
| 109 | + |
| 110 | + float sig = (pnts[cur] - pnts[pre]) / (pnts[nxt] - pnts[pre]); |
| 111 | + float p = sig * ypp[i - 1] + 2.0f; |
| 112 | + ypp[i] = (sig - 1.0f) / p; |
| 113 | + u[i] = ((pnts[nxt + 1] - pnts[cur + 1]) / (pnts[nxt] - pnts[cur]) - (pnts[cur + 1] - pnts[pre + 1]) / (pnts[cur] - pnts[pre])); |
| 114 | + u[i] = (6.0f * u[i] / (pnts[nxt] - pnts[pre]) - sig * u[i - 1]) / p; |
| 115 | + } |
| 116 | + |
| 117 | + ypp[cnt - 1] = 0.0f; |
| 118 | + for(int i = cnt - 2; i >= 0; --i) |
| 119 | + { |
| 120 | + ypp[i] = ypp[i] * ypp[i+1] + u[i]; |
| 121 | + } |
| 122 | + |
| 123 | + int kL = -1, kH = 0; |
| 124 | + byte[] buffer = mCurveBuffer.array(); |
| 125 | + for(int i = 0; i != CURVE_PRECISION; ++i) |
| 126 | + { |
| 127 | + float t = (float)i/(CURVE_PRECISION - 1); |
| 128 | + |
| 129 | + while(kH < cnt && t > pnts[kH * 2]) |
| 130 | + { |
| 131 | + kL = kH; |
| 132 | + ++kH; |
| 133 | + } |
| 134 | + |
| 135 | + if(kH == cnt) |
| 136 | + { |
| 137 | + buffer[i * 3 + stride] = (byte) (pnts[(cnt-1) * 2 + 1] * 255); |
| 138 | + continue; |
| 139 | + } |
| 140 | + |
| 141 | + if(kL == -1) |
| 142 | + { |
| 143 | + buffer[i * 3 + stride] = (byte) (pnts[1] * 255); |
| 144 | + continue; |
| 145 | + } |
| 146 | + |
| 147 | + float h = pnts[kH * 2] - pnts[kL * 2]; |
| 148 | + float a = (pnts[kH * 2] - t) / h; |
| 149 | + float b = (t - pnts[kL * 2]) / h; |
| 150 | + float g = a * pnts[kL * 2 + 1] + b*pnts[kH * 2 + 1] + ((a*a*a - a)*ypp[kL] + (b*b*b - b) * ypp[kH]) * (h*h) / 6.0f; |
| 151 | + float result = Math.min(Math.max(g, 0.0f), 1.0f); |
| 152 | + buffer[i * 3 + stride] = (byte) (result * 255.0f); |
| 153 | + } |
| 154 | + |
| 155 | + } |
| 156 | + |
| 157 | + private void resetCurve() { |
| 158 | + mCurveBuffer.position(0); |
| 159 | + |
| 160 | + byte[] bytes = mCurveBuffer.array(); |
| 161 | + for(int i = 0; i < CURVE_PRECISION * 3; i += 3) { |
| 162 | + byte tmp = (byte) (i * 255 / (CURVE_PRECISION * 3)); |
| 163 | + bytes[i] = bytes[i + 1] = bytes[i + 2] = tmp; |
| 164 | + } |
| 165 | + } |
| 166 | + |
| 167 | + private void assignCurve() { |
| 168 | + mCurveBuffer.position(0); |
| 169 | + |
| 170 | + runOnDraw(new Runnable() { |
| 171 | + @Override |
| 172 | + public void run() { |
| 173 | + GLES20.glActiveTexture(GLES20.GL_TEXTURE1); |
| 174 | + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mCurveTextureID); |
| 175 | + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, 256, 1, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, mCurveBuffer); |
| 176 | + } |
| 177 | + }); |
| 178 | + } |
| 179 | +} |
0 commit comments