From cbac776f914814a911e0a45a3d090c808c26fa5e Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Tue, 29 Aug 2017 11:23:46 +0200 Subject: [PATCH] HDR support (#11) --- README.md | 18 +++++-- .../com/otaliastudios/cameraview/Camera1.java | 27 ++++++++-- .../com/otaliastudios/cameraview/Camera2.java | 8 ++- .../cameraview/CameraController.java | 3 ++ .../cameraview/CameraOptions.java | 29 ++++++++-- .../otaliastudios/cameraview/CameraView.java | 22 ++++++++ .../com/otaliastudios/cameraview/Mapper.java | 54 ++++++++++++++----- .../com/otaliastudios/cameraview/Hdr.java | 42 +++++++++++++++ cameraview/src/main/res/values/attrs.xml | 5 ++ .../otaliastudios/cameraview/CameraUtils.java | 2 +- 10 files changed, 186 insertions(+), 24 deletions(-) create mode 100644 cameraview/src/main/options/com/otaliastudios/cameraview/Hdr.java diff --git a/README.md b/README.md index e7a76684..ada2cbbc 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ compile 'com.otaliastudios:cameraview:1.1.4' - Multiple capture methods - Take high-resolution pictures with `capturePicture` - Take quick snapshots as a freeze frame of the preview with `captureSnapshot` (similar to Snapchat and Instagram) -- Control flash, zoom, white balance, exposure correction and more +- Control HDR, flash, zoom, white balance, exposure correction and more - **Metadata** support for pictures and videos - Automatically detected orientation tags - Plug in location tags with `setLocation()` API @@ -66,7 +66,7 @@ To use the CameraView engine, simply add a `CameraView` to your layout: android:layout_height="wrap_content" /> ``` -Make sure you override `onResume`, `onPause` and `onDestroy` in your activity or fragment, and call `CameraView.start()`, `stop()` and `destroy()`. +`CameraView` has lots of XML attributes, so keep reading. Make sure you override `onResume`, `onPause` and `onDestroy` in your activity or fragment, and call `CameraView.start()`, `stop()` and `destroy()`. ```java @Override @@ -291,7 +291,8 @@ Most camera parameters can be controlled through XML attributes or linked method app:cameraCropOutput="false" app:cameraJpegQuality="100" app:cameraVideoQuality="480p" - app:cameraWhiteBalance="auto" /> + app:cameraWhiteBalance="auto" + app:cameraHdr="off" /> ``` |XML Attribute|Method|Values|Default Value| @@ -304,6 +305,7 @@ Most camera parameters can be controlled through XML attributes or linked method |[`cameraJpegQuality`](#camerajpegquality)|`setJpegQuality()`|`0 <= n <= 100`|`100`| |[`cameraVideoQuality`](#cameravideoquality)|`setVideoQuality()`|`lowest` `highest` `maxQvga` `max480p` `max720p` `max1080p` `max2160p`|`max480p`| |[`cameraWhiteBalance`](#camerawhitebalance)|`setWhiteBalance()`|`auto` `incandescent` `fluorescent` `daylight` `cloudy`|`auto`| +|[`cameraHdr`](#camerahdr)|`setHdr()`|`off` `on`|`off`| #### cameraSessionType @@ -390,6 +392,15 @@ cameraView.setWhiteBalance(WhiteBalance.DAYLIGHT); cameraView.setWhiteBalance(WhiteBalance.CLOUDY); ``` +#### cameraHdr + +Turns on or off HDR captures. + +```java +cameraView.setHdr(Hdr.OFF); +cameraView.setHdr(Hdr.ON); +``` + ## Other APIs Other APIs not mentioned above are provided, and are well documented and commented in code. @@ -465,6 +476,7 @@ This is what was done since the library was forked. I have kept the original str - *gesture framework support* - *scroll gestures support* - *MediaActionSound support* +- *Hdr controls* These are still things that need to be done, off the top of my head: diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java index b25eee6f..2018ee35 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java @@ -174,7 +174,7 @@ class Camera1 extends CameraController { } private boolean collectCameraId() { - int internalFacing = mMapper.mapFacing(mFacing); + int internalFacing = mMapper.map(mFacing); Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) { Camera.getCameraInfo(i, cameraInfo); @@ -261,13 +261,34 @@ class Camera1 extends CameraController { private boolean mergeWhiteBalance(Camera.Parameters params, WhiteBalance oldWhiteBalance) { if (mOptions.getSupportedWhiteBalance().contains(mWhiteBalance)) { - params.setWhiteBalance((String) mMapper.mapWhiteBalance(mWhiteBalance)); + params.setWhiteBalance((String) mMapper.map(mWhiteBalance)); return true; } mWhiteBalance = oldWhiteBalance; return false; } + @Override + void setHdr(Hdr hdr) { + Hdr old = mHdr; + mHdr = hdr; + if (isCameraOpened()) { + synchronized (mLock) { + Camera.Parameters params = mCamera.getParameters(); + if (mergeHdr(params, old)) mCamera.setParameters(params); + } + } + } + + private boolean mergeHdr(Camera.Parameters params, Hdr oldHdr) { + if (mOptions.getSupportedHdr().contains(mHdr)) { + params.setSceneMode((String) mMapper.map(mHdr)); + return true; + } + mHdr = oldHdr; + return false; + } + @Override void setFlash(Flash flash) { Flash old = mFlash; @@ -283,7 +304,7 @@ class Camera1 extends CameraController { private boolean mergeFlash(Camera.Parameters params, Flash oldFlash) { if (mOptions.getSupportedFlash().contains(mFlash)) { - params.setFlashMode((String) mMapper.mapFlash(mFlash)); + params.setFlashMode((String) mMapper.map(mFlash)); return true; } mFlash = oldFlash; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java index 81037aba..b50c9ce8 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java @@ -101,7 +101,7 @@ class Camera2 extends CameraController { @Override void setFacing(Facing facing) { - int internalFacing = mMapper.mapFacing(facing); + int internalFacing = mMapper.map(facing); final String[] ids; try { ids = mCameraManager.getCameraIdList(); @@ -139,7 +139,6 @@ class Camera2 extends CameraController { @Override void setFlash(Flash flash) { - } @Override @@ -147,6 +146,11 @@ class Camera2 extends CameraController { } + @Override + void setHdr(Hdr hdr) { + + } + @Override void setLocation(Location location) { diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java index 2d667874..6cd4c921 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java @@ -17,6 +17,7 @@ abstract class CameraController implements Preview.SurfaceCallback { protected WhiteBalance mWhiteBalance; protected VideoQuality mVideoQuality; protected SessionType mSessionType; + protected Hdr mHdr; CameraController(CameraView.CameraCallbacks callback, Preview preview) { mCameraCallbacks = callback; @@ -37,6 +38,7 @@ abstract class CameraController implements Preview.SurfaceCallback { abstract void setWhiteBalance(WhiteBalance whiteBalance); abstract void setVideoQuality(VideoQuality videoQuality); abstract void setSessionType(SessionType sessionType); + abstract void setHdr(Hdr hdr); abstract void setLocation(Location location); abstract boolean capturePicture(); @@ -59,4 +61,5 @@ abstract class CameraController implements Preview.SurfaceCallback { final WhiteBalance getWhiteBalance() { return mWhiteBalance; } final VideoQuality getVideoQuality() { return mVideoQuality; } final SessionType getSessionType() { return mSessionType; } + final Hdr getHdr() { return mHdr; } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java index de1ec244..d40084db 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java @@ -6,6 +6,7 @@ import android.hardware.Camera; import android.hardware.camera2.CameraCharacteristics; import android.support.annotation.NonNull; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -18,6 +19,7 @@ public class CameraOptions { private Set supportedWhiteBalance = new HashSet<>(5); private Set supportedFacing = new HashSet<>(2); private Set supportedFlash = new HashSet<>(4); + private Set supportedHdr = new HashSet<>(2); private boolean zoomSupported; private boolean videoSnapshotSupported; @@ -59,6 +61,15 @@ public class CameraOptions { } } + // Hdr + strings = params.getSupportedSceneModes(); + if (strings != null) { + for (String string : strings) { + Hdr value = mapper.unmapHdr(string); + if (value != null) supportedHdr.add(value); + } + } + zoomSupported = params.isZoomSupported(); videoSnapshotSupported = params.isVideoSnapshotSupported(); autoFocusSupported = params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO); @@ -86,7 +97,7 @@ public class CameraOptions { */ @NonNull public Set getSupportedFacing() { - return supportedFacing; + return Collections.unmodifiableSet(supportedFacing); } @@ -101,7 +112,7 @@ public class CameraOptions { */ @NonNull public Set getSupportedFlash() { - return supportedFlash; + return Collections.unmodifiableSet(supportedFlash); } @@ -117,10 +128,22 @@ public class CameraOptions { */ @NonNull public Set getSupportedWhiteBalance() { - return supportedWhiteBalance; + return Collections.unmodifiableSet(supportedWhiteBalance); } + /** + * Set of supported hdr values. + * + * @see Hdr#OFF + * @see Hdr#ON + * @return a set of supported values. + */ + @NonNull + public Set getSupportedHdr() { + return Collections.unmodifiableSet(supportedHdr); + } + /** * Whether zoom is supported. If this is false, pinch-to-zoom * will not work and {@link CameraView#setZoom(float)} will have no effect. diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 7528b25f..3d408dcf 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -706,6 +706,28 @@ public class CameraView extends FrameLayout { } + /** + * Controls the grids to be drawn over the current layout. + * + * @see Hdr#OFF + * @see Hdr#ON + * + * @param hdr desired hdr value + */ + public void setHdr(Hdr hdr) { + mCameraController.setHdr(hdr); + } + + + /** + * Gets the current hdr value. + * @return the current hdr value + */ + public Hdr getHdr() { + return mCameraController.getHdr(); + } + + /** * Set location coordinates to be found later in the jpeg EXIF header * diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java index 80b25cba..54ec3a93 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java @@ -1,23 +1,27 @@ package com.otaliastudios.cameraview; import android.hardware.Camera; +import android.os.Build; import java.util.HashMap; abstract class Mapper { - abstract T mapFlash(Flash internalConstant); - abstract T mapFacing(Facing internalConstant); - abstract T mapWhiteBalance(WhiteBalance internalConstant); + abstract T map(Flash flash); + abstract T map(Facing facing); + abstract T map(WhiteBalance whiteBalance); + abstract T map(Hdr hdr); abstract Flash unmapFlash(T cameraConstant); abstract Facing unmapFacing(T cameraConstant); abstract WhiteBalance unmapWhiteBalance(T cameraConstant); + abstract Hdr unmapHdr(T cameraConstant); static class Mapper1 extends Mapper { private static final HashMap FLASH = new HashMap<>(); private static final HashMap WB = new HashMap<>(); private static final HashMap FACING = new HashMap<>(); + private static final HashMap HDR = new HashMap<>(); static { FLASH.put(Flash.OFF, Camera.Parameters.FLASH_MODE_OFF); @@ -31,21 +35,32 @@ abstract class Mapper { WB.put(WhiteBalance.FLUORESCENT, Camera.Parameters.WHITE_BALANCE_FLUORESCENT); WB.put(WhiteBalance.DAYLIGHT, Camera.Parameters.WHITE_BALANCE_DAYLIGHT); WB.put(WhiteBalance.CLOUDY, Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT); + HDR.put(Hdr.OFF, Camera.Parameters.SCENE_MODE_AUTO); + if (Build.VERSION.SDK_INT >= 17) { + HDR.put(Hdr.ON, Camera.Parameters.SCENE_MODE_HDR); + } else { + HDR.put(Hdr.ON, "hdr"); + } + } + + @Override + T map(Flash flash) { + return (T) FLASH.get(flash); } @Override - T mapFlash(Flash internalConstant) { - return (T) FLASH.get(internalConstant); + T map(Facing facing) { + return (T) FACING.get(facing); } @Override - T mapFacing(Facing internalConstant) { - return (T) FACING.get(internalConstant); + T map(WhiteBalance whiteBalance) { + return (T) WB.get(whiteBalance); } @Override - T mapWhiteBalance(WhiteBalance internalConstant) { - return (T) WB.get(internalConstant); + T map(Hdr hdr) { + return (T) HDR.get(hdr); } private T reverseLookup(HashMap map, Object object) { @@ -71,17 +86,22 @@ abstract class Mapper { WhiteBalance unmapWhiteBalance(T cameraConstant) { return reverseLookup(WB, cameraConstant); } + + @Override + Hdr unmapHdr(T cameraConstant) { + return reverseLookup(HDR, cameraConstant); + } } static class Mapper2 extends Mapper { @Override - T mapWhiteBalance(WhiteBalance internalConstant) { + T map(WhiteBalance whiteBalance) { return null; } @Override - T mapFlash(Flash internalConstant) { + T map(Flash flash) { return null; } @@ -96,7 +116,7 @@ abstract class Mapper { } @Override - T mapFacing(Facing internalConstant) { + T map(Facing facing) { return null; } @@ -104,6 +124,16 @@ abstract class Mapper { Facing unmapFacing(T cameraConstant) { return null; } + + @Override + T map(Hdr hdr) { + return null; + } + + @Override + Hdr unmapHdr(T cameraConstant) { + return null; + } } } diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Hdr.java b/cameraview/src/main/options/com/otaliastudios/cameraview/Hdr.java new file mode 100644 index 00000000..411407c0 --- /dev/null +++ b/cameraview/src/main/options/com/otaliastudios/cameraview/Hdr.java @@ -0,0 +1,42 @@ +package com.otaliastudios.cameraview; + + +/** + * Hdr values indicate whether to use high dynamic range techniques when capturing pictures. + * + * @see CameraView#setHdr(Hdr) + */ +public enum Hdr { + + /** + * No HDR. + */ + OFF(0), + + /** + * Using HDR. + */ + ON(1); + + final static Hdr DEFAULT = OFF; + + private int value; + + Hdr(int value) { + this.value = value; + } + + int value() { + return value; + } + + static Hdr fromValue(int value) { + Hdr[] list = Hdr.values(); + for (Hdr action : list) { + if (action.value() == value) { + return action; + } + } + return null; + } +} diff --git a/cameraview/src/main/res/values/attrs.xml b/cameraview/src/main/res/values/attrs.xml index dc761991..b2f41d80 100644 --- a/cameraview/src/main/res/values/attrs.xml +++ b/cameraview/src/main/res/values/attrs.xml @@ -45,6 +45,11 @@ + + + + + diff --git a/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java b/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java index bb2ba906..f15c88c7 100644 --- a/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java +++ b/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java @@ -44,7 +44,7 @@ public class CameraUtils { * @return true if such sensor exists */ public static boolean hasCameraFacing(Context context, Facing facing) { - int internal = new Mapper.Mapper1().mapFacing(facing); + int internal = new Mapper.Mapper1().map(facing); Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) { Camera.getCameraInfo(i, cameraInfo);