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.outputs.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;
}
}