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 34143150..312f2a54 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java @@ -15,6 +15,26 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * A {@link MultiFilter} is a special {@link Filter} that can group one or more filters together. + * When this happens, filters are applied in sequence: + * - the first filter reads from input frames + * - the second filters reads the output of the first + * And so on, until the last filter which will read from the previous and write to the real output. + * + * New filters can be added at any time through {@link #addFilter(Filter)}, but currently they + * can not be removed because we can not easily ensure that they would be correctly released. + * + * The {@link MultiFilter} does also implement {@link OneParameterFilter} and {@link TwoParameterFilter}, + * dispatching all the parameter calls to child filters, assuming they support it. + * + * There are some important technical caveats when using {@link MultiFilter}: + * - each child filter requires the allocation of a GL framebuffer. Using a large number of filters + * will likely cause memory issues (e.g. https://stackoverflow.com/q/6354208/4288782). + * - some of the children need to write into {@link GLES20#GL_TEXTURE_2D} instead of + * {@link GLES11Ext#GL_TEXTURE_EXTERNAL_OES}! To achieve this, we replace samplerExternalOES + * with sampler2D in your fragment shader code. This might cause issues for some shaders. + */ @SuppressWarnings("unused") public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilter { @@ -35,11 +55,19 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt private float parameter1 = 0F; private float parameter2 = 0F; + /** + * Creates a new group with the given filters. + * @param filters children + */ @SuppressWarnings("WeakerAccess") public MultiFilter(@NonNull Filter... filters) { this(Arrays.asList(filters)); } + /** + * Creates a new group with the given filters. + * @param filters children + */ @SuppressWarnings("WeakerAccess") public MultiFilter(@NonNull Collection filters) { for (Filter filter : filters) { @@ -47,6 +75,12 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt } } + /** + * Adds a new filter. It will be used in the next frame. + * If the filter is a {@link MultiFilter}, we'll use its children instead. + * + * @param filter a new filter + */ @SuppressWarnings("WeakerAccess") public void addFilter(@NonNull Filter filter) { if (filter instanceof MultiFilter) { @@ -153,12 +187,14 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt @NonNull @Override public String getVertexShader() { + // Whatever, we won't be using this. return new NoFilter().getVertexShader(); } @NonNull @Override public String getFragmentShader() { + // Whatever, we won't be using this. return new NoFilter().getFragmentShader(); } diff --git a/docs/_posts/2019-08-06-filters.md b/docs/_posts/2019-08-06-filters.md index e1a864e4..56c80a85 100644 --- a/docs/_posts/2019-08-06-filters.md +++ b/docs/_posts/2019-08-06-filters.md @@ -91,6 +91,27 @@ camera.mapGesture(Gesture.SCROLL_HORIZONTAL, GestureAction.FILTER_CONTROL_1); camera.mapGesture(Gesture.SCROLL_VERTICAL, GestureAction.FILTER_CONTROL_2); ``` +### MultiFilter + +CameraView provides a special filter called `MultiFilter` which can be used to group different filters +together, and apply them in sequence to the input frame, in order to process it more than once. + +```java +camera.setFilter(new MultiFilter(firstFilter, secondFilter, thirdFilter)); +``` + +You can even add new filters to the group at any time, using `MultiFilter.addFilter()`. +The `MultiFilter` will also try to dispatch [filter controls](#filters-controls) (e.g. from gestures) +to its children. + +There are some technical caveats when using a `MultiFilter`: + +- Using a large number of child filters can consume the available graphic memory +- For [advanced users](#advanced-usage), child filters need to read from `GLES20.GL_TEXTURE_2D` + instead of the typical `GLES11Ext.GL_TEXTURE_EXTERNAL_OES`. To achieve this, we get your fragment + shader String and replace any `"samplerExternalOES"` with `"sampler2D"`. This is a hack + that might cause issues with specific shader implementations. + ### Advanced usage Advanced users with OpenGL experience can create their own filters by implementing the `Filter` interface