diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/MultiFilterTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/MultiFilterTest.java index 4482abae..40c4957b 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/MultiFilterTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/MultiFilterTest.java @@ -24,9 +24,11 @@ import java.util.ArrayList; import java.util.List; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; @@ -172,8 +174,8 @@ public class MultiFilterTest extends BaseEglTest { public Object answer(InvocationOnMock invocation) { MultiFilter.State state = multiFilter.states.get(filter1); assertNotNull(state); - assertTrue(state.framebufferId != -1); - assertTrue(state.textureId != -1); + assertTrue(state.isCreated); + assertTrue(state.isFramebufferCreated); GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0); assertTrue(result[0] != 0); @@ -181,20 +183,22 @@ public class MultiFilterTest extends BaseEglTest { } }).when(filter1).draw(matrix); + // Note: second filter is drawn with the identity matrix! doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { // The last filter has no FBO / texture. MultiFilter.State state = multiFilter.states.get(filter2); assertNotNull(state); - assertEquals(-1, state.framebufferId); - assertEquals(-1, state.textureId); + assertTrue(state.isCreated); + assertFalse(state.isFramebufferCreated); GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0); assertEquals(0, result[0]); return null; + } - }).when(filter2).draw(matrix); + }).when(filter2).draw(any(float[].class)); EglViewport viewport = new EglViewport(multiFilter); int texture = viewport.createTexture(); @@ -203,7 +207,7 @@ public class MultiFilterTest extends BaseEglTest { // Verify that both are drawn. verify(filter1, times(1)).draw(matrix); - verify(filter2, times(1)).draw(matrix); + verify(filter2, times(1)).draw(any(float[].class)); } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java index 1213cce3..440b84cb 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java @@ -39,15 +39,15 @@ import java.util.Map; @SuppressWarnings("unused") public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilter { - private final static int TARGET = GLES20.GL_TEXTURE_2D; - @VisibleForTesting static class State { - boolean isCreated = false; - int programHandle = -1; - int framebufferId = -1; - int textureId = -1; - Size size = null; + @VisibleForTesting boolean isCreated = false; + @VisibleForTesting boolean isFramebufferCreated = false; + @VisibleForTesting Size size = null; + + private int programHandle = -1; + private int framebufferId = -1; + private int textureId = -1; } @VisibleForTesting final List filters = new ArrayList<>(); @@ -135,7 +135,9 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt if (isLast) return; State state = states.get(filter); //noinspection ConstantConditions - if (state.framebufferId == -1) { + if (!state.isFramebufferCreated) { + state.isFramebufferCreated = true; + int[] framebufferArray = new int[1]; int[] textureArray = new int[1]; GLES20.glGenFramebuffers(1, framebufferArray, 0); @@ -143,16 +145,16 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt state.framebufferId = framebufferArray[0]; state.textureId = textureArray[0]; - GLES20.glBindTexture(TARGET, state.textureId); - GLES20.glTexImage2D(TARGET, 0, GLES20.GL_RGBA, state.size.getWidth(), state.size.getHeight(), 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); - GLES20.glTexParameterf(TARGET, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameterf(TARGET, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameterf(TARGET, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameterf(TARGET, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.textureId); + 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.framebufferId); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, - TARGET, + GLES20.GL_TEXTURE_2D, state.textureId, 0); @@ -161,7 +163,7 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt throw new RuntimeException("Invalid framebuffer generation. Error:" + status); } - GLES20.glBindTexture(TARGET, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); } } @@ -169,7 +171,8 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt private void maybeDestroyFramebuffer(@NonNull Filter filter) { State state = states.get(filter); //noinspection ConstantConditions - if (state.framebufferId != -1) { + if (state.isFramebufferCreated) { + state.isFramebufferCreated = false; GLES20.glDeleteFramebuffers(1, new int[]{state.framebufferId}, 0); state.framebufferId = -1; GLES20.glDeleteTextures(1, new int[]{state.textureId}, 0); @@ -264,9 +267,9 @@ 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(TARGET, state.textureId); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.textureId); } else { - GLES20.glBindTexture(TARGET, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); } GLES20.glUseProgram(0);