Skip to content

Commit efda5a9

Browse files
committed
[update by wysaid] Add curve filter. After this commit, the curve filter would not need resource images input any more. Curve algorithm based on Catmull-Rom Spline
1 parent 83c7772 commit efda5a9

3 files changed

Lines changed: 196 additions & 4 deletions

File tree

library/src/jp/co/cyberagent/android/gpuimage/GPUImageBilateralFilter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/**
22
* @author wysaid
33
* @mail admin@wysaid.org
4-
*
54
*/
65

76
package jp.co.cyberagent.android.gpuimage;
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
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+
}

sample/src/jp/co/cyberagent/android/gpuimage/sample/GPUImageFilterTools.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ public static void showDialog(final Context context,
106106

107107
filters.addFilter("Levels Min (Mid Adjust)", FilterType.LEVELS_FILTER_MIN);
108108

109-
filters. addFilter("Bilateral Blur", FilterType.BILATERAL_BLUR);
109+
filters.addFilter("Bilateral Blur", FilterType.BILATERAL_BLUR);
110+
filters.addFilter("Curve Test Case", FilterType.SAMPLE_CURVE);
110111

111112

112113
AlertDialog.Builder builder = new AlertDialog.Builder(context);
@@ -293,6 +294,9 @@ private static GPUImageFilter createFilterForType(final Context context, final F
293294
case BILATERAL_BLUR:
294295
return new GPUImageBilateralFilter();
295296

297+
case SAMPLE_CURVE:
298+
return new GPUImageCurveFilter();
299+
296300
default:
297301
throw new IllegalStateException("No filter of that type!");
298302
}
@@ -320,7 +324,7 @@ private enum FilterType {
320324
BLEND_DISSOLVE, BLEND_EXCLUSION, BLEND_SOURCE_OVER, BLEND_HARD_LIGHT, BLEND_LIGHTEN, BLEND_ADD, BLEND_DIVIDE, BLEND_MULTIPLY, BLEND_OVERLAY, BLEND_SCREEN, BLEND_ALPHA,
321325
BLEND_COLOR, BLEND_HUE, BLEND_SATURATION, BLEND_LUMINOSITY, BLEND_LINEAR_BURN, BLEND_SOFT_LIGHT, BLEND_SUBTRACT, BLEND_CHROMA_KEY, BLEND_NORMAL, LOOKUP_AMATORKA,
322326
GAUSSIAN_BLUR, CROSSHATCH, BOX_BLUR, CGA_COLORSPACE, DILATION, KUWAHARA, RGB_DILATION, SKETCH, TOON, SMOOTH_TOON, BULGE_DISTORTION, GLASS_SPHERE, HAZE, LAPLACIAN, NON_MAXIMUM_SUPPRESSION,
323-
SPHERE_REFRACTION, SWIRL, WEAK_PIXEL_INCLUSION, FALSE_COLOR, COLOR_BALANCE, LEVELS_FILTER_MIN, BILATERAL_BLUR
327+
SPHERE_REFRACTION, SWIRL, WEAK_PIXEL_INCLUSION, FALSE_COLOR, COLOR_BALANCE, LEVELS_FILTER_MIN, BILATERAL_BLUR, SAMPLE_CURVE
324328
}
325329

326330
private static class FilterList {
@@ -397,6 +401,8 @@ public FilterAdjuster(final GPUImageFilter filter) {
397401
adjuster = new LevelsMinMidAdjuster().filter(filter);
398402
} else if (filter instanceof GPUImageBilateralFilter) {
399403
adjuster = new BilateralAdjuster().filter(filter);
404+
} else if(filter instanceof GPUImageCurveFilter) {
405+
adjuster = new CurveAdjuster().filter(filter);
400406
}
401407
else {
402408

@@ -650,7 +656,7 @@ public void adjust(int percentage) {
650656
private class LevelsMinMidAdjuster extends Adjuster<GPUImageLevelsFilter> {
651657
@Override
652658
public void adjust(int percentage) {
653-
getFilter().setMin(0.0f, range(percentage, 0.0f, 1.0f) , 1.0f);
659+
getFilter().setMin(0.0f, range(percentage, 0.0f, 1.0f), 1.0f);
654660
}
655661
}
656662

@@ -661,5 +667,13 @@ public void adjust(final int percentage) {
661667
}
662668
}
663669

670+
private class CurveAdjuster extends Adjuster<GPUImageCurveFilter> {
671+
@Override
672+
public void adjust(final int percentage) {
673+
float value = range(percentage, 0.0f, 1.0f);
674+
getFilter().setCurveByPoints(new float[]{0.0f, 0.0f, 0.5f, value, 1.0f, 1.0f}, null, null);
675+
}
676+
}
677+
664678
}
665679
}

0 commit comments

Comments
 (0)