Skip to content

Commit d61558a

Browse files
BeMacizedamantoux
authored andcommitted
[camera] Fix coordinate rotation for setting focus- and exposure points on iOS (flutter#4158)
1 parent 939b1b2 commit d61558a

File tree

6 files changed

+168
-6
lines changed

6 files changed

+168
-6
lines changed

packages/camera/camera/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.8.1+5
2+
3+
* Make sure the `setFocusPoint` and `setExposurePoint` coordinates work correctly in all orientations on iOS (instead of only in portrait mode).
4+
15
## 0.8.1+4
26

37
* Silenced warnings that may occur during build when using a very
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
@import camera;
6+
@import XCTest;
7+
@import AVFoundation;
8+
#import <OCMock/OCMock.h>
9+
10+
@interface FLTCam : NSObject <FlutterTexture,
11+
AVCaptureVideoDataOutputSampleBufferDelegate,
12+
AVCaptureAudioDataOutputSampleBufferDelegate>
13+
14+
- (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y;
15+
@end
16+
17+
@interface CameraExposureTests : XCTestCase
18+
@property(readonly, nonatomic) FLTCam *camera;
19+
@property(readonly, nonatomic) id mockDevice;
20+
@property(readonly, nonatomic) id mockUIDevice;
21+
@end
22+
23+
@implementation CameraExposureTests
24+
25+
- (void)setUp {
26+
_camera = [[FLTCam alloc] init];
27+
_mockDevice = OCMClassMock([AVCaptureDevice class]);
28+
_mockUIDevice = OCMPartialMock([UIDevice currentDevice]);
29+
}
30+
31+
- (void)tearDown {
32+
[_mockDevice stopMocking];
33+
[_mockUIDevice stopMocking];
34+
}
35+
36+
- (void)testSetExpsourePointWithResult_SetsExposurePointOfInterest {
37+
// UI is currently in landscape left orientation
38+
OCMStub([(UIDevice *)_mockUIDevice orientation]).andReturn(UIDeviceOrientationLandscapeLeft);
39+
// Exposure point of interest is supported
40+
OCMStub([_mockDevice isExposurePointOfInterestSupported]).andReturn(true);
41+
// Set mock device as the current capture device
42+
[_camera setValue:_mockDevice forKey:@"captureDevice"];
43+
44+
// Run test
45+
[_camera
46+
setExposurePointWithResult:^void(id _Nullable result) {
47+
}
48+
x:1
49+
y:1];
50+
51+
// Verify the focus point of interest has been set
52+
OCMVerify([_mockDevice setExposurePointOfInterest:CGPointMake(1, 1)]);
53+
}
54+
55+
@end

packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,26 @@ @interface FLTCam : NSObject <FlutterTexture,
1919

2020
- (void)applyFocusMode;
2121
- (void)applyFocusMode:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice;
22+
- (void)setFocusPointWithResult:(FlutterResult)result x:(double)x y:(double)y;
2223
@end
2324

2425
@interface CameraFocusTests : XCTestCase
2526
@property(readonly, nonatomic) FLTCam *camera;
2627
@property(readonly, nonatomic) id mockDevice;
27-
28+
@property(readonly, nonatomic) id mockUIDevice;
2829
@end
2930

3031
@implementation CameraFocusTests
3132

3233
- (void)setUp {
3334
_camera = [[FLTCam alloc] init];
3435
_mockDevice = OCMClassMock([AVCaptureDevice class]);
36+
_mockUIDevice = OCMPartialMock([UIDevice currentDevice]);
3537
}
3638

3739
- (void)tearDown {
38-
// Put teardown code here. This method is called after the invocation of each test method in the
39-
// class.
40+
[_mockDevice stopMocking];
41+
[_mockUIDevice stopMocking];
4042
}
4143

4244
- (void)testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus {
@@ -117,4 +119,23 @@ - (void)testLockedFocusWithModeNotSupported_ShouldSetNothing {
117119
[_camera applyFocusMode:FocusModeLocked onDevice:_mockDevice];
118120
}
119121

122+
- (void)testSetFocusPointWithResult_SetsFocusPointOfInterest {
123+
// UI is currently in landscape left orientation
124+
OCMStub([(UIDevice *)_mockUIDevice orientation]).andReturn(UIDeviceOrientationLandscapeLeft);
125+
// Focus point of interest is supported
126+
OCMStub([_mockDevice isFocusPointOfInterestSupported]).andReturn(true);
127+
// Set mock device as the current capture device
128+
[_camera setValue:_mockDevice forKey:@"captureDevice"];
129+
130+
// Run test
131+
[_camera
132+
setFocusPointWithResult:^void(id _Nullable result) {
133+
}
134+
x:1
135+
y:1];
136+
137+
// Verify the focus point of interest has been set
138+
OCMVerify([_mockDevice setFocusPointOfInterest:CGPointMake(1, 1)]);
139+
}
140+
120141
@end
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
@import camera;
6+
@import XCTest;
7+
@import AVFoundation;
8+
#import <OCMock/OCMock.h>
9+
10+
@interface FLTCam : NSObject <FlutterTexture,
11+
AVCaptureVideoDataOutputSampleBufferDelegate,
12+
AVCaptureAudioDataOutputSampleBufferDelegate>
13+
14+
- (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation
15+
x:(double)x
16+
y:(double)y;
17+
18+
@end
19+
20+
@interface CameraUtilTests : XCTestCase
21+
@property(readonly, nonatomic) FLTCam *camera;
22+
23+
@end
24+
25+
@implementation CameraUtilTests
26+
27+
- (void)setUp {
28+
_camera = [[FLTCam alloc] init];
29+
}
30+
31+
- (void)testGetCGPointForCoordsWithOrientation_ShouldRotateCoords {
32+
CGPoint point;
33+
point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationLandscapeLeft x:1 y:1];
34+
XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
35+
@"Resulting coordinates are invalid.");
36+
point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationPortrait x:0 y:1];
37+
XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
38+
@"Resulting coordinates are invalid.");
39+
point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationLandscapeRight x:0 y:0];
40+
XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
41+
@"Resulting coordinates are invalid.");
42+
point = [_camera getCGPointForCoordsWithOrientation:UIDeviceOrientationPortraitUpsideDown
43+
x:1
44+
y:0];
45+
XCTAssertTrue(CGPointEqualToPoint(point, CGPointMake(1, 1)),
46+
@"Resulting coordinates are invalid.");
47+
}
48+
49+
@end

packages/camera/camera/ios/Classes/CameraPlugin.m

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,15 +1030,43 @@ - (void)applyFocusMode:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureD
10301030
[captureDevice unlockForConfiguration];
10311031
}
10321032

1033+
- (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation
1034+
x:(double)x
1035+
y:(double)y {
1036+
double oldX = x, oldY = y;
1037+
switch (orientation) {
1038+
case UIDeviceOrientationPortrait: // 90 ccw
1039+
y = 1 - oldX;
1040+
x = oldY;
1041+
break;
1042+
case UIDeviceOrientationPortraitUpsideDown: // 90 cw
1043+
x = 1 - oldY;
1044+
y = oldX;
1045+
break;
1046+
case UIDeviceOrientationLandscapeRight: // 180
1047+
x = 1 - x;
1048+
y = 1 - y;
1049+
break;
1050+
case UIDeviceOrientationLandscapeLeft:
1051+
default:
1052+
// No rotation required
1053+
break;
1054+
}
1055+
return CGPointMake(x, y);
1056+
}
1057+
10331058
- (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y {
10341059
if (!_captureDevice.isExposurePointOfInterestSupported) {
10351060
result([FlutterError errorWithCode:@"setExposurePointFailed"
10361061
message:@"Device does not have exposure point capabilities"
10371062
details:nil]);
10381063
return;
10391064
}
1065+
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
10401066
[_captureDevice lockForConfiguration:nil];
1041-
[_captureDevice setExposurePointOfInterest:CGPointMake(y, 1 - x)];
1067+
[_captureDevice setExposurePointOfInterest:[self getCGPointForCoordsWithOrientation:orientation
1068+
x:x
1069+
y:y]];
10421070
[_captureDevice unlockForConfiguration];
10431071
// Retrigger auto exposure
10441072
[self applyExposureMode];
@@ -1052,11 +1080,16 @@ - (void)setFocusPointWithResult:(FlutterResult)result x:(double)x y:(double)y {
10521080
details:nil]);
10531081
return;
10541082
}
1083+
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
10551084
[_captureDevice lockForConfiguration:nil];
1056-
[_captureDevice setFocusPointOfInterest:CGPointMake(y, 1 - x)];
1085+
1086+
[_captureDevice setFocusPointOfInterest:[self getCGPointForCoordsWithOrientation:orientation
1087+
x:x
1088+
y:y]];
10571089
[_captureDevice unlockForConfiguration];
10581090
// Retrigger auto focus
10591091
[self applyFocusMode];
1092+
10601093
result(nil);
10611094
}
10621095

packages/camera/camera/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: A Flutter plugin for getting information about and controlling the
44
and streaming image buffers to dart.
55
repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera
66
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
7-
version: 0.8.1+4
7+
version: 0.8.1+5
88

99
environment:
1010
sdk: ">=2.12.0 <3.0.0"

0 commit comments

Comments
 (0)