/* * Copyright (C) 2012 CyberAgent * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jp.co.cyberagent.android.gpuimage; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.AssetManager; import android.graphics.PointF; import android.opengl.GLES20; import java.io.InputStream; import java.nio.FloatBuffer; import java.util.LinkedList; @SuppressLint("NewApi") public class GPUImageFilter { public static final String NO_FILTER_VERTEX_SHADER = "" + "attribute vec4 position;\n" + "attribute vec4 inputTextureCoordinate;\n" + " \n" + "varying vec2 textureCoordinate;\n" + " \n" + "void main()\n" + "{\n" + " gl_Position = position;\n" + " textureCoordinate = inputTextureCoordinate.xy;\n" + "}"; public static final String NO_FILTER_FRAGMENT_SHADER = "" + "varying highp vec2 textureCoordinate;\n" + " \n" + "uniform sampler2D inputImageTexture;\n" + " \n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(inputImageTexture, textureCoordinate);\n" + "}"; private final LinkedList<Runnable> mRunOnDraw; private final String mVertexShader; private final String mFragmentShader; protected int mGLProgId; protected int mGLAttribPosition; protected int mGLUniformTexture; protected int mGLAttribTextureCoordinate; protected int mOutputWidth; protected int mOutputHeight; private boolean mIsInitialized; public GPUImageFilter() { this(NO_FILTER_VERTEX_SHADER, NO_FILTER_FRAGMENT_SHADER); } public GPUImageFilter(final String vertexShader, final String fragmentShader) { mRunOnDraw = new LinkedList<Runnable>(); mVertexShader = vertexShader; mFragmentShader = fragmentShader; } public final void init() { onInit(); mIsInitialized = true; onInitialized(); } public void onInit() { mGLProgId = OpenGlUtils.loadProgram(mVertexShader, mFragmentShader); mGLAttribPosition = GLES20.glGetAttribLocation(mGLProgId, "position"); mGLUniformTexture = GLES20.glGetUniformLocation(mGLProgId, "inputImageTexture"); mGLAttribTextureCoordinate = GLES20.glGetAttribLocation(mGLProgId, "inputTextureCoordinate"); mIsInitialized = true; } public void onInitialized() { } public final void destroy() { mIsInitialized = false; GLES20.glDeleteProgram(mGLProgId); onDestroy(); } public void onDestroy() { } public void onOutputSizeChanged(final int width, final int height) { mOutputWidth = width; mOutputHeight = height; } public void onDraw(final int textureId, final FloatBuffer cubeBuffer, final FloatBuffer textureBuffer) { GLES20.glUseProgram(mGLProgId); runPendingOnDrawTasks(); if (!mIsInitialized) { return; } cubeBuffer.position(0); GLES20.glVertexAttribPointer(mGLAttribPosition, 2, GLES20.GL_FLOAT, false, 0, cubeBuffer); GLES20.glEnableVertexAttribArray(mGLAttribPosition); textureBuffer.position(0); GLES20.glVertexAttribPointer(mGLAttribTextureCoordinate, 2, GLES20.GL_FLOAT, false, 0, textureBuffer); GLES20.glEnableVertexAttribArray(mGLAttribTextureCoordinate); if (textureId != OpenGlUtils.NO_TEXTURE) { GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); GLES20.glUniform1i(mGLUniformTexture, 0); } onDrawArraysPre(); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glDisableVertexAttribArray(mGLAttribPosition); GLES20.glDisableVertexAttribArray(mGLAttribTextureCoordinate); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); } protected void onDrawArraysPre() {} protected void runPendingOnDrawTasks() { while (!mRunOnDraw.isEmpty()) { mRunOnDraw.removeFirst().run(); } } public boolean isInitialized() { return mIsInitialized; } public int getOutputWidth() { return mOutputWidth; } public int getOutputHeight() { return mOutputHeight; } public int getProgram() { return mGLProgId; } public int getAttribPosition() { return mGLAttribPosition; } public int getAttribTextureCoordinate() { return mGLAttribTextureCoordinate; } public int getUniformTexture() { return mGLUniformTexture; } protected void setInteger(final int location, final int intValue) { runOnDraw(new Runnable() { @Override public void run() { GLES20.glUniform1i(location, intValue); } }); } protected void setFloat(final int location, final float floatValue) { runOnDraw(new Runnable() { @Override public void run() { GLES20.glUniform1f(location, floatValue); } }); } protected void setFloatVec2(final int location, final float[] arrayValue) { runOnDraw(new Runnable() { @Override public void run() { GLES20.glUniform2fv(location, 1, FloatBuffer.wrap(arrayValue)); } }); } protected void setFloatVec3(final int location, final float[] arrayValue) { runOnDraw(new Runnable() { @Override public void run() { GLES20.glUniform3fv(location, 1, FloatBuffer.wrap(arrayValue)); } }); } protected void setFloatVec4(final int location, final float[] arrayValue) { runOnDraw(new Runnable() { @Override public void run() { GLES20.glUniform4fv(location, 1, FloatBuffer.wrap(arrayValue)); } }); } protected void setFloatArray(final int location, final float[] arrayValue) { runOnDraw(new Runnable() { @Override public void run() { GLES20.glUniform1fv(location, arrayValue.length, FloatBuffer.wrap(arrayValue)); } }); } protected void setPoint(final int location, final PointF point) { runOnDraw(new Runnable() { @Override public void run() { float[] vec2 = new float[2]; vec2[0] = point.x; vec2[1] = point.y; GLES20.glUniform2fv(location, 1, vec2, 0); } }); } protected void setUniformMatrix3f(final int location, final float[] matrix) { runOnDraw(new Runnable() { @Override public void run() { GLES20.glUniformMatrix3fv(location, 1, false, matrix, 0); } }); } protected void setUniformMatrix4f(final int location, final float[] matrix) { runOnDraw(new Runnable() { @Override public void run() { GLES20.glUniformMatrix4fv(location, 1, false, matrix, 0); } }); } protected void runOnDraw(final Runnable runnable) { synchronized (mRunOnDraw) { mRunOnDraw.addLast(runnable); } } public static String loadShader(String file, Context context) { try { AssetManager assetManager = context.getAssets(); InputStream ims = assetManager.open(file); String re = convertStreamToString(ims); ims.close(); return re; } catch (Exception e) { e.printStackTrace(); } return ""; } public static String convertStreamToString(java.io.InputStream is) { java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; } }