Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add curve filter. (no resource images are required) #229

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -57,7 +57,8 @@ public GPUImageView(Context context, AttributeSet attrs) {

private void init(Context context, AttributeSet attrs) {
mGLSurfaceView = new GPUImageGLSurfaceView(context, attrs);
mGLSurfaceView.setZOrderOnTop(true);
// mGLSurfaceView.setZOrderOnTop(true);
mGLSurfaceView.setZOrderMediaOverlay(true);
mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
mGLSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
addView(mGLSurfaceView);
Expand Down
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);
}
}

}
}