|  |  |  | @ -10,6 +10,8 @@ import com.otaliastudios.cameraview.size.Size; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.opengl.core.Egloo; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.opengl.program.GlProgram; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.opengl.program.GlTextureProgram; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.opengl.texture.GlFramebuffer; | 
			
		
	
		
			
				
					|  |  |  |  | import com.otaliastudios.opengl.texture.GlTexture; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | import java.util.ArrayList; | 
			
		
	
		
			
				
					|  |  |  |  | import java.util.Arrays; | 
			
		
	
	
		
			
				
					|  |  |  | @ -48,8 +50,8 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt | 
			
		
	
		
			
				
					|  |  |  |  |         @VisibleForTesting boolean isFramebufferCreated = false; | 
			
		
	
		
			
				
					|  |  |  |  |         @VisibleForTesting Size size = null; | 
			
		
	
		
			
				
					|  |  |  |  |         private int programHandle = -1; | 
			
		
	
		
			
				
					|  |  |  |  |         private int outputFramebufferId = -1; | 
			
		
	
		
			
				
					|  |  |  |  |         private int outputTextureId = -1; | 
			
		
	
		
			
				
					|  |  |  |  |         private GlFramebuffer outputFramebuffer = null; | 
			
		
	
		
			
				
					|  |  |  |  |         private GlTexture outputTexture = null; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @VisibleForTesting final List<Filter> filters = new ArrayList<>(); | 
			
		
	
	
		
			
				
					|  |  |  | @ -136,40 +138,12 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt | 
			
		
	
		
			
				
					|  |  |  |  |         //noinspection ConstantConditions
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (state.isFramebufferCreated || isLast) return; | 
			
		
	
		
			
				
					|  |  |  |  |         state.isFramebufferCreated = true; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         int[] framebufferArray = new int[1]; | 
			
		
	
		
			
				
					|  |  |  |  |         int[] textureArray = new int[1]; | 
			
		
	
		
			
				
					|  |  |  |  |         GLES20.glGenFramebuffers(1, framebufferArray, 0); | 
			
		
	
		
			
				
					|  |  |  |  |         GLES20.glGenTextures(1, textureArray, 0); | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputFramebufferId = framebufferArray[0]; | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputTextureId = textureArray[0]; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.outputTextureId); | 
			
		
	
		
			
				
					|  |  |  |  |         GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, | 
			
		
	
		
			
				
					|  |  |  |  |                 state.size.getWidth(), state.size.getHeight(), 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, state.outputFramebufferId); | 
			
		
	
		
			
				
					|  |  |  |  |         GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, | 
			
		
	
		
			
				
					|  |  |  |  |                 GLES20.GL_COLOR_ATTACHMENT0, | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputTexture = new GlTexture(GLES20.GL_TEXTURE0, | 
			
		
	
		
			
				
					|  |  |  |  |                 GLES20.GL_TEXTURE_2D, | 
			
		
	
		
			
				
					|  |  |  |  |                 state.outputTextureId, | 
			
		
	
		
			
				
					|  |  |  |  |                 0); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); | 
			
		
	
		
			
				
					|  |  |  |  |         if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) { | 
			
		
	
		
			
				
					|  |  |  |  |             throw new RuntimeException("Invalid framebuffer generation. Error:" + status); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); | 
			
		
	
		
			
				
					|  |  |  |  |         GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); | 
			
		
	
		
			
				
					|  |  |  |  |                 state.size.getWidth(), | 
			
		
	
		
			
				
					|  |  |  |  |                 state.size.getHeight()); | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputFramebuffer = new GlFramebuffer(); | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputFramebuffer.attach(state.outputTexture); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private void maybeDestroyFramebuffer(@NonNull Filter filter) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -177,10 +151,10 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt | 
			
		
	
		
			
				
					|  |  |  |  |         //noinspection ConstantConditions
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (!state.isFramebufferCreated) return; | 
			
		
	
		
			
				
					|  |  |  |  |         state.isFramebufferCreated = false; | 
			
		
	
		
			
				
					|  |  |  |  |         GLES20.glDeleteFramebuffers(1, new int[]{state.outputFramebufferId}, 0); | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputFramebufferId = -1; | 
			
		
	
		
			
				
					|  |  |  |  |         GLES20.glDeleteTextures(1, new int[]{state.outputTextureId}, 0); | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputTextureId = -1; | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputFramebuffer.release(); | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputFramebuffer = null; | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputTexture.release(); | 
			
		
	
		
			
				
					|  |  |  |  |         state.outputTexture = null; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private void maybeSetSize(@NonNull Filter filter) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -240,6 +214,7 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt | 
			
		
	
		
			
				
					|  |  |  |  |                 boolean isLast = i == filters.size() - 1; | 
			
		
	
		
			
				
					|  |  |  |  |                 Filter filter = filters.get(i); | 
			
		
	
		
			
				
					|  |  |  |  |                 State state = states.get(filter); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |                 maybeSetSize(filter); | 
			
		
	
		
			
				
					|  |  |  |  |                 maybeCreateProgram(filter, isFirst, isLast); | 
			
		
	
		
			
				
					|  |  |  |  |                 maybeCreateFramebuffer(filter, isFirst, isLast); | 
			
		
	
	
		
			
				
					|  |  |  | @ -251,7 +226,7 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt | 
			
		
	
		
			
				
					|  |  |  |  |                 // Each filter outputs into its own framebuffer object, except the
 | 
			
		
	
		
			
				
					|  |  |  |  |                 // last filter, which outputs into the default framebuffer.
 | 
			
		
	
		
			
				
					|  |  |  |  |                 if (!isLast) { | 
			
		
	
		
			
				
					|  |  |  |  |                     GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, state.outputFramebufferId); | 
			
		
	
		
			
				
					|  |  |  |  |                     GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, state.outputFramebuffer.getId()); | 
			
		
	
		
			
				
					|  |  |  |  |                     GLES20.glClearColor(0, 0, 0, 0); | 
			
		
	
		
			
				
					|  |  |  |  |                 } else { | 
			
		
	
		
			
				
					|  |  |  |  |                     GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); | 
			
		
	
	
		
			
				
					|  |  |  | @ -270,7 +245,7 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt | 
			
		
	
		
			
				
					|  |  |  |  |                 // It is the framebuffer texture from this cycle. If this is the last
 | 
			
		
	
		
			
				
					|  |  |  |  |                 // filter, reset this value just to cleanup.
 | 
			
		
	
		
			
				
					|  |  |  |  |                 if (!isLast) { | 
			
		
	
		
			
				
					|  |  |  |  |                     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.outputTextureId); | 
			
		
	
		
			
				
					|  |  |  |  |                     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.outputTexture.getId()); | 
			
		
	
		
			
				
					|  |  |  |  |                 } else { | 
			
		
	
		
			
				
					|  |  |  |  |                     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
	
		
			
				
					|  |  |  | @ -285,6 +260,9 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt | 
			
		
	
		
			
				
					|  |  |  |  |     public Filter copy() { | 
			
		
	
		
			
				
					|  |  |  |  |         synchronized (lock) { | 
			
		
	
		
			
				
					|  |  |  |  |             MultiFilter copy = new MultiFilter(); | 
			
		
	
		
			
				
					|  |  |  |  |             if (size != null) { | 
			
		
	
		
			
				
					|  |  |  |  |                 copy.setSize(size.getWidth(), size.getHeight()); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             for (Filter filter : filters) { | 
			
		
	
		
			
				
					|  |  |  |  |                 copy.addFilter(filter.copy()); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
	
		
			
				
					|  |  |  | 
 |