diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index ad47eaa6b88d..bedd2915d3f5 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.17 + +* Fixes incorrect camera preview mirroring for front cameras of devices using the `ImageReader` + Impeller backend. + ## 0.6.16 * Fixes incorrect camera preview rotation for landscape-oriented devices. diff --git a/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart b/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart index de3a970fe8ad..f588ad06ce3f 100644 --- a/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart @@ -152,9 +152,27 @@ final class _ImageReaderRotatedPreviewState sign: widget.facingSign, ); + // If the camera is front facing, mirror the camera preview + // according to the current device orientation. + Widget cameraPreview = widget.child; + if (widget.facingSign == 1) { + if (deviceOrientation == DeviceOrientation.portraitDown || + deviceOrientation == DeviceOrientation.portraitUp) { + cameraPreview = Transform.scale( + scaleY: -1, + child: cameraPreview, + ); + } else { + cameraPreview = Transform.scale( + scaleX: -1, + child: cameraPreview, + ); + } + } + return RotatedBox( quarterTurns: rotationDegrees ~/ 90, - child: widget.child, + child: cameraPreview, ); } else { return const SizedBox.shrink(); diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 8eb65f428cf3..2b1df1be215f 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.6.16 +version: 0.6.17 environment: sdk: ^3.6.0 diff --git a/packages/camera/camera_android_camerax/test/preview_rotation_test.dart b/packages/camera/camera_android_camerax/test/preview_rotation_test.dart index 13c4cda98fdb..e459b31b9259 100644 --- a/packages/camera/camera_android_camerax/test/preview_rotation_test.dart +++ b/packages/camera/camera_android_camerax/test/preview_rotation_test.dart @@ -7,7 +7,7 @@ import 'package:camera_android_camerax/src/camerax_library.dart'; import 'package:camera_android_camerax/src/camerax_proxy.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart' show RotatedBox, Texture; +import 'package:flutter/widgets.dart' show RotatedBox, Texture, Transform; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -1031,8 +1031,13 @@ void main() { final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, expectedQuarterTurns, @@ -1077,8 +1082,14 @@ void main() { const int expectedQuarterTurns = _180DegreesClockwise; final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); + + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, expectedQuarterTurns, @@ -1122,8 +1133,14 @@ void main() { const int expectedQuarterTurns = _90DegreesClockwise; final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); + + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, expectedQuarterTurns, @@ -1168,8 +1185,14 @@ void main() { const int expectedQuarterTurns = _0DegreesClockwise; final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); + + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); expect(rotatedBox.quarterTurns, expectedQuarterTurns, reason: getExpectedRotationTestFailureReason( expectedQuarterTurns, rotatedBox.quarterTurns)); @@ -1245,8 +1268,13 @@ void main() { final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); expect(rotatedBox.quarterTurns, expectedQuarterTurns, reason: getExpectedRotationTestFailureReason( expectedQuarterTurns, rotatedBox.quarterTurns)); @@ -1283,9 +1311,16 @@ void main() { const int expectedQuarterTurns = _90DegreesClockwise; final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); + + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); + final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); expect(clockwiseQuarterTurns, expectedQuarterTurns, reason: getExpectedRotationTestFailureReason( expectedQuarterTurns, rotatedBox.quarterTurns)); @@ -1320,9 +1355,16 @@ void main() { const int expectedQuarterTurns = _180DegreesClockwise; final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); + + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); + final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); expect(clockwiseQuarterTurns, expectedQuarterTurns, reason: getExpectedRotationTestFailureReason( expectedQuarterTurns, rotatedBox.quarterTurns)); @@ -1359,9 +1401,16 @@ void main() { const int expectedQuarterTurns = _270DegreesClockwise; final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); + + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); + final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); expect(clockwiseQuarterTurns, expectedQuarterTurns, reason: getExpectedRotationTestFailureReason( expectedQuarterTurns, rotatedBox.quarterTurns)); @@ -1449,11 +1498,18 @@ void main() { currentDefaultDisplayRotation]!; final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); + + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); + final int clockwiseQuarterTurns = rotatedBox.quarterTurns < 0 ? rotatedBox.quarterTurns + 4 : rotatedBox.quarterTurns; - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); expect(clockwiseQuarterTurns, expectedQuarterTurns, reason: 'When the default display rotation is $currentDefaultDisplayRotation, expected the preview to be rotated by $expectedQuarterTurns quarter turns (which is ${expectedQuarterTurns * 90} degrees clockwise) but instead was rotated ${rotatedBox.quarterTurns} quarter turns.'); @@ -1539,11 +1595,17 @@ void main() { final RotatedBox rotatedBox = tester.widget( find.byType(RotatedBox), ); + + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns < 0 ? rotatedBox.quarterTurns + 4 : rotatedBox.quarterTurns; - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); expect(clockwiseQuarterTurns, expectedQuarterTurns, reason: 'When the device orientation is $currentDeviceOrientation, expected the preview to be rotated by $expectedQuarterTurns quarter turns (which is ${expectedQuarterTurns * 90} degrees clockwise) but instead was rotated ${rotatedBox.quarterTurns} quarter turns.'); @@ -1760,8 +1822,14 @@ void main() { const int expectedQuarterTurns = _90DegreesClockwise; final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); - expect(rotatedBox.child, isA()); - expect((rotatedBox.child! as Texture).textureId, cameraId); + + // We expect a Transform widget to wrap the RotatedBox with the camera + // preview to mirror the preview, since the front camera is being + // used. + expect(rotatedBox.child, isA()); + + final Transform tranformedPreview = rotatedBox.child! as Transform; + expect((tranformedPreview.child! as Texture).textureId, cameraId); expect(rotatedBox.quarterTurns, expectedQuarterTurns, reason: getExpectedRotationTestFailureReason( expectedQuarterTurns, rotatedBox.quarterTurns));