Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package the.dreams.wind.blendingfilter;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.opengl.GLES20;
- import android.opengl.GLException;
- import android.opengl.GLSurfaceView;
- import android.opengl.GLUtils;
- import android.support.annotation.NonNull;
- import android.view.WindowManager;
- import java.lang.ref.WeakReference;
- import java.nio.ByteBuffer;
- import java.nio.ByteOrder;
- import java.nio.FloatBuffer;
- import java.util.Objects;
- import javax.microedition.khronos.egl.EGLConfig;
- import javax.microedition.khronos.opengles.GL10;
- class BlendingFilterRenderer implements GLSurfaceView.Renderer {
- @NonNull
- private final Bitmap mBitmap;
- @NonNull
- private final WeakReference<GLSurfaceView> mHostViewReference;
- @NonNull
- private final float[] mColorFilter;
- @NonNull
- private final BlendingFilterUtil.Callback mCallback;
- private boolean mFinished = false;
- // ========================================== //
- // Lifecycle
- // ========================================== //
- BlendingFilterRenderer(@NonNull GLSurfaceView hostView, @NonNull Bitmap bitmap,
- @NonNull float[] colorFilter,
- @NonNull BlendingFilterUtil.Callback callback)
- throws IllegalArgumentException {
- if (colorFilter.length != 4 * 5) {
- throw new IllegalArgumentException("Color filter should be a 4 x 5 matrix");
- }
- mBitmap = bitmap;
- mHostViewReference = new WeakReference<>(hostView);
- mColorFilter = colorFilter;
- mCallback = callback;
- }
- // ========================================== //
- // GLSurfaceView.Renderer
- // ========================================== //
- @Override
- public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- GLES20.glEnable(GLES20.GL_BLEND);
- GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ZERO);
- }
- @Override
- public void onSurfaceChanged(GL10 gl, int width, int height) {
- GLES20.glViewport(0, 0, width, height);
- final int program = loadProgram();
- GLES20.glUseProgram(program);
- initVertices(program);
- attachTexture(program);
- attachColorFilter(program);
- }
- @Override
- public void onDrawFrame(GL10 gl) {
- GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
- GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
- postResult();
- }
- // ========================================== //
- // Private
- // ========================================== //
- private int loadShader(int type, String shaderCode) throws GLException {
- int reference = GLES20.glCreateShader(type);
- GLES20.glShaderSource(reference, shaderCode);
- GLES20.glCompileShader(reference);
- int[] compileStatus = new int[1];
- GLES20.glGetShaderiv(reference, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
- if (compileStatus[0] != GLES20.GL_TRUE) {
- GLES20.glDeleteShader(reference);
- final String message = GLES20.glGetShaderInfoLog(reference);
- throw new GLException(compileStatus[0], message);
- }
- return reference;
- }
- private int loadProgram() {
- int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, "precision mediump float;" +
- "struct ColorFilter {" +
- " mat4 factor;" +
- " vec4 shift;" +
- "};" +
- "uniform sampler2D uSampler;" +
- "uniform ColorFilter uColorFilter;" +
- "varying vec2 vTextureCoord;" +
- "void main() {" +
- " vec4 originalColor = texture2D(uSampler, vTextureCoord);" +
- " originalColor.rgb *= originalColor.a;" +
- " vec4 filteredColor = (originalColor * uColorFilter.factor) + uColorFilter.shift;" +
- " filteredColor.rgb *= filteredColor.a;" +
- " gl_FragColor = vec4(originalColor.rgb - filteredColor.rgb, originalColor.a);" +
- "}");
- int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, "attribute vec2 aPosition;" +
- "attribute vec2 aTextureCoord;" +
- "varying vec2 vTextureCoord;" +
- "void main() {" +
- " gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0);" +
- " vTextureCoord = aTextureCoord;"+
- "}");
- int programReference = GLES20.glCreateProgram();
- GLES20.glAttachShader(programReference, vertexShader);
- GLES20.glAttachShader(programReference, fragmentShader);
- GLES20.glLinkProgram(programReference);
- return programReference;
- }
- private FloatBuffer convertToBuffer(float[] array) {
- final ByteBuffer buffer = ByteBuffer.allocateDirect(array.length * PrimitiveSizes.FLOAT);
- FloatBuffer output = buffer.order(ByteOrder.nativeOrder()).asFloatBuffer();
- output.put(array);
- output.position(0);
- return output;
- }
- private void initVertices(int programReference) {
- // for GL the texture coordinates are flipped vertically, but it doesn't matter for the
- // purpose of this utility, as we should return the image back to the android coordinate
- // system in the end
- createVerticesBuffer(new float[] {
- //NDCS coords //UV map
- -1, 1, 0, 1,
- -1, -1, 0, 0,
- 1, 1, 1, 1,
- 1, -1, 1, 0
- });
- final int stride = 4 * PrimitiveSizes.FLOAT;
- enableVertexAttribute(programReference, "aPosition", 2, stride, 0);
- enableVertexAttribute(programReference, "aTextureCoord", 2, stride,
- 2 * PrimitiveSizes.FLOAT);
- }
- private void attachTexture(int programReference) {
- final int[] textures = new int[1];
- GLES20.glGenTextures(1, textures, 0);
- final int textureId = textures[0];
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
- GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
- GLES20.GL_NEAREST);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
- GLES20.GL_NEAREST);
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
- GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0);
- GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
- final int samplerLocation = GLES20.glGetUniformLocation(programReference, "uSampler");
- GLES20.glUniform1i(samplerLocation, 0);
- }
- private void attachColorFilter(int program) {
- final float[] colorFilterFactor = new float[4 * 4];
- final float[] colorFilterShift = new float[4];
- for (int i = 0; i < mColorFilter.length; i++) {
- final float value = mColorFilter[i];
- final int calculateIndex = i + 1;
- if (calculateIndex % 5 == 0) {
- colorFilterShift[calculateIndex / 5 - 1] = value / 255;
- } else {
- colorFilterFactor[i - calculateIndex / 5] = value;
- }
- }
- final int colorFactorLocation = GLES20.glGetUniformLocation(program,
- "uColorFilter.factor");
- GLES20.glUniformMatrix4fv(
- colorFactorLocation, 1, false, colorFilterFactor, 0
- );
- final int colorShiftLocation = GLES20.glGetUniformLocation(program,
- "uColorFilter.shift");
- GLES20.glUniform4fv(
- colorShiftLocation, 1, colorFilterShift, 0
- );
- }
- private void createVerticesBuffer(float[] verticesData) {
- final int verticesBuffer = createGlBuffer();
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, verticesBuffer);
- GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, verticesData.length * PrimitiveSizes.FLOAT,
- convertToBuffer(verticesData), GLES20.GL_STREAM_DRAW);
- }
- private int createGlBuffer() {
- int buffers[] = new int[1];
- GLES20.glGenBuffers(1, buffers, 0);
- return buffers[0];
- }
- @SuppressWarnings("SameParameterValue")
- private void enableVertexAttribute(int program, String attributeName, int size, int stride,
- int offset) {
- final int attributeLocation = GLES20.glGetAttribLocation(program, attributeName);
- GLES20.glVertexAttribPointer(attributeLocation, size, GLES20.GL_FLOAT, false,
- stride, offset);
- GLES20.glEnableVertexAttribArray(attributeLocation);
- }
- private Bitmap retrieveBitmapFromGl(int width, int height) {
- final ByteBuffer pixelBuffer = ByteBuffer.allocateDirect(width * height *
- PrimitiveSizes.FLOAT);
- pixelBuffer.order(ByteOrder.LITTLE_ENDIAN);
- GLES20.glReadPixels(0,0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
- pixelBuffer);
- final Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- image.copyPixelsFromBuffer(pixelBuffer);
- return image;
- }
- private GLException getGlError() {
- int errorValue = GLES20.glGetError();
- switch (errorValue) {
- case GLES20.GL_NO_ERROR:
- return null;
- default:
- return new GLException(errorValue);
- }
- }
- private void postResult() {
- if (mFinished) {
- return;
- }
- final GLSurfaceView hostView = mHostViewReference.get();
- if (hostView == null) {
- return;
- }
- GLException glError = getGlError();
- if (glError != null) {
- hostView.post(() -> {
- mCallback.onFailure(glError);
- removeHostView(hostView);
- });
- } else {
- final Bitmap result = retrieveBitmapFromGl(mBitmap.getWidth(), mBitmap.getHeight());
- hostView.post(() -> {
- mCallback.onSuccess(result);
- removeHostView(hostView);
- });
- }
- mFinished = true;
- }
- private void removeHostView(@NonNull GLSurfaceView hostView) {
- if (hostView.getParent() == null) {
- return;
- }
- final WindowManager windowManager = (WindowManager) hostView.getContext()
- .getSystemService(Context.WINDOW_SERVICE);
- Objects.requireNonNull(windowManager).removeView(hostView);
- }
- }
Add Comment
Please, Sign In to add comment