[camera] Fix coordinate rotation for setting focus- and exposure points on iOS (#4158)
diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index 1f30104..236cf96 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.8.1+5
+
+* Make sure the `setFocusPoint` and `setExposurePoint` coordinates work correctly in all orientations on iOS (instead of only in portrait mode).
+
## 0.8.1+4
* Silenced warnings that may occur during build when using a very
diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m
new file mode 100644
index 0000000..ee43d3f
--- /dev/null
+++ b/packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m
@@ -0,0 +1,55 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+@import camera;
+@import XCTest;
+@import AVFoundation;
+#import <OCMock/OCMock.h>
+
+@interface FLTCam : NSObject <FlutterTexture,
+ AVCaptureVideoDataOutputSampleBufferDelegate,
+ AVCaptureAudioDataOutputSampleBufferDelegate>
+
+- (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y;
+@end
+
+@interface CameraExposureTests : XCTestCase
+@property(readonly, nonatomic) FLTCam *camera;
+@property(readonly, nonatomic) id mockDevice;
+@property(readonly, nonatomic) id mockUIDevice;
+@end
+
+@implementation CameraExposureTests
+
+- (void)setUp {
+ _camera = [[FLTCam alloc] init];
+ _mockDevice = OCMClassMock([AVCaptureDevice class]);
+ _mockUIDevice = OCMPartialMock([UIDevice currentDevice]);
+}
+
+- (void)tearDown {
+ [_mockDevice stopMocking];
+ [_mockUIDevice stopMocking];
+}
+
+- (void)testSetExpsourePointWithResult_SetsExposurePointOfInterest {
+ // UI is currently in landscape left orientation
+ OCMStub([(UIDevice *)_mockUIDevice orientation]).andReturn(UIDeviceOrientationLandscapeLeft);
+ // Exposure point of interest is supported
+ OCMStub([_mockDevice isExposurePointOfInterestSupported]).andReturn(true);
+ // Set mock device as the current capture device
+ [_camera setValue:_mockDevice forKey:@"captureDevice"];
+
+ // Run test
+ [_camera
+ setExposurePointWithResult:^void(id _Nullable result) {
+ }
+ x:1
+ y:1];
+
+ // Verify the focus point of interest has been set
+ OCMVerify([_mockDevice setExposurePointOfInterest:CGPointMake(1, 1)]);
+}
+
+@end
diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m
index 5d93bdf..27537e7 100644
--- a/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m
+++ b/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m
@@ -19,12 +19,13 @@
- (void)applyFocusMode;
- (void)applyFocusMode:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice;
+- (void)setFocusPointWithResult:(FlutterResult)result x:(double)x y:(double)y;
@end
@interface CameraFocusTests : XCTestCase
@property(readonly, nonatomic) FLTCam *camera;
@property(readonly, nonatomic) id mockDevice;
-
+@property(readonly, nonatomic) id mockUIDevice;
@end
@implementation CameraFocusTests
@@ -32,11 +33,12 @@
- (void)setUp {
_camera = [[FLTCam alloc] init];
_mockDevice = OCMClassMock([AVCaptureDevice class]);
+ _mockUIDevice = OCMPartialMock([UIDevice currentDevice]);
}
- (void)tearDown {
- // Put teardown code here. This method is called after the invocation of each test method in the
- // class.
+ [_mockDevice stopMocking];
+ [_mockUIDevice stopMocking];
}
- (void)testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus {
@@ -117,4 +119,23 @@
[_camera applyFocusMode:FocusModeLocked onDevice:_mockDevice];
}
+- (void)testSetFocusPointWithResult_SetsFocusPointOfInterest {
+ // UI is currently in landscape left orientation
+ OCMStub([(UIDevice *)_mockUIDevice orientation]).andReturn(UIDeviceOrientationLandscapeLeft);
+ // Focus point of interest is supported
+ OCMStub([_mockDevice isFocusPointOfInterestSupported]).andReturn(true);
+ // Set mock device as the current capture device
+ [_camera setValue:_mockDevice forKey:@"captureDevice"];
+
+ // Run test
+ [_camera
+ setFocusPointWithResult:^void(id _Nullable result) {
+ }
+ x:1
+ y:1];
+
+ // Verify the focus point of interest has been set
+ OCMVerify([_mockDevice setFocusPointOfInterest:CGPointMake(1, 1)]);
+}
+
@end
diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m
new file mode 100644
index 0000000..380f6e9
--- /dev/null
+++ b/packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m
@@ -0,0 +1,49 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+@import camera;
+@import XCTest;
+@import AVFoundation;
+#import <OCMock/OCMock.h>
+
+@interface FLTCam : NSObject <FlutterTexture,
+ AVCaptureVideoDataOutputSampleBufferDelegate,
+ AVCaptureAudioDataOutputSampleBufferDelegate>
+
+- (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation
+ x:(double)x
+ y:(double)y;
+
+@end
+
+@interface CameraUtilTests : XCTestCase
+@property(readonly, nonatomic) FLTCam *camera;
+
+@end
+
+@implementation CameraUtilTests
+
+- (void)setUp {
+ _camera = [[FLTCam alloc] init];
+}
+
+- (void)testGetCGPointForCoordsWithOrientation_ShouldRotateCoords {
+ CGPoint point;
+ point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationLandscapeLeft x:1 y:1];
+ XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
+ @"Resulting coordinates are invalid.");
+ point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationPortrait x:0 y:1];
+ XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
+ @"Resulting coordinates are invalid.");
+ point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationLandscapeRight x:0 y:0];
+ XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
+ @"Resulting coordinates are invalid.");
+ point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationPortraitUpsideDown
+ x:1
+ y:0];
+ XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
+ @"Resulting coordinates are invalid.");
+}
+
+@end
diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m
index ebd5366..d88eb45 100644
--- a/packages/camera/camera/ios/Classes/CameraPlugin.m
+++ b/packages/camera/camera/ios/Classes/CameraPlugin.m
@@ -1030,6 +1030,31 @@
[captureDevice unlockForConfiguration];
}
+- (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation
+ x:(double)x
+ y:(double)y {
+ double oldX = x, oldY = y;
+ switch (orientation) {
+ case UIDeviceOrientationPortrait: // 90 ccw
+ y = 1 - oldX;
+ x = oldY;
+ break;
+ case UIDeviceOrientationPortraitUpsideDown: // 90 cw
+ x = 1 - oldY;
+ y = oldX;
+ break;
+ case UIDeviceOrientationLandscapeRight: // 180
+ x = 1 - x;
+ y = 1 - y;
+ break;
+ case UIDeviceOrientationLandscapeLeft:
+ default:
+ // No rotation required
+ break;
+ }
+ return CGPointMake(x, y);
+}
+
- (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y {
if (!_captureDevice.isExposurePointOfInterestSupported) {
result([FlutterError errorWithCode:@"setExposurePointFailed"
@@ -1037,8 +1062,11 @@
details:nil]);
return;
}
+ UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
[_captureDevice lockForConfiguration:nil];
- [_captureDevice setExposurePointOfInterest:CGPointMake(y, 1 - x)];
+ [_captureDevice setExposurePointOfInterest:[self getCGPointForCoordsWithOrientation:orientation
+ x:x
+ y:y]];
[_captureDevice unlockForConfiguration];
// Retrigger auto exposure
[self applyExposureMode];
@@ -1052,11 +1080,16 @@
details:nil]);
return;
}
+ UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
[_captureDevice lockForConfiguration:nil];
- [_captureDevice setFocusPointOfInterest:CGPointMake(y, 1 - x)];
+
+ [_captureDevice setFocusPointOfInterest:[self getCGPointForCoordsWithOrientation:orientation
+ x:x
+ y:y]];
[_captureDevice unlockForConfiguration];
// Retrigger auto focus
[self applyFocusMode];
+
result(nil);
}
diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml
index 789910e..78eb49a 100644
--- a/packages/camera/camera/pubspec.yaml
+++ b/packages/camera/camera/pubspec.yaml
@@ -4,7 +4,7 @@
and streaming image buffers to dart.
repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.8.1+4
+version: 0.8.1+5
environment:
sdk: ">=2.12.0 <3.0.0"