feat(android): Add exposure (brightness) support for iOS and standardize exposure value (#2419)
* Use standardized exposure value between 0 and 1 * Add iOS native exposure control * Delete logging * Add documentation + spell fixes * Update RNCamera.m * Remove reset of exposure after tap-to-focus
This commit is contained in:
parent
70c8cbdb12
commit
ccd6f0b57c
@ -106,7 +106,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
private int mFlash;
|
||||
|
||||
private int mExposure;
|
||||
private float mExposure;
|
||||
|
||||
private int mDisplayOrientation;
|
||||
|
||||
@ -358,12 +358,12 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
int getExposureCompensation() {
|
||||
float getExposureCompensation() {
|
||||
return mExposure;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setExposureCompensation(int exposure) {
|
||||
void setExposureCompensation(float exposure) {
|
||||
|
||||
if (exposure == mExposure) {
|
||||
return;
|
||||
@ -994,17 +994,19 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setExposureInternal(int exposure) {
|
||||
Log.e("CAMERA_1::", ""+isCameraOpened()+"; Exposure: "+exposure);
|
||||
private boolean setExposureInternal(float exposure) {
|
||||
mExposure = exposure;
|
||||
if (isCameraOpened()){
|
||||
int minExposure = mCameraParameters.getMinExposureCompensation();
|
||||
int maxExposure = mCameraParameters.getMaxExposureCompensation();
|
||||
Log.e("CAMERA_1::", ""+minExposure);
|
||||
Log.e("CAMERA_1::", ""+maxExposure);
|
||||
|
||||
if (minExposure != maxExposure) {
|
||||
mCameraParameters.setExposureCompensation(mExposure);
|
||||
int scaledValue = 0;
|
||||
if (mExposure >= 0 && mExposure <= 1) {
|
||||
scaledValue = (int) (mExposure * (maxExposure - minExposure)) + minExposure;
|
||||
}
|
||||
|
||||
mCameraParameters.setExposureCompensation(scaledValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,7 +238,7 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
|
||||
private int mFlash;
|
||||
|
||||
private int mExposure;
|
||||
private float mExposure;
|
||||
|
||||
private int mCameraOrientation;
|
||||
|
||||
@ -477,12 +477,12 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
}
|
||||
|
||||
@Override
|
||||
int getExposureCompensation() {
|
||||
float getExposureCompensation() {
|
||||
return mExposure;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setExposureCompensation(int exposure) {
|
||||
void setExposureCompensation(float exposure) {
|
||||
Log.e("CAMERA_2:: ", "Adjusting exposure is not currently supported for Camera2");
|
||||
}
|
||||
|
||||
|
||||
@ -478,11 +478,11 @@ public class CameraView extends FrameLayout {
|
||||
return mImpl.getFlash();
|
||||
}
|
||||
|
||||
public void setExposureCompensation(int exposure) {
|
||||
public void setExposureCompensation(float exposure) {
|
||||
mImpl.setExposureCompensation(exposure);
|
||||
}
|
||||
|
||||
public int getExposureCompensation() {
|
||||
public float getExposureCompensation() {
|
||||
return mImpl.getExposureCompensation();
|
||||
}
|
||||
|
||||
@ -653,7 +653,7 @@ public class CameraView extends FrameLayout {
|
||||
@Flash
|
||||
int flash;
|
||||
|
||||
int exposure;
|
||||
float exposure;
|
||||
|
||||
float focusDepth;
|
||||
|
||||
@ -672,7 +672,7 @@ public class CameraView extends FrameLayout {
|
||||
ratio = source.readParcelable(loader);
|
||||
autoFocus = source.readByte() != 0;
|
||||
flash = source.readInt();
|
||||
exposure = source.readInt();
|
||||
exposure = source.readFloat();
|
||||
focusDepth = source.readFloat();
|
||||
zoom = source.readFloat();
|
||||
whiteBalance = source.readInt();
|
||||
@ -691,7 +691,7 @@ public class CameraView extends FrameLayout {
|
||||
out.writeParcelable(ratio, 0);
|
||||
out.writeByte((byte) (autoFocus ? 1 : 0));
|
||||
out.writeInt(flash);
|
||||
out.writeInt(exposure);
|
||||
out.writeFloat(exposure);
|
||||
out.writeFloat(focusDepth);
|
||||
out.writeFloat(zoom);
|
||||
out.writeInt(whiteBalance);
|
||||
|
||||
@ -76,9 +76,9 @@ abstract class CameraViewImpl {
|
||||
|
||||
abstract int getFlash();
|
||||
|
||||
abstract void setExposureCompensation(int exposure);
|
||||
abstract void setExposureCompensation(float exposure);
|
||||
|
||||
abstract int getExposureCompensation();
|
||||
abstract float getExposureCompensation();
|
||||
|
||||
abstract void takePicture(ReadableMap options);
|
||||
|
||||
|
||||
@ -84,7 +84,7 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
||||
}
|
||||
|
||||
@ReactProp(name = "exposure")
|
||||
public void setExposureCompensation(RNCameraView view, int exposure){
|
||||
public void setExposureCompensation(RNCameraView view, float exposure){
|
||||
view.setExposureCompensation(exposure);
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,9 @@
|
||||
@property(copy, nonatomic) NSDictionary *autoFocusPointOfInterest;
|
||||
@property(assign, nonatomic) float focusDepth;
|
||||
@property(assign, nonatomic) NSInteger whiteBalance;
|
||||
@property(assign, nonatomic) float exposure;
|
||||
@property(assign, nonatomic) float exposureIsoMin;
|
||||
@property(assign, nonatomic) float exposureIsoMax;
|
||||
@property(assign, nonatomic) AVCaptureSessionPreset pictureSize;
|
||||
@property(nonatomic, assign) BOOL isReadingBarCodes;
|
||||
@property(nonatomic, assign) BOOL isRecording;
|
||||
@ -56,6 +59,7 @@
|
||||
- (void)updateAutoFocusPointOfInterest;
|
||||
- (void)updateZoom;
|
||||
- (void)updateWhiteBalance;
|
||||
- (void)updateExposure;
|
||||
- (void)updatePictureSize;
|
||||
// Face Detection props
|
||||
- (void)updateTrackingEnabled:(id)requestedTracking;
|
||||
|
||||
@ -84,7 +84,7 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
|
||||
name:AVAudioSessionInterruptionNotification
|
||||
object:nil];
|
||||
self.autoFocus = -1;
|
||||
|
||||
self.exposure = -1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -223,28 +223,28 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
|
||||
{
|
||||
AVCaptureDevice *device = [self.videoCaptureDeviceInput device];
|
||||
NSError *error = nil;
|
||||
|
||||
|
||||
if (![device lockForConfiguration:&error]) {
|
||||
if (error) {
|
||||
RCTLogError(@"%s: %@", __func__, error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ([self.autoFocusPointOfInterest objectForKey:@"x"] && [self.autoFocusPointOfInterest objectForKey:@"y"]) {
|
||||
float xValue = [self.autoFocusPointOfInterest[@"x"] floatValue];
|
||||
float yValue = [self.autoFocusPointOfInterest[@"y"] floatValue];
|
||||
if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
|
||||
|
||||
|
||||
CGPoint autofocusPoint = CGPointMake(xValue, yValue);
|
||||
[device setFocusPointOfInterest:autofocusPoint];
|
||||
[device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
|
||||
}
|
||||
}
|
||||
else {
|
||||
RCTLogWarn(@"AutoFocusPointOfInterest not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[device unlockForConfiguration];
|
||||
}
|
||||
|
||||
@ -352,6 +352,57 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
|
||||
[device unlockForConfiguration];
|
||||
}
|
||||
|
||||
|
||||
/// Set the AVCaptureDevice's ISO values based on RNCamera's 'exposure' value,
|
||||
/// which is a float between 0 and 1 if defined by the user or -1 to indicate that no
|
||||
/// selection is active. 'exposure' gets mapped to a valid ISO value between the
|
||||
/// device's min/max-range of ISO-values.
|
||||
///
|
||||
/// The exposure gets reset every time the user manually sets the autofocus-point in
|
||||
/// 'updateAutoFocusPointOfInterest' automatically. Currently no explicit event is fired.
|
||||
/// This leads to two 'exposure'-states: one here and one in the component, which is
|
||||
/// fine. 'exposure' here gets only synced if 'exposure' on the js-side changes. You
|
||||
/// can manually keep the state in sync by setting 'exposure' in your React-state
|
||||
/// everytime the js-updateAutoFocusPointOfInterest-function gets called.
|
||||
- (void)updateExposure
|
||||
{
|
||||
AVCaptureDevice *device = [self.videoCaptureDeviceInput device];
|
||||
NSError *error = nil;
|
||||
|
||||
if (![device lockForConfiguration:&error]) {
|
||||
if (error) {
|
||||
RCTLogError(@"%s: %@", __func__, error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that either no explicit exposure-val has been set yet
|
||||
// or that it has been reset. Check for > 1 is only a guard.
|
||||
if(self.exposure < 0 || self.exposure > 1){
|
||||
[device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
|
||||
[device unlockForConfiguration];
|
||||
return;
|
||||
}
|
||||
|
||||
// Lazy init of range.
|
||||
if(!self.exposureIsoMin){ self.exposureIsoMin = device.activeFormat.minISO; }
|
||||
if(!self.exposureIsoMax){ self.exposureIsoMax = device.activeFormat.maxISO; }
|
||||
|
||||
// Get a valid ISO-value in range from min to max. After we mapped the exposure
|
||||
// (a val between 0 - 1), the result gets corrected by the offset from 0, which
|
||||
// is the min-ISO-value.
|
||||
float appliedExposure = (self.exposureIsoMax - self.exposureIsoMin) * self.exposure + self.exposureIsoMin;
|
||||
|
||||
// Make sure we're in AVCaptureExposureModeCustom, else the ISO + duration time won't apply.
|
||||
if(device.exposureMode != AVCaptureExposureModeCustom){
|
||||
[device setExposureMode:AVCaptureExposureModeCustom];
|
||||
}
|
||||
|
||||
// Only set the ISO for now, duration will be default as a change might affect frame rate.
|
||||
[device setExposureModeCustomWithDuration:AVCaptureExposureDurationCurrent ISO:appliedExposure completionHandler:nil];
|
||||
[device unlockForConfiguration];
|
||||
}
|
||||
|
||||
- (void)updatePictureSize
|
||||
{
|
||||
[self updateSessionPreset:self.pictureSize];
|
||||
|
||||
@ -206,6 +206,12 @@ RCT_CUSTOM_VIEW_PROPERTY(whiteBalance, NSInteger, RNCamera)
|
||||
[view updateWhiteBalance];
|
||||
}
|
||||
|
||||
RCT_CUSTOM_VIEW_PROPERTY(exposure, NSNumber, RNCamera)
|
||||
{
|
||||
[view setExposure:[RCTConvert float:json]];
|
||||
[view updateExposure];
|
||||
}
|
||||
|
||||
RCT_CUSTOM_VIEW_PROPERTY(pictureSize, NSString *, RNCamera)
|
||||
{
|
||||
[view setPictureSize:[[self class] pictureSizes][[RCTConvert NSString:json]]];
|
||||
|
||||
@ -420,7 +420,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
type: CameraManager.Type.back,
|
||||
autoFocus: CameraManager.AutoFocus.on,
|
||||
flashMode: CameraManager.FlashMode.off,
|
||||
exposure: 0,
|
||||
exposure: -1,
|
||||
whiteBalance: CameraManager.WhiteBalance.auto,
|
||||
faceDetectionMode: (CameraManager.FaceDetection || {}).fast,
|
||||
barCodeTypes: Object.values(CameraManager.BarCodeType),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user