Skip to content

Commit

Permalink
[update by wysaid] Add curve filter. After this commit, the curve fil…
Browse files Browse the repository at this point in the history
…ter would not need resource images input any more. The curve algorithm is based on Catmull-Rom Spline
  • Loading branch information
wysaid committed Nov 23, 2015
1 parent 83c7772 commit e4ab6dc
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/**
* @author wysaid
* @mail [email protected]
*
*/

package jp.co.cyberagent.android.gpuimage;
Expand Down
179 changes: 179 additions & 0 deletions library/src/jp/co/cyberagent/android/gpuimage/GPUImageCurveFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/**
* @author wysaid
* @mail [email protected]
* @refer https://github.com/wysaid/android-gpuimage-plus
*/



package jp.co.cyberagent.android.gpuimage;


import android.opengl.GLES20;

import java.nio.ByteBuffer;

public class GPUImageCurveFilter extends GPUImageFilter {
public static final String CURVE_ADJUST_FRAGMENT_SHADER = "" +
"precision mediump float;" +
"varying vec2 textureCoordinate;\n" +
"uniform sampler2D inputImageTexture;\n" +
"uniform sampler2D curveTexture; //We do not use sampler1D because GLES dosenot support that.\n" +

"void main()\n" +
"{\n" +
" vec4 src = texture2D(inputImageTexture, textureCoordinate);\n" +
" gl_FragColor = vec4(texture2D(curveTexture, vec2(src.r, 0.0)).r," +
" texture2D(curveTexture, vec2(src.g, 0.0)).g," +
" texture2D(curveTexture, vec2(src.b, 0.0)).b," +
" src.a);\n" +
// "gl_FragColor = texture2D(curveTexture, textureCoordinate);" + //for testing
"}";
private static int CURVE_PRECISION = 256;

private int mCurveTexLocation;
private int mCurveTextureID = 0;
ByteBuffer mCurveBuffer;

public GPUImageCurveFilter() {
super(NO_FILTER_VERTEX_SHADER, CURVE_ADJUST_FRAGMENT_SHADER);
}

@Override
public void onInit() {
super.onInit();
GLES20.glUseProgram(getProgram());
mCurveTexLocation = GLES20.glGetUniformLocation(getProgram(), "curveTexture");
GLES20.glUniform1i(mCurveTexLocation, 1); //Curve Texture Location is never changed!

//Generate curve texture
int[] texID = {0};
GLES20.glGenTextures(1, texID, 0);
mCurveTextureID = texID[0];
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mCurveTextureID);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

mCurveBuffer = ByteBuffer.allocate(CURVE_PRECISION * 3);
resetCurve();

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mCurveTextureID);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, 256, 1, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, mCurveBuffer);
}

@Override
public void onDestroy() {
super.onDestroy();
GLES20.glDeleteTextures(1, new int[]{mCurveTextureID}, 0);
}

@Override
public void onDrawArraysPre() {

//As mCurveTexLocation is already set to 1, just bind the tex id to GL_TEXTURE1.
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mCurveTextureID);
}

// 'points[RGB]' are paired with x and y, and both range from 0 to 1.
// If any channel shall stay, pass 'null'.
public void setCurveByPoints(float[] pointsR, float[] pointsG, float[] pointsB) {
if(pointsR != null && pointsR.length >= 4)
setSingleCurve(pointsR, 0);

if(pointsG != null && pointsG.length >= 4)
setSingleCurve(pointsG, 1);

if(pointsB != null && pointsB.length >= 4)
setSingleCurve(pointsB, 2);

assignCurve();
}


// Algorithm from: https://github.com/wysaid/android-gpuimage-plus
private void setSingleCurve(float[] pnts, int stride) {

mCurveBuffer.position(0);

int cnt = pnts.length / 2;
float[] u = new float[cnt - 1];
float[] ypp = new float[cnt];
ypp[0] = u[0] = 0.0f;

for(int i=1; i != cnt - 1; ++i)
{
int pre = (i - 1) * 2, cur = i * 2, nxt = (i + 1) * 2;

float sig = (pnts[cur] - pnts[pre]) / (pnts[nxt] - pnts[pre]);
float p = sig * ypp[i - 1] + 2.0f;
ypp[i] = (sig - 1.0f) / p;
u[i] = ((pnts[nxt + 1] - pnts[cur + 1]) / (pnts[nxt] - pnts[cur]) - (pnts[cur + 1] - pnts[pre + 1]) / (pnts[cur] - pnts[pre]));
u[i] = (6.0f * u[i] / (pnts[nxt] - pnts[pre]) - sig * u[i - 1]) / p;
}

ypp[cnt - 1] = 0.0f;
for(int i = cnt - 2; i >= 0; --i)
{
ypp[i] = ypp[i] * ypp[i+1] + u[i];
}

int kL = -1, kH = 0;
byte[] buffer = mCurveBuffer.array();
for(int i = 0; i != CURVE_PRECISION; ++i)
{
float t = (float)i/(CURVE_PRECISION - 1);

while(kH < cnt && t > pnts[kH * 2])
{
kL = kH;
++kH;
}

if(kH == cnt)
{
buffer[i * 3 + stride] = (byte) (pnts[(cnt-1) * 2 + 1] * 255);
continue;
}

if(kL == -1)
{
buffer[i * 3 + stride] = (byte) (pnts[1] * 255);
continue;
}

float h = pnts[kH * 2] - pnts[kL * 2];
float a = (pnts[kH * 2] - t) / h;
float b = (t - pnts[kL * 2]) / h;
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;
float result = Math.min(Math.max(g, 0.0f), 1.0f);
buffer[i * 3 + stride] = (byte) (result * 255.0f);
}

}

private void resetCurve() {
mCurveBuffer.position(0);

byte[] bytes = mCurveBuffer.array();
for(int i = 0; i < CURVE_PRECISION * 3; i += 3) {
byte tmp = (byte) (i * 255 / (CURVE_PRECISION * 3));
bytes[i] = bytes[i + 1] = bytes[i + 2] = tmp;
}
}

private void assignCurve() {
mCurveBuffer.position(0);

runOnDraw(new Runnable() {
@Override
public void run() {
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mCurveTextureID);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, 256, 1, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, mCurveBuffer);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ public static void showDialog(final Context context,

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

filters. addFilter("Bilateral Blur", FilterType.BILATERAL_BLUR);
filters.addFilter("Bilateral Blur", FilterType.BILATERAL_BLUR);
filters.addFilter("Curve Test Case", FilterType.SAMPLE_CURVE);


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

case SAMPLE_CURVE:
return new GPUImageCurveFilter();

default:
throw new IllegalStateException("No filter of that type!");
}
Expand Down Expand Up @@ -320,7 +324,7 @@ private enum FilterType {
BLEND_DISSOLVE, BLEND_EXCLUSION, BLEND_SOURCE_OVER, BLEND_HARD_LIGHT, BLEND_LIGHTEN, BLEND_ADD, BLEND_DIVIDE, BLEND_MULTIPLY, BLEND_OVERLAY, BLEND_SCREEN, BLEND_ALPHA,
BLEND_COLOR, BLEND_HUE, BLEND_SATURATION, BLEND_LUMINOSITY, BLEND_LINEAR_BURN, BLEND_SOFT_LIGHT, BLEND_SUBTRACT, BLEND_CHROMA_KEY, BLEND_NORMAL, LOOKUP_AMATORKA,
GAUSSIAN_BLUR, CROSSHATCH, BOX_BLUR, CGA_COLORSPACE, DILATION, KUWAHARA, RGB_DILATION, SKETCH, TOON, SMOOTH_TOON, BULGE_DISTORTION, GLASS_SPHERE, HAZE, LAPLACIAN, NON_MAXIMUM_SUPPRESSION,
SPHERE_REFRACTION, SWIRL, WEAK_PIXEL_INCLUSION, FALSE_COLOR, COLOR_BALANCE, LEVELS_FILTER_MIN, BILATERAL_BLUR
SPHERE_REFRACTION, SWIRL, WEAK_PIXEL_INCLUSION, FALSE_COLOR, COLOR_BALANCE, LEVELS_FILTER_MIN, BILATERAL_BLUR, SAMPLE_CURVE
}

private static class FilterList {
Expand Down Expand Up @@ -397,6 +401,8 @@ public FilterAdjuster(final GPUImageFilter filter) {
adjuster = new LevelsMinMidAdjuster().filter(filter);
} else if (filter instanceof GPUImageBilateralFilter) {
adjuster = new BilateralAdjuster().filter(filter);
} else if(filter instanceof GPUImageCurveFilter) {
adjuster = new CurveAdjuster().filter(filter);
}
else {

Expand Down Expand Up @@ -650,7 +656,7 @@ public void adjust(int percentage) {
private class LevelsMinMidAdjuster extends Adjuster<GPUImageLevelsFilter> {
@Override
public void adjust(int percentage) {
getFilter().setMin(0.0f, range(percentage, 0.0f, 1.0f) , 1.0f);
getFilter().setMin(0.0f, range(percentage, 0.0f, 1.0f), 1.0f);
}
}

Expand All @@ -661,5 +667,13 @@ public void adjust(final int percentage) {
}
}

private class CurveAdjuster extends Adjuster<GPUImageCurveFilter> {
@Override
public void adjust(final int percentage) {
float value = range(percentage, 0.0f, 1.0f);
getFilter().setCurveByPoints(new float[]{0.0f, 0.0f, 0.5f, value, 1.0f, 1.0f}, null, null);
}
}

}
}

0 comments on commit e4ab6dc

Please sign in to comment.