Add FILTER_CONTROL_1 and FILTER_CONTROL_2 to control filters with gestures

pull/537/head
Mattia Iavarone 6 years ago
parent bf41489279
commit a9ff98a88c
  1. 6
      README.md
  2. 2
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java
  3. 83
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  4. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java
  5. 26
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  6. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/gesture/Gesture.java
  7. 21
      cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureAction.java
  8. 6
      cameraview/src/main/res/values/attrs.xml
  9. 4
      demo/src/main/res/layout/activity_camera.xml
  10. 30
      docs/_posts/2018-12-20-gestures.md
  11. 12
      docs/_posts/2019-08-06-filters.md

@ -82,9 +82,9 @@ motivation boost to push the library forward.
app:cameraAudioBitRate="@integer/audio_bit_rate" app:cameraAudioBitRate="@integer/audio_bit_rate"
app:cameraGestureTap="none|autoFocus|takePicture" app:cameraGestureTap="none|autoFocus|takePicture"
app:cameraGestureLongTap="none|autoFocus|takePicture" app:cameraGestureLongTap="none|autoFocus|takePicture"
app:cameraGesturePinch="none|zoom|exposureCorrection" app:cameraGesturePinch="none|zoom|exposureCorrection|filterControl1|filterControl2"
app:cameraGestureScrollHorizontal="none|zoom|exposureCorrection" app:cameraGestureScrollHorizontal="none|zoom|exposureCorrection|filterControl1|filterControl2"
app:cameraGestureScrollVertical="none|zoom|exposureCorrection" app:cameraGestureScrollVertical="none|zoom|exposureCorrection|filterControl1|filterControl2"
app:cameraEngine="camera1|camera2" app:cameraEngine="camera1|camera2"
app:cameraPreview="glSurface|surface|texture" app:cameraPreview="glSurface|surface|texture"
app:cameraFacing="back|front" app:cameraFacing="back|front"

@ -221,6 +221,8 @@ public class CameraOptions1Test extends BaseTest {
assertTrue(o.supports(GestureAction.TAKE_PICTURE)); assertTrue(o.supports(GestureAction.TAKE_PICTURE));
assertTrue(o.supports(GestureAction.NONE)); assertTrue(o.supports(GestureAction.NONE));
assertTrue(o.supports(GestureAction.ZOOM)); assertTrue(o.supports(GestureAction.ZOOM));
assertTrue(o.supports(GestureAction.FILTER_CONTROL_1));
assertTrue(o.supports(GestureAction.FILTER_CONTROL_2));
assertFalse(o.supports(GestureAction.EXPOSURE_CORRECTION)); assertFalse(o.supports(GestureAction.EXPOSURE_CORRECTION));
} }

@ -25,6 +25,7 @@ import com.otaliastudios.cameraview.engine.CameraEngine;
import com.otaliastudios.cameraview.filter.Filter; import com.otaliastudios.cameraview.filter.Filter;
import com.otaliastudios.cameraview.filter.Filters; import com.otaliastudios.cameraview.filter.Filters;
import com.otaliastudios.cameraview.filter.NoFilter; import com.otaliastudios.cameraview.filter.NoFilter;
import com.otaliastudios.cameraview.filters.DuotoneFilter;
import com.otaliastudios.cameraview.frame.Frame; import com.otaliastudios.cameraview.frame.Frame;
import com.otaliastudios.cameraview.frame.FrameProcessor; import com.otaliastudios.cameraview.frame.FrameProcessor;
import com.otaliastudios.cameraview.gesture.Gesture; import com.otaliastudios.cameraview.gesture.Gesture;
@ -355,6 +356,88 @@ public class CameraViewTest extends BaseTest {
assertTrue(mockController.mExposureCorrectionChanged); assertTrue(mockController.mExposureCorrectionChanged);
} }
@Test
public void testGestureAction_filterControl1() {
mockController.setMockEngineState(true);
DuotoneFilter filter = new DuotoneFilter(); // supports two parameters
filter.setParameter1(0F);
filter = spy(filter);
cameraView.setExperimental(true);
cameraView.setFilter(filter);
mockController.mExposureCorrectionChanged = false;
MotionEvent event = MotionEvent.obtain(0L, 0L, 0, 0f, 0f, 0);
final FactorHolder factor = new FactorHolder();
uiSync(new Runnable() {
@Override
public void run() {
cameraView.mScrollGestureFinder = new ScrollGestureFinder(cameraView.mCameraCallbacks) {
@Override
protected boolean handleTouchEvent(@NonNull MotionEvent event) {
setGesture(Gesture.SCROLL_HORIZONTAL);
return true;
}
@Override
protected float getFactor() {
return factor.value;
}
};
cameraView.mapGesture(Gesture.SCROLL_HORIZONTAL, GestureAction.FILTER_CONTROL_1);
}
});
// If factor is 0, we return the same value. The filter should not be notified.
factor.value = 0f;
cameraView.dispatchTouchEvent(event);
verify(filter, never()).setParameter1(anyFloat());
// For larger factors, the value is scaled. The filter should be notified.
factor.value = 1f;
cameraView.dispatchTouchEvent(event);
verify(filter, times(1)).setParameter1(anyFloat());
}
@Test
public void testGestureAction_filterControl2() {
mockController.setMockEngineState(true);
DuotoneFilter filter = new DuotoneFilter(); // supports two parameters
filter.setParameter2(0F);
filter = spy(filter);
cameraView.setExperimental(true);
cameraView.setFilter(filter);
mockController.mExposureCorrectionChanged = false;
MotionEvent event = MotionEvent.obtain(0L, 0L, 0, 0f, 0f, 0);
final FactorHolder factor = new FactorHolder();
uiSync(new Runnable() {
@Override
public void run() {
cameraView.mScrollGestureFinder = new ScrollGestureFinder(cameraView.mCameraCallbacks) {
@Override
protected boolean handleTouchEvent(@NonNull MotionEvent event) {
setGesture(Gesture.SCROLL_HORIZONTAL);
return true;
}
@Override
protected float getFactor() {
return factor.value;
}
};
cameraView.mapGesture(Gesture.SCROLL_HORIZONTAL, GestureAction.FILTER_CONTROL_2);
}
});
// If factor is 0, we return the same value. The filter should not be notified.
factor.value = 0f;
cameraView.dispatchTouchEvent(event);
verify(filter, never()).setParameter2(anyFloat());
// For larger factors, the value is scaled. The filter should be notified.
factor.value = 1f;
cameraView.dispatchTouchEvent(event);
verify(filter, times(1)).setParameter2(anyFloat());
}
//endregion //endregion
//region testMeasure //region testMeasure

@ -273,6 +273,8 @@ public class CameraOptions {
case AUTO_FOCUS: case AUTO_FOCUS:
return isAutoFocusSupported(); return isAutoFocusSupported();
case TAKE_PICTURE: case TAKE_PICTURE:
case FILTER_CONTROL_1:
case FILTER_CONTROL_2:
case NONE: case NONE:
return true; return true;
case ZOOM: case ZOOM:

@ -51,6 +51,8 @@ import com.otaliastudios.cameraview.filter.Filter;
import com.otaliastudios.cameraview.filter.FilterParser; import com.otaliastudios.cameraview.filter.FilterParser;
import com.otaliastudios.cameraview.filter.Filters; import com.otaliastudios.cameraview.filter.Filters;
import com.otaliastudios.cameraview.filter.NoFilter; import com.otaliastudios.cameraview.filter.NoFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.filter.TwoParameterFilter;
import com.otaliastudios.cameraview.frame.Frame; import com.otaliastudios.cameraview.frame.Frame;
import com.otaliastudios.cameraview.frame.FrameProcessor; import com.otaliastudios.cameraview.frame.FrameProcessor;
import com.otaliastudios.cameraview.gesture.Gesture; import com.otaliastudios.cameraview.gesture.Gesture;
@ -627,6 +629,30 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mCameraEngine.setExposureCorrection(newValue, bounds, points, true); mCameraEngine.setExposureCorrection(newValue, bounds, points, true);
} }
break; break;
case FILTER_CONTROL_1:
if (!mExperimental) break;
if (getFilter() instanceof OneParameterFilter) {
OneParameterFilter filter = (OneParameterFilter) getFilter();
oldValue = filter.getParameter1();
newValue = source.computeValue(oldValue, 0, 1);
if (newValue != oldValue) {
filter.setParameter1(newValue);
}
}
break;
case FILTER_CONTROL_2:
if (!mExperimental) break;
if (getFilter() instanceof TwoParameterFilter) {
TwoParameterFilter filter = (TwoParameterFilter) getFilter();
oldValue = filter.getParameter2();
newValue = source.computeValue(oldValue, 0, 1);
if (newValue != oldValue) {
filter.setParameter2(newValue);
}
}
break;
} }
} }

@ -22,6 +22,8 @@ public enum Gesture {
* *
* - {@link GestureAction#ZOOM} * - {@link GestureAction#ZOOM}
* - {@link GestureAction#EXPOSURE_CORRECTION} * - {@link GestureAction#EXPOSURE_CORRECTION}
* - {@link GestureAction#FILTER_CONTROL_1}
* - {@link GestureAction#FILTER_CONTROL_2}
* - {@link GestureAction#NONE} * - {@link GestureAction#NONE}
*/ */
PINCH(GestureType.CONTINUOUS), PINCH(GestureType.CONTINUOUS),
@ -52,6 +54,8 @@ public enum Gesture {
* *
* - {@link GestureAction#ZOOM} * - {@link GestureAction#ZOOM}
* - {@link GestureAction#EXPOSURE_CORRECTION} * - {@link GestureAction#EXPOSURE_CORRECTION}
* - {@link GestureAction#FILTER_CONTROL_1}
* - {@link GestureAction#FILTER_CONTROL_2}
* - {@link GestureAction#NONE} * - {@link GestureAction#NONE}
*/ */
SCROLL_HORIZONTAL(GestureType.CONTINUOUS), SCROLL_HORIZONTAL(GestureType.CONTINUOUS),
@ -62,6 +66,8 @@ public enum Gesture {
* *
* - {@link GestureAction#ZOOM} * - {@link GestureAction#ZOOM}
* - {@link GestureAction#EXPOSURE_CORRECTION} * - {@link GestureAction#EXPOSURE_CORRECTION}
* - {@link GestureAction#FILTER_CONTROL_1}
* - {@link GestureAction#FILTER_CONTROL_2}
* - {@link GestureAction#NONE} * - {@link GestureAction#NONE}
*/ */
SCROLL_VERTICAL(GestureType.CONTINUOUS); SCROLL_VERTICAL(GestureType.CONTINUOUS);

@ -60,8 +60,27 @@ public enum GestureAction {
* - {@link Gesture#SCROLL_HORIZONTAL} * - {@link Gesture#SCROLL_HORIZONTAL}
* - {@link Gesture#SCROLL_VERTICAL} * - {@link Gesture#SCROLL_VERTICAL}
*/ */
EXPOSURE_CORRECTION(4, GestureType.CONTINUOUS); EXPOSURE_CORRECTION(4, GestureType.CONTINUOUS),
/**
* Controls the first parameter of a real-time {@link com.otaliastudios.cameraview.filter.Filter},
* if it accepts one. This action can be mapped to continuous gestures:
*
* - {@link Gesture#PINCH}
* - {@link Gesture#SCROLL_HORIZONTAL}
* - {@link Gesture#SCROLL_VERTICAL}
*/
FILTER_CONTROL_1(5, GestureType.CONTINUOUS),
/**
* Controls the second parameter of a real-time {@link com.otaliastudios.cameraview.filter.Filter},
* if it accepts one. This action can be mapped to continuous gestures:
*
* - {@link Gesture#PINCH}
* - {@link Gesture#SCROLL_HORIZONTAL}
* - {@link Gesture#SCROLL_VERTICAL}
*/
FILTER_CONTROL_2(6, GestureType.CONTINUOUS);
final static GestureAction DEFAULT_PINCH = NONE; final static GestureAction DEFAULT_PINCH = NONE;
final static GestureAction DEFAULT_TAP = NONE; final static GestureAction DEFAULT_TAP = NONE;

@ -44,18 +44,24 @@
<enum name="none" value="0" /> <enum name="none" value="0" />
<enum name="zoom" value="3" /> <enum name="zoom" value="3" />
<enum name="exposureCorrection" value="4" /> <enum name="exposureCorrection" value="4" />
<enum name="filterControl1" value="5" />
<enum name="filterControl2" value="6" />
</attr> </attr>
<attr name="cameraGestureScrollHorizontal" format="enum"> <attr name="cameraGestureScrollHorizontal" format="enum">
<enum name="none" value="0" /> <enum name="none" value="0" />
<enum name="zoom" value="3" /> <enum name="zoom" value="3" />
<enum name="exposureCorrection" value="4" /> <enum name="exposureCorrection" value="4" />
<enum name="filterControl1" value="5" />
<enum name="filterControl2" value="6" />
</attr> </attr>
<attr name="cameraGestureScrollVertical" format="enum"> <attr name="cameraGestureScrollVertical" format="enum">
<enum name="none" value="0" /> <enum name="none" value="0" />
<enum name="zoom" value="3" /> <enum name="zoom" value="3" />
<enum name="exposureCorrection" value="4" /> <enum name="exposureCorrection" value="4" />
<enum name="filterControl1" value="5" />
<enum name="filterControl2" value="6" />
</attr> </attr>
<attr name="cameraEngine" format="enum"> <attr name="cameraEngine" format="enum">

@ -26,8 +26,8 @@
app:cameraGestureTap="autoFocus" app:cameraGestureTap="autoFocus"
app:cameraGestureLongTap="none" app:cameraGestureLongTap="none"
app:cameraGesturePinch="zoom" app:cameraGesturePinch="zoom"
app:cameraGestureScrollHorizontal="exposureCorrection" app:cameraGestureScrollHorizontal="filterControl1"
app:cameraGestureScrollVertical="none" app:cameraGestureScrollVertical="exposureCorrection"
app:cameraMode="picture" app:cameraMode="picture"
app:cameraAutoFocusMarker="@string/cameraview_default_autofocus_marker"> app:cameraAutoFocusMarker="@string/cameraview_default_autofocus_marker">

@ -26,21 +26,35 @@ Simple as that. There are two things to be noted:
|Gesture|Description|Can be mapped to| |Gesture|Description|Can be mapped to|
|-------------|-----------|----------------| |-------------|-----------|----------------|
|`PINCH`|Pinch gesture, typically assigned to the zoom control.|`zoom` `exposureCorrection` `none`| |`PINCH`|Pinch gesture, typically assigned to the zoom control.|`ZOOM` `EXPOSURE_CORRECTION` `FILTER_CONTROL_1` `FILTER_CONTROL_2` `NONE`|
|`TAP`|Single tap gesture, typically assigned to the focus control.|`autoFocus` `takePicture` `none`| |`TAP`|Single tap gesture, typically assigned to the focus control.|`AUTO_FOCUS` `TAKE_PICTURE` `NONE`|
|`LONG_TAP`|Long tap gesture.|`autoFocus` `takePicture` `none`| |`LONG_TAP`|Long tap gesture.|`AUTO_FOCUS` `TAKE_PICTURE` `NONE`|
|`SCROLL_HORIZONTAL`|Horizontal movement gesture.|`zoom` `exposureCorrection` `none`| |`SCROLL_HORIZONTAL`|Horizontal movement gesture.|`ZOOM` `EXPOSURE_CORRECTION` `FILTER_CONTROL_1` `FILTER_CONTROL_2` `NONE`|
|`SCROLL_VERTICAL`|Vertical movement gesture.|`zoom` `exposureCorrection` `none`| |`SCROLL_VERTICAL`|Vertical movement gesture.|`ZOOM` `EXPOSURE_CORRECTION` `FILTER_CONTROL_1` `FILTER_CONTROL_2` `NONE`|
### Gesture Actions
Looking at this from the other side:
|Gesture action|Description|Can be mapped to|
|--------------|-----------|----------------|
|`NONE`|Disables this gesture.|`TAP` `LONG_TAP` `PINCH` `SCROLL_HORIZONTAL` `SCROLL_VERTICAL`|
|`AUTO_FOCUS`|Launches an [auto-focus operation](controls.html#auto-focus) on the finger position.|`TAP` `LONG_TAP`|
|`TAKE_PICTURE`|Takes a picture using [takePicture](capturing-media.html).|`TAP` `LONG_TAP`|
|`ZOOM`|[Zooms](controls.html#zoom) in or out.|`PINCH` `SCROLL_HORIZONTAL` `SCROLL_VERTICAL`|
|`EXPOSURE_CORRECTION`|Controls the [exposure correction](controls.html#exposure-correction).|`PINCH` `SCROLL_HORIZONTAL` `SCROLL_VERTICAL`|
|`FILTER_CONTROL_1`|Controls the first parameter (if any) of a [real-time filter](filters.html).|`PINCH` `SCROLL_HORIZONTAL` `SCROLL_VERTICAL`|
|`FILTER_CONTROL_2`|Controls the second parameter (if any) of a [real-time filter](filters.html).|`PINCH` `SCROLL_HORIZONTAL` `SCROLL_VERTICAL`|
### XML Attributes ### XML Attributes
```xml ```xml
<com.otaliastudios.cameraview.CameraView <com.otaliastudios.cameraview.CameraView
app:cameraGesturePinch="zoom|exposureCorrection|none" app:cameraGesturePinch="zoom|exposureCorrection|filterControl1|filterControl2|none"
app:cameraGestureTap="autoFocus|takePicture|none" app:cameraGestureTap="autoFocus|takePicture|none"
app:cameraGestureLongTap="autoFocus|takePicture|none" app:cameraGestureLongTap="autoFocus|takePicture|none"
app:cameraGestureScrollHorizontal="zoom|exposureCorrection|none" app:cameraGestureScrollHorizontal="zoom|exposureCorrection|filterControl1|filterControl2|none"
app:cameraGestureScrollVertical="zoom|exposureCorrection|none"/> app:cameraGestureScrollVertical="zoom|exposureCorrection|filterControl1|filterControl2|none"/>
``` ```
### Related APIs ### Related APIs

@ -70,7 +70,9 @@ filter that was previously set and return back to normal.
|`TintFilter`|`Filters.TINT`|`@string/cameraview_filter_tint`| |`TintFilter`|`Filters.TINT`|`@string/cameraview_filter_tint`|
|`VignetteFilter`|`Filters.VIGNETTE`|`@string/cameraview_filter_vignette`| |`VignetteFilter`|`Filters.VIGNETTE`|`@string/cameraview_filter_vignette`|
Most of these filters accept input parameters to tune them. For example, `DuotoneFilter` will ### Filters controls
Most of the provided filters accept input parameters to tune them. For example, `DuotoneFilter` will
accept two colors to apply the duotone effect. accept two colors to apply the duotone effect.
```java ```java
@ -81,6 +83,14 @@ duotoneFilter.setSecondColor(Color.GREEN);
You can change these values by acting on the filter object, before or after passing it to `CameraView`. You can change these values by acting on the filter object, before or after passing it to `CameraView`.
Whenever something is changed, the updated values will be visible immediately in the next frame. Whenever something is changed, the updated values will be visible immediately in the next frame.
You can also map the first or second filter control to a gesture (like horizontal or vertical scrolling),
as explained in [the gesture documentation](gestures.html):
```java
camera.mapGesture(Gesture.SCROLL_HORIZONTAL, GestureAction.FILTER_CONTROL_1);
camera.mapGesture(Gesture.SCROLL_VERTICAL, GestureAction.FILTER_CONTROL_2);
```
### Advanced usage ### Advanced usage
Advanced users with OpenGL experience can create their own filters by implementing the `Filter` interface Advanced users with OpenGL experience can create their own filters by implementing the `Filter` interface

Loading…
Cancel
Save