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:cameraGestureTap="none|autoFocus|takePicture"
app:cameraGestureLongTap="none|autoFocus|takePicture"
app:cameraGesturePinch="none|zoom|exposureCorrection"
app:cameraGestureScrollHorizontal="none|zoom|exposureCorrection"
app:cameraGestureScrollVertical="none|zoom|exposureCorrection"
app:cameraGesturePinch="none|zoom|exposureCorrection|filterControl1|filterControl2"
app:cameraGestureScrollHorizontal="none|zoom|exposureCorrection|filterControl1|filterControl2"
app:cameraGestureScrollVertical="none|zoom|exposureCorrection|filterControl1|filterControl2"
app:cameraEngine="camera1|camera2"
app:cameraPreview="glSurface|surface|texture"
app:cameraFacing="back|front"

@ -221,6 +221,8 @@ public class CameraOptions1Test extends BaseTest {
assertTrue(o.supports(GestureAction.TAKE_PICTURE));
assertTrue(o.supports(GestureAction.NONE));
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));
}

@ -25,6 +25,7 @@ import com.otaliastudios.cameraview.engine.CameraEngine;
import com.otaliastudios.cameraview.filter.Filter;
import com.otaliastudios.cameraview.filter.Filters;
import com.otaliastudios.cameraview.filter.NoFilter;
import com.otaliastudios.cameraview.filters.DuotoneFilter;
import com.otaliastudios.cameraview.frame.Frame;
import com.otaliastudios.cameraview.frame.FrameProcessor;
import com.otaliastudios.cameraview.gesture.Gesture;
@ -355,6 +356,88 @@ public class CameraViewTest extends BaseTest {
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
//region testMeasure

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

@ -51,6 +51,8 @@ import com.otaliastudios.cameraview.filter.Filter;
import com.otaliastudios.cameraview.filter.FilterParser;
import com.otaliastudios.cameraview.filter.Filters;
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.FrameProcessor;
import com.otaliastudios.cameraview.gesture.Gesture;
@ -627,6 +629,30 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mCameraEngine.setExposureCorrection(newValue, bounds, points, true);
}
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#EXPOSURE_CORRECTION}
* - {@link GestureAction#FILTER_CONTROL_1}
* - {@link GestureAction#FILTER_CONTROL_2}
* - {@link GestureAction#NONE}
*/
PINCH(GestureType.CONTINUOUS),
@ -52,6 +54,8 @@ public enum Gesture {
*
* - {@link GestureAction#ZOOM}
* - {@link GestureAction#EXPOSURE_CORRECTION}
* - {@link GestureAction#FILTER_CONTROL_1}
* - {@link GestureAction#FILTER_CONTROL_2}
* - {@link GestureAction#NONE}
*/
SCROLL_HORIZONTAL(GestureType.CONTINUOUS),
@ -62,6 +66,8 @@ public enum Gesture {
*
* - {@link GestureAction#ZOOM}
* - {@link GestureAction#EXPOSURE_CORRECTION}
* - {@link GestureAction#FILTER_CONTROL_1}
* - {@link GestureAction#FILTER_CONTROL_2}
* - {@link GestureAction#NONE}
*/
SCROLL_VERTICAL(GestureType.CONTINUOUS);

@ -60,8 +60,27 @@ public enum GestureAction {
* - {@link Gesture#SCROLL_HORIZONTAL}
* - {@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_TAP = NONE;

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

@ -26,8 +26,8 @@
app:cameraGestureTap="autoFocus"
app:cameraGestureLongTap="none"
app:cameraGesturePinch="zoom"
app:cameraGestureScrollHorizontal="exposureCorrection"
app:cameraGestureScrollVertical="none"
app:cameraGestureScrollHorizontal="filterControl1"
app:cameraGestureScrollVertical="exposureCorrection"
app:cameraMode="picture"
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|
|-------------|-----------|----------------|
|`PINCH`|Pinch gesture, typically assigned to the zoom control.|`zoom` `exposureCorrection` `none`|
|`TAP`|Single tap gesture, typically assigned to the focus control.|`autoFocus` `takePicture` `none`|
|`LONG_TAP`|Long tap gesture.|`autoFocus` `takePicture` `none`|
|`SCROLL_HORIZONTAL`|Horizontal movement gesture.|`zoom` `exposureCorrection` `none`|
|`SCROLL_VERTICAL`|Vertical movement gesture.|`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.|`AUTO_FOCUS` `TAKE_PICTURE` `NONE`|
|`LONG_TAP`|Long tap gesture.|`AUTO_FOCUS` `TAKE_PICTURE` `NONE`|
|`SCROLL_HORIZONTAL`|Horizontal movement gesture.|`ZOOM` `EXPOSURE_CORRECTION` `FILTER_CONTROL_1` `FILTER_CONTROL_2` `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
<com.otaliastudios.cameraview.CameraView
app:cameraGesturePinch="zoom|exposureCorrection|none"
app:cameraGesturePinch="zoom|exposureCorrection|filterControl1|filterControl2|none"
app:cameraGestureTap="autoFocus|takePicture|none"
app:cameraGestureLongTap="autoFocus|takePicture|none"
app:cameraGestureScrollHorizontal="zoom|exposureCorrection|none"
app:cameraGestureScrollVertical="zoom|exposureCorrection|none"/>
app:cameraGestureScrollHorizontal="zoom|exposureCorrection|filterControl1|filterControl2|none"
app:cameraGestureScrollVertical="zoom|exposureCorrection|filterControl1|filterControl2|none"/>
```
### Related APIs

@ -70,7 +70,9 @@ filter that was previously set and return back to normal.
|`TintFilter`|`Filters.TINT`|`@string/cameraview_filter_tint`|
|`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.
```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`.
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 users with OpenGL experience can create their own filters by implementing the `Filter` interface

Loading…
Cancel
Save