Skip to content
This repository has been 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 1 commit
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

* Fixed setFocusPoint and setExposurePoint coordinates only being correct in a single orientation on iOS.
BeMacized marked this conversation as resolved.
Show resolved Hide resolved

## 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,57 @@
// 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 {
// Put teardown code here. This method is called after the invocation of each test method in the
// class.
BeMacized marked this conversation as resolved.
Show resolved Hide resolved
[_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
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,28 @@ @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.
BeMacized marked this conversation as resolved.
Show resolved Hide resolved
[_mockDevice stopMocking];
[_mockUIDevice stopMocking];
}

- (void)testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus {
Expand Down Expand Up @@ -117,4 +121,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
54 changes: 54 additions & 0 deletions packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the
// class.
}

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