/* * 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.opengl.GLES20; import jp.co.cyberagent.android.gpuimage.util.TextureRotationUtil; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.List; import static jp.co.cyberagent.android.gpuimage.GPUImageRenderer.CUBE; import static jp.co.cyberagent.android.gpuimage.util.TextureRotationUtil.TEXTURE_NO_ROTATION; /** * Resembles a filter that consists of multiple filters applied after each * other. */ public class GPUImageFilterGroup extends GPUImageFilter { protected List<GPUImageFilter> mFilters; protected List<GPUImageFilter> mMergedFilters; private int[] mFrameBuffers; private int[] mFrameBufferTextures; private final FloatBuffer mGLCubeBuffer; private final FloatBuffer mGLTextureBuffer; private final FloatBuffer mGLTextureFlipBuffer; /** * Instantiates a new GPUImageFilterGroup with no filters. */ public GPUImageFilterGroup() { this(null); } /** * Instantiates a new GPUImageFilterGroup with the given filters. * * @param filters the filters which represent this filter */ public GPUImageFilterGroup(List<GPUImageFilter> filters) { mFilters = filters; if (mFilters == null) { mFilters = new ArrayList<GPUImageFilter>(); } else { updateMergedFilters(); } mGLCubeBuffer = ByteBuffer.allocateDirect(CUBE.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); mGLCubeBuffer.put(CUBE).position(0); mGLTextureBuffer = ByteBuffer.allocateDirect(TEXTURE_NO_ROTATION.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); mGLTextureBuffer.put(TEXTURE_NO_ROTATION).position(0); float[] flipTexture = TextureRotationUtil.getRotation(Rotation.NORMAL, false, true); mGLTextureFlipBuffer = ByteBuffer.allocateDirect(flipTexture.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); mGLTextureFlipBuffer.put(flipTexture).position(0); } public void addFilter(GPUImageFilter aFilter) { if (aFilter == null) { return; } mFilters.add(aFilter); updateMergedFilters(); } /* * (non-Javadoc) * @see jp.co.cyberagent.android.gpuimage.GPUImageFilter#onInit() */ @Override public void onInit() { super.onInit(); for (GPUImageFilter filter : mFilters) { filter.init(); } } /* * (non-Javadoc) * @see jp.co.cyberagent.android.gpuimage.GPUImageFilter#onDestroy() */ @Override public void onDestroy() { destroyFramebuffers(); for (GPUImageFilter filter : mFilters) { filter.destroy(); } super.onDestroy(); } private void destroyFramebuffers() { if (mFrameBufferTextures != null) { GLES20.glDeleteTextures(mFrameBufferTextures.length, mFrameBufferTextures, 0); mFrameBufferTextures = null; } if (mFrameBuffers != null) { GLES20.glDeleteFramebuffers(mFrameBuffers.length, mFrameBuffers, 0); mFrameBuffers = null; } } /* * (non-Javadoc) * @see * jp.co.cyberagent.android.gpuimage.GPUImageFilter#onOutputSizeChanged(int, * int) */ @Override public void onOutputSizeChanged(final int width, final int height) { super.onOutputSizeChanged(width, height); if (mFrameBuffers != null) { destroyFramebuffers(); } int size = mFilters.size(); for (int i = 0; i < size; i++) { mFilters.get(i).onOutputSizeChanged(width, height); } if (mMergedFilters != null && mMergedFilters.size() > 0) { size = mMergedFilters.size(); mFrameBuffers = new int[size - 1]; mFrameBufferTextures = new int[size - 1]; for (int i = 0; i < size - 1; i++) { GLES20.glGenFramebuffers(1, mFrameBuffers, i); GLES20.glGenTextures(1, mFrameBufferTextures, i); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFrameBufferTextures[i]); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mFrameBufferTextures[i], 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); } } } /* * (non-Javadoc) * @see jp.co.cyberagent.android.gpuimage.GPUImageFilter#onDraw(int, * java.nio.FloatBuffer, java.nio.FloatBuffer) */ @SuppressLint("WrongCall") @Override public void onDraw(final int textureId, final FloatBuffer cubeBuffer, final FloatBuffer textureBuffer) { runPendingOnDrawTasks(); if (!isInitialized() || mFrameBuffers == null || mFrameBufferTextures == null) { return; } if (mMergedFilters != null) { int size = mMergedFilters.size(); int previousTexture = textureId; for (int i = 0; i < size; i++) { GPUImageFilter filter = mMergedFilters.get(i); boolean isNotLast = i < size - 1; if (isNotLast) { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]); GLES20.glClearColor(0, 0, 0, 0); } if (i == 0) { filter.onDraw(previousTexture, cubeBuffer, textureBuffer); } else if (i == size - 1) { filter.onDraw(previousTexture, mGLCubeBuffer, (size % 2 == 0) ? mGLTextureFlipBuffer : mGLTextureBuffer); } else { filter.onDraw(previousTexture, mGLCubeBuffer, mGLTextureBuffer); } if (isNotLast) { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); previousTexture = mFrameBufferTextures[i]; } } } } /** * Gets the filters. * * @return the filters */ public List<GPUImageFilter> getFilters() { return mFilters; } public List<GPUImageFilter> getMergedFilters() { return mMergedFilters; } public void updateMergedFilters() { if (mFilters == null) { return; } if (mMergedFilters == null) { mMergedFilters = new ArrayList<GPUImageFilter>(); } else { mMergedFilters.clear(); } List<GPUImageFilter> filters; for (GPUImageFilter filter : mFilters) { if (filter instanceof GPUImageFilterGroup) { ((GPUImageFilterGroup) filter).updateMergedFilters(); filters = ((GPUImageFilterGroup) filter).getMergedFilters(); if (filters == null || filters.isEmpty()) continue; mMergedFilters.addAll(filters); continue; } mMergedFilters.add(filter); } } }