Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[camera] Fix coordinate rotation for setting focus- and exposure points on iOS #4158

Merged
merged 4 commits into from
Jul 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
27 changes: 24 additions & 3 deletions packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,26 @@ @interface FLTCam : NSObject <FlutterTexture,

- (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

- (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 {
Expand Down Expand Up @@ -117,4 +119,23 @@ - (void)testLockedFocusWithModeNotSupported_ShouldSetNothing {
[_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
49 changes: 49 additions & 0 deletions packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m
Original file line number Diff line number Diff line change
@@ -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
37 changes: 35 additions & 2 deletions packages/camera/camera/ios/Classes/CameraPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -1030,15 +1030,43 @@ - (void)applyFocusMode:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureD
[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"
message:@"Device does not have exposure point capabilities"
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];
Expand All @@ -1052,11 +1080,16 @@ - (void)setFocusPointWithResult:(FlutterResult)result x:(double)x y:(double)y {
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);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A Flutter plugin for getting information about and controlling the
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"
Expand Down