package com.marshalchen.common.uimodule.imageprocessing.input; import java.util.ArrayList; import java.util.List; import com.marshalchen.common.uimodule.imageprocessing.GLRenderer; import com.marshalchen.common.uimodule.imageprocessing.output.GLTextureInputRenderer; import android.opengl.GLES20; /** * A output producing extension of GLRenderer. * This class produces its output in the form of a texture and then sends that texture to * all of the filters or endpoints that have been added as targets to this renderer. * @author Chris Batt */ public abstract class GLTextureOutputRenderer extends GLRenderer { protected int[] frameBuffer; protected int[] texture_out; protected int[] depthRenderBuffer; private List<GLTextureInputRenderer> targets; private Object listLock = new Object(); private boolean dirty; /** * Creates a GLTextureOutputRenderer which initially has an empty list of targets. */ public GLTextureOutputRenderer() { super(); targets = new ArrayList<GLTextureInputRenderer>(); } /** * Adds the given target to the list of targets that this renderer sends its output to. * @param target * The target which should be added to the list of targets that this renderer sends its output to. */ public synchronized void addTarget(GLTextureInputRenderer target) { synchronized(listLock) { targets.add(target); } } /* (non-Javadoc) * @see com.marshalchen.common.uimodule.imageprocessing.GLRenderer#destroy() */ @Override public void destroy() { super.destroy(); if(frameBuffer != null) { GLES20.glDeleteFramebuffers(1, frameBuffer, 0); frameBuffer = null; } if(texture_out != null) { GLES20.glDeleteTextures(1, texture_out, 0); texture_out = null; } if(depthRenderBuffer != null) { GLES20.glDeleteRenderbuffers(1, depthRenderBuffer, 0); depthRenderBuffer = null; } } @Override protected void drawFrame() { if(frameBuffer == null) { if(getWidth() != 0 && getHeight() != 0) { initFBO(); } else { return; } } boolean newData = false; if(dirty) { newData = true; GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer[0]); super.drawFrame(); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); } synchronized(listLock) { for(GLTextureInputRenderer target : targets) { target.newTextureReady(texture_out[0], this, newData); } } } /** * Returns the object used to lock the target list. Iterating over or changing the target list * should be done in a synchronized block that is locked using the object return. * @return lock * the object which is used to lock the target list */ public Object getLockObject() { return listLock; } /** * Returns a list of all the targets that this renderer should send its output to. Iterating over or changing this * list should be done in a synchronized block, locked using the object returned from getLockObject(). * @return targets */ public List<GLTextureInputRenderer> getTargets() { return targets; } @Override protected void handleSizeChange() { initFBO(); } private void initFBO() { if(frameBuffer != null) { GLES20.glDeleteFramebuffers(1, frameBuffer, 0); frameBuffer = null; } if(texture_out != null) { GLES20.glDeleteTextures(1, texture_out, 0); texture_out = null; } if(depthRenderBuffer != null) { GLES20.glDeleteRenderbuffers(1, depthRenderBuffer, 0); depthRenderBuffer = null; } frameBuffer = new int[1]; texture_out = new int[1]; depthRenderBuffer = new int[1]; GLES20.glGenFramebuffers(1, frameBuffer, 0); GLES20.glGenRenderbuffers(1, depthRenderBuffer, 0); GLES20.glGenTextures(1, texture_out, 0); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer[0]); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture_out[0]); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, getWidth(), getHeight(), 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); 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); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texture_out[0], 0); GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthRenderBuffer[0]); GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, getWidth(), getHeight()); GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, depthRenderBuffer[0]); int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) { throw new RuntimeException(this+": Failed to set up render buffer with status "+status+" and error "+GLES20.glGetError()); } } /** * Removes the given target from the list of targets that this renderer sends its output to. * @param target * The target which should be removed from the list of targets that this renderer sends its output to. */ public void removeTarget(GLTextureInputRenderer target) { synchronized(listLock) { targets.remove(target); } } public void markAsDirty() { dirty = true; } }