From 7b3809a8b1864a78d7c0ae315a27380c513bc1c2 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Thu, 8 May 2025 13:47:47 -0700 Subject: [PATCH 01/12] mirror camera --- .../lib/src/image_reader_rotated_preview.dart | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) 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 de3a970fe8a..f588ad06ce3 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(); From 1351c201fa25e7ae9ffeac9f94349c8a731f51df Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Fri, 9 May 2025 09:35:44 -0700 Subject: [PATCH 02/12] fix tests + bump version --- .../camera_android_camerax/CHANGELOG.md | 5 + .../camera_android_camerax/pubspec.yaml | 2 +- .../test/preview_rotation_test.dart | 114 ++++++++++++++---- 3 files changed, 97 insertions(+), 24 deletions(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index ad47eaa6b88..bedd2915d3f 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/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 8eb65f428cf..2b1df1be215 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 13c4cda98fd..e459b31b925 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)); From ceb9865dabb174dae53cedd77a472754dde3560b Mon Sep 17 00:00:00 2001 From: jesswrd Date: Mon, 2 Jun 2025 12:22:44 -0700 Subject: [PATCH 03/12] added mirroring tests --- .../test/preview_rotation_test.dart | 106 ++++++++++++++---- 1 file changed, 84 insertions(+), 22 deletions(-) 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 e459b31b925..c8ae97e7bf0 100644 --- a/packages/camera/camera_android_camerax/test/preview_rotation_test.dart +++ b/packages/camera/camera_android_camerax/test/preview_rotation_test.dart @@ -1036,8 +1036,13 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in portrait mode, we expect the camera + // preview to be mirrored across the y-axis. + expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, expectedQuarterTurns, @@ -1088,8 +1093,13 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, expectedQuarterTurns, @@ -1139,8 +1149,13 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in portrait mode, we expect the camera + // preview to be mirrored across the y-axis. + expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, expectedQuarterTurns, @@ -1191,8 +1206,13 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); expect(rotatedBox.quarterTurns, expectedQuarterTurns, reason: getExpectedRotationTestFailureReason( expectedQuarterTurns, rotatedBox.quarterTurns)); @@ -1273,8 +1293,13 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); expect(rotatedBox.quarterTurns, expectedQuarterTurns, reason: getExpectedRotationTestFailureReason( expectedQuarterTurns, rotatedBox.quarterTurns)); @@ -1317,8 +1342,13 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; expect(clockwiseQuarterTurns, expectedQuarterTurns, @@ -1361,8 +1391,13 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; expect(clockwiseQuarterTurns, expectedQuarterTurns, @@ -1407,8 +1442,13 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; expect(clockwiseQuarterTurns, expectedQuarterTurns, @@ -1504,8 +1544,13 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns < 0 ? rotatedBox.quarterTurns + 4 @@ -1601,8 +1646,20 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // When the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. When the front camera + // is in portrait mode, we expect the camera preview to be mirrored + // across the y-axis. + if (currentDeviceOrientation == DeviceOrientation.landscapeLeft || + currentDeviceOrientation == DeviceOrientation.landscapeRight) { + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + } else { + expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + } + expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns < 0 ? rotatedBox.quarterTurns + 4 : rotatedBox.quarterTurns; @@ -1828,8 +1885,13 @@ void main() { // used. expect(rotatedBox.child, isA()); - final Transform tranformedPreview = rotatedBox.child! as Transform; - expect((tranformedPreview.child! as Texture).textureId, cameraId); + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); expect(rotatedBox.quarterTurns, expectedQuarterTurns, reason: getExpectedRotationTestFailureReason( expectedQuarterTurns, rotatedBox.quarterTurns)); From fe4ffb374da2e6ca73f301c9c639febc50c83439 Mon Sep 17 00:00:00 2001 From: jesswrd Date: Mon, 2 Jun 2025 15:26:10 -0700 Subject: [PATCH 04/12] updated changelog --- packages/camera/camera_android_camerax/CHANGELOG.md | 4 ++++ packages/camera/camera_android_camerax/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index ffb540b6587..07833b03851 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.18+1 + +* Fixes incorrect camera preview mirroring for front cameras of devices using the `ImageReader` Impeller backend. + ## 0.6.18 * Adds support for the `MediaSettings.enableAudio` setting, which determines whether or not audio is diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 1299af343e3..f0cd8d8ace2 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.18 +version: 0.6.18+1 environment: sdk: ^3.7.0 From bdf32a76ddec619637a95269daed63dc647ecdfe Mon Sep 17 00:00:00 2001 From: jesswrd Date: Mon, 2 Jun 2025 16:17:06 -0700 Subject: [PATCH 05/12] formatted files --- .../lib/src/image_reader_rotated_preview.dart | 43 +- .../test/preview_rotation_test.dart | 532 ++++++++++-------- 2 files changed, 315 insertions(+), 260 deletions(-) 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 71da8ff6b86..3bff2cd0497 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 @@ -157,31 +157,26 @@ 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, - ); - } + // 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: cameraPreview, - ); - } else { - return const SizedBox.shrink(); } - }); + + return RotatedBox( + quarterTurns: rotationDegrees ~/ 90, + child: cameraPreview, + ); + } else { + return const SizedBox.shrink(); + } + }, + ); } } 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 1dcb989fb96..4512a47be65 100644 --- a/packages/camera/camera_android_camerax/test/preview_rotation_test.dart +++ b/packages/camera/camera_android_camerax/test/preview_rotation_test.dart @@ -1167,28 +1167,29 @@ void main() { 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; - - // Since the front camera is in portrait mode, we expect the camera - // preview to be mirrored across the y-axis. - expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); - expect((transformedPreview.child! as Texture).textureId, cameraId); - expect( - rotatedBox.quarterTurns, - expectedQuarterTurns, - reason: getExpectedRotationTestFailureReason( - expectedQuarterTurns, + // 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 transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = + transformedPreview.transform; + + // Since the front camera is in portrait mode, we expect the camera + // preview to be mirrored across the y-axis. + expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); + expect( rotatedBox.quarterTurns, - ), - ); - }, - ); + expectedQuarterTurns, + reason: getExpectedRotationTestFailureReason( + expectedQuarterTurns, + rotatedBox.quarterTurns, + ), + ); + }, + ); testWidgets( 'initial device orientation fixed to DeviceOrientation.landscapeRight, then the preview Texture is rotated 180 degrees clockwise', @@ -1221,33 +1222,35 @@ void main() { await tester.pumpWidget(camera.buildPreview(cameraId)); await tester.pump(); - // Verify Texture is rotated by ((270 - 0 * 1 + 360) % 360) - 90 = 180 degrees. - const int expectedQuarterTurns = _180DegreesClockwise; - final RotatedBox rotatedBox = - tester.widget(find.byType(RotatedBox)); + // Verify Texture is rotated by ((270 - 0 * 1 + 360) % 360) - 90 = 180 degrees. + 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()); + // 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = + transformedPreview.transform; - // Since the front camera is in landscape mode, we expect the camera - // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); - expect((transformedPreview.child! as Texture).textureId, cameraId); - expect( - rotatedBox.quarterTurns, - expectedQuarterTurns, - reason: getExpectedRotationTestFailureReason( - expectedQuarterTurns, + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); + expect( rotatedBox.quarterTurns, - ), - ); - }, - ); + expectedQuarterTurns, + reason: getExpectedRotationTestFailureReason( + expectedQuarterTurns, + rotatedBox.quarterTurns, + ), + ); + }, + ); testWidgets( 'initial device orientation fixed to DeviceOrientation.portraitDown, then the preview Texture is rotated 90 degrees clockwise', @@ -1280,33 +1283,35 @@ void main() { await tester.pumpWidget(camera.buildPreview(cameraId)); await tester.pump(); - // Verify Texture is rotated by ((270 - 0 * 1 + 360) % 360) - 180 = 90 degrees clockwise. - const int expectedQuarterTurns = _90DegreesClockwise; - final RotatedBox rotatedBox = - tester.widget(find.byType(RotatedBox)); + // Verify Texture is rotated by ((270 - 0 * 1 + 360) % 360) - 180 = 90 degrees clockwise. + 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()); + // 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = + transformedPreview.transform; - // Since the front camera is in portrait mode, we expect the camera - // preview to be mirrored across the y-axis. - expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); - expect((transformedPreview.child! as Texture).textureId, cameraId); - expect( - rotatedBox.quarterTurns, - expectedQuarterTurns, - reason: getExpectedRotationTestFailureReason( - expectedQuarterTurns, + // Since the front camera is in portrait mode, we expect the camera + // preview to be mirrored across the y-axis. + expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); + expect( rotatedBox.quarterTurns, - ), - ); - }, - ); + expectedQuarterTurns, + reason: getExpectedRotationTestFailureReason( + expectedQuarterTurns, + rotatedBox.quarterTurns, + ), + ); + }, + ); testWidgets( 'initial device orientation fixed to DeviceOrientation.landscapeLeft, then the preview Texture is rotated 0 degrees clockwise', @@ -1339,28 +1344,37 @@ void main() { await tester.pumpWidget(camera.buildPreview(cameraId)); await tester.pump(); - // Verify Texture is rotated by ((270 - 0 * 1 + 360) % 360) - 270 = 0 degrees clockwise. - const int expectedQuarterTurns = _0DegreesClockwise; - final RotatedBox rotatedBox = - tester.widget(find.byType(RotatedBox)); + // Verify Texture is rotated by ((270 - 0 * 1 + 360) % 360) - 270 = 0 degrees clockwise. + const int expectedQuarterTurns = _0DegreesClockwise; + 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()); + // 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = + transformedPreview.transform; - // Since the front camera is in landscape mode, we expect the camera - // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); - expect((transformedPreview.child! as Texture).textureId, cameraId); - expect(rotatedBox.quarterTurns, expectedQuarterTurns, - reason: getExpectedRotationTestFailureReason( - expectedQuarterTurns, rotatedBox.quarterTurns)); - }); - }); + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); + expect( + rotatedBox.quarterTurns, + expectedQuarterTurns, + reason: getExpectedRotationTestFailureReason( + expectedQuarterTurns, + rotatedBox.quarterTurns, + ), + ); + }, + ); + }, + ); // Test that preview rotation responds to initial default display rotation: group( @@ -1439,22 +1453,29 @@ void main() { 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()); + // 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = + transformedPreview.transform; - // Since the front camera is in landscape mode, we expect the camera - // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); - expect((transformedPreview.child! as Texture).textureId, cameraId); - expect(rotatedBox.quarterTurns, expectedQuarterTurns, - reason: getExpectedRotationTestFailureReason( - expectedQuarterTurns, rotatedBox.quarterTurns)); - }); + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); + expect( + rotatedBox.quarterTurns, + expectedQuarterTurns, + reason: getExpectedRotationTestFailureReason( + expectedQuarterTurns, + rotatedBox.quarterTurns, + ), + ); + }, + ); testWidgets( 'initial default display rotation is 90 degrees, then the preview Texture is rotated 90 degrees clockwise', @@ -1485,31 +1506,39 @@ void main() { await tester.pumpWidget(camera.buildPreview(cameraId)); await tester.pump(); - // Verify Texture is rotated by ((270 - 270 * 1 + 360) % 360) - 270 = -270 degrees clockwise = 90 degrees clockwise. - // 270 is used in this calculation for the device orientation because it is the counter-clockwise degrees of the - // default display rotation. - const int expectedQuarterTurns = _90DegreesClockwise; - final RotatedBox rotatedBox = - tester.widget(find.byType(RotatedBox)); + // Verify Texture is rotated by ((270 - 270 * 1 + 360) % 360) - 270 = -270 degrees clockwise = 90 degrees clockwise. + // 270 is used in this calculation for the device orientation because it is the counter-clockwise degrees of the + // default display rotation. + 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()); + // 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = + transformedPreview.transform; - // Since the front camera is in landscape mode, we expect the camera - // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); - expect((transformedPreview.child! as Texture).textureId, cameraId); + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); - final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; - expect(clockwiseQuarterTurns, expectedQuarterTurns, - reason: getExpectedRotationTestFailureReason( - expectedQuarterTurns, rotatedBox.quarterTurns)); - }); + final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; + expect( + clockwiseQuarterTurns, + expectedQuarterTurns, + reason: getExpectedRotationTestFailureReason( + expectedQuarterTurns, + rotatedBox.quarterTurns, + ), + ); + }, + ); testWidgets( 'initial default display rotation is 180 degrees, then the preview Texture is rotated 180 degrees clockwise', @@ -1540,29 +1569,37 @@ void main() { await tester.pumpWidget(camera.buildPreview(cameraId)); await tester.pump(); - // Verify Texture is rotated by ((270 - 180 * 1 + 360) % 360) - 270 = -180 degrees clockwise = 180 degrees clockwise. - const int expectedQuarterTurns = _180DegreesClockwise; - final RotatedBox rotatedBox = - tester.widget(find.byType(RotatedBox)); + // Verify Texture is rotated by ((270 - 180 * 1 + 360) % 360) - 270 = -180 degrees clockwise = 180 degrees clockwise. + 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()); + // 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = + transformedPreview.transform; - // Since the front camera is in landscape mode, we expect the camera - // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); - expect((transformedPreview.child! as Texture).textureId, cameraId); + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); - final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; - expect(clockwiseQuarterTurns, expectedQuarterTurns, - reason: getExpectedRotationTestFailureReason( - expectedQuarterTurns, rotatedBox.quarterTurns)); - }); + final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; + expect( + clockwiseQuarterTurns, + expectedQuarterTurns, + reason: getExpectedRotationTestFailureReason( + expectedQuarterTurns, + rotatedBox.quarterTurns, + ), + ); + }, + ); testWidgets( 'initial default display rotation is 270 degrees, then the preview Texture is rotated 270 degrees clockwise', @@ -1593,32 +1630,41 @@ void main() { await tester.pumpWidget(camera.buildPreview(cameraId)); await tester.pump(); - // Verify Texture is rotated by ((270 - 90 * 1 + 360) % 360) - 270 = -90 degrees clockwise = 270 degrees clockwise. - // 90 is used in this calculation for the device orientation because it is the counter-clockwise degrees of the - // default display rotation. - const int expectedQuarterTurns = _270DegreesClockwise; - final RotatedBox rotatedBox = - tester.widget(find.byType(RotatedBox)); + // Verify Texture is rotated by ((270 - 90 * 1 + 360) % 360) - 270 = -90 degrees clockwise = 270 degrees clockwise. + // 90 is used in this calculation for the device orientation because it is the counter-clockwise degrees of the + // default display rotation. + 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()); + // 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = + transformedPreview.transform; - // Since the front camera is in landscape mode, we expect the camera - // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); - expect((transformedPreview.child! as Texture).textureId, cameraId); + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); - final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; - expect(clockwiseQuarterTurns, expectedQuarterTurns, - reason: getExpectedRotationTestFailureReason( - expectedQuarterTurns, rotatedBox.quarterTurns)); - }); - }); + final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; + expect( + clockwiseQuarterTurns, + expectedQuarterTurns, + reason: getExpectedRotationTestFailureReason( + expectedQuarterTurns, + rotatedBox.quarterTurns, + ), + ); + }, + ); + }, + ); // Test that preview widgets responds as expected to the default display rotation changing: testWidgets( @@ -1707,30 +1753,33 @@ void main() { await tester.pumpAndSettle(); - // Verify Texture is rotated by expected clockwise degrees. - final int expectedQuarterTurns = - expectedRotationPerDefaultDisplayRotation[ - 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; - - // Since the front camera is in landscape mode, we expect the camera - // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); - expect((transformedPreview.child! as Texture).textureId, cameraId); - - final int clockwiseQuarterTurns = rotatedBox.quarterTurns < 0 - ? rotatedBox.quarterTurns + 4 - : rotatedBox.quarterTurns; - expect(clockwiseQuarterTurns, expectedQuarterTurns, + // Verify Texture is rotated by expected clockwise degrees. + final int expectedQuarterTurns = + expectedRotationPerDefaultDisplayRotation[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 transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); + + final int clockwiseQuarterTurns = + rotatedBox.quarterTurns < 0 + ? rotatedBox.quarterTurns + 4 + : rotatedBox.quarterTurns; + 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.', ); @@ -1814,36 +1863,39 @@ void main() { await tester.pumpAndSettle(); - // Verify Texture is rotated by expected clockwise degrees. - final int expectedQuarterTurns = - expectedRotationPerDeviceOrientation[currentDeviceOrientation]!; - final RotatedBox rotatedBox = tester.widget( - find.byType(RotatedBox), - ); + // Verify Texture is rotated by expected clockwise degrees. + final int expectedQuarterTurns = + expectedRotationPerDeviceOrientation[currentDeviceOrientation]!; + 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()); + // 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; + final Transform transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = transformedPreview.transform; - // When the front camera is in landscape mode, we expect the camera - // preview to be mirrored across the x-axis. When the front camera - // is in portrait mode, we expect the camera preview to be mirrored - // across the y-axis. - if (currentDeviceOrientation == DeviceOrientation.landscapeLeft || - currentDeviceOrientation == DeviceOrientation.landscapeRight) { - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); - } else { - expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); - } - expect((transformedPreview.child! as Texture).textureId, cameraId); - final int clockwiseQuarterTurns = rotatedBox.quarterTurns < 0 - ? rotatedBox.quarterTurns + 4 - : rotatedBox.quarterTurns; - expect(clockwiseQuarterTurns, expectedQuarterTurns, + // When the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. When the front camera + // is in portrait mode, we expect the camera preview to be mirrored + // across the y-axis. + if (currentDeviceOrientation == DeviceOrientation.landscapeLeft || + currentDeviceOrientation == DeviceOrientation.landscapeRight) { + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + } else { + expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + } + expect((transformedPreview.child! as Texture).textureId, cameraId); + final int clockwiseQuarterTurns = + rotatedBox.quarterTurns < 0 + ? rotatedBox.quarterTurns + 4 + : rotatedBox.quarterTurns; + 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.', ); @@ -2081,29 +2133,37 @@ void main() { await tester.pumpWidget(camera.buildPreview(cameraId)); await tester.pump(); - // Verify Texture is rotated by ((90 - 270 * 1 + 360) % 360) - 90 = 90 degrees clockwise. - // 270 is used in this calculation for the device orientation because it is the counter-clockwise degrees of the - // default display rotation. - 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 transformedPreview = rotatedBox.child! as Transform; - final Matrix4 transformedPreviewMatrix = transformedPreview.transform; - - // Since the front camera is in landscape mode, we expect the camera - // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); - expect((transformedPreview.child! as Texture).textureId, cameraId); - expect(rotatedBox.quarterTurns, expectedQuarterTurns, - reason: getExpectedRotationTestFailureReason( - expectedQuarterTurns, rotatedBox.quarterTurns)); - }); + // Verify Texture is rotated by ((90 - 270 * 1 + 360) % 360) - 90 = 90 degrees clockwise. + // 270 is used in this calculation for the device orientation because it is the counter-clockwise degrees of the + // default display rotation. + 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 transformedPreview = rotatedBox.child! as Transform; + final Matrix4 transformedPreviewMatrix = + transformedPreview.transform; + + // Since the front camera is in landscape mode, we expect the camera + // preview to be mirrored across the x-axis. + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + expect((transformedPreview.child! as Texture).textureId, cameraId); + expect( + rotatedBox.quarterTurns, + expectedQuarterTurns, + reason: getExpectedRotationTestFailureReason( + expectedQuarterTurns, + rotatedBox.quarterTurns, + ), + ); + }, + ); testWidgets( 'camera is back facing, then the preview Texture is rotated 270 degrees clockwise', From e2fba01a0807e4ccaf75a169c5d8617e31661575 Mon Sep 17 00:00:00 2001 From: jesswrd Date: Wed, 11 Jun 2025 14:10:48 -0700 Subject: [PATCH 06/12] added helper function. has to check entire matrix --- .../test/preview_rotation_test.dart | 60 +++++++++++++++---- 1 file changed, 47 insertions(+), 13 deletions(-) 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 4512a47be65..dd0c7184ebc 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, Transform; +import 'package:flutter/widgets.dart' show MatrixUtils, RotatedBox, Texture, Transform; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -380,6 +380,40 @@ void main() { ) => 'Expected the preview to be rotated by $expectedQuarterTurns quarter turns (which is ${expectedQuarterTurns * 90} degrees clockwise) but instead was rotated $actualQuarterTurns quarter turns.'; + /// Checks that the transform matrix (Matrix4) mirrors across the x-axis by + /// confirming the following to be the transformation matrix: + /// [[-1.0, 0.0, 0.0, 0.0], + /// [ 0.0, 1.0, 0.0, 0.0], + /// [ 0.0, 0.0, 1.0, 0.0], + /// [ 0.0, 0.0, 0.0, 1.0]] + void checkXAxisIsMirrored(Matrix4 transformationMatrix) { + final Matrix4 mirrorAcrossXMatrix = Matrix4( + -1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + + expect(MatrixUtils.matrixEquals(mirrorAcrossXMatrix, transformationMatrix), isTrue); + } + + /// Checks that the transform matrix (Matrix4) mirrors across the y-axis by + /// confirming the following to be the transformation matrix: + /// [[1.0, 0.0, 0.0, 0.0], + /// [ 0.0, -1.0, 0.0, 0.0], + /// [ 0.0, 0.0, 1.0, 0.0], + /// [ 0.0, 0.0, 0.0, 1.0]] + void checkYAxisIsMirrored(Matrix4 transformationMatrix) { + final Matrix4 mirrorAcrossYMatrix = Matrix4( + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + + expect(MatrixUtils.matrixEquals(mirrorAcrossYMatrix, transformationMatrix), isTrue); + } + group('when handlesCropAndRotation is true', () { // Test that preview rotation responds to initial default display rotation: group('initial device orientation is landscapeRight,', () { @@ -1178,7 +1212,7 @@ void main() { // Since the front camera is in portrait mode, we expect the camera // preview to be mirrored across the y-axis. - expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + checkYAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1239,7 +1273,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1300,7 +1334,7 @@ void main() { // Since the front camera is in portrait mode, we expect the camera // preview to be mirrored across the y-axis. - expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + checkYAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1361,7 +1395,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1464,7 +1498,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1525,7 +1559,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; @@ -1586,7 +1620,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; @@ -1649,7 +1683,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; @@ -1770,7 +1804,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = @@ -1884,9 +1918,9 @@ void main() { // across the y-axis. if (currentDeviceOrientation == DeviceOrientation.landscapeLeft || currentDeviceOrientation == DeviceOrientation.landscapeRight) { - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); } else { - expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + checkYAxisIsMirrored(transformedPreviewMatrix); } expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = @@ -2152,7 +2186,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, From c4b1fd6102b20f78c7b45c0d07b1582dbb7f8777 Mon Sep 17 00:00:00 2001 From: jesswrd Date: Wed, 11 Jun 2025 14:15:00 -0700 Subject: [PATCH 07/12] Revert "added helper function. has to check entire matrix" This reverts commit e2fba01a0807e4ccaf75a169c5d8617e31661575. --- .../test/preview_rotation_test.dart | 60 ++++--------------- 1 file changed, 13 insertions(+), 47 deletions(-) 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 dd0c7184ebc..4512a47be65 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 MatrixUtils, RotatedBox, Texture, Transform; +import 'package:flutter/widgets.dart' show RotatedBox, Texture, Transform; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -380,40 +380,6 @@ void main() { ) => 'Expected the preview to be rotated by $expectedQuarterTurns quarter turns (which is ${expectedQuarterTurns * 90} degrees clockwise) but instead was rotated $actualQuarterTurns quarter turns.'; - /// Checks that the transform matrix (Matrix4) mirrors across the x-axis by - /// confirming the following to be the transformation matrix: - /// [[-1.0, 0.0, 0.0, 0.0], - /// [ 0.0, 1.0, 0.0, 0.0], - /// [ 0.0, 0.0, 1.0, 0.0], - /// [ 0.0, 0.0, 0.0, 1.0]] - void checkXAxisIsMirrored(Matrix4 transformationMatrix) { - final Matrix4 mirrorAcrossXMatrix = Matrix4( - -1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 - ); - - expect(MatrixUtils.matrixEquals(mirrorAcrossXMatrix, transformationMatrix), isTrue); - } - - /// Checks that the transform matrix (Matrix4) mirrors across the y-axis by - /// confirming the following to be the transformation matrix: - /// [[1.0, 0.0, 0.0, 0.0], - /// [ 0.0, -1.0, 0.0, 0.0], - /// [ 0.0, 0.0, 1.0, 0.0], - /// [ 0.0, 0.0, 0.0, 1.0]] - void checkYAxisIsMirrored(Matrix4 transformationMatrix) { - final Matrix4 mirrorAcrossYMatrix = Matrix4( - 1.0, 0.0, 0.0, 0.0, - 0.0, -1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 - ); - - expect(MatrixUtils.matrixEquals(mirrorAcrossYMatrix, transformationMatrix), isTrue); - } - group('when handlesCropAndRotation is true', () { // Test that preview rotation responds to initial default display rotation: group('initial device orientation is landscapeRight,', () { @@ -1212,7 +1178,7 @@ void main() { // Since the front camera is in portrait mode, we expect the camera // preview to be mirrored across the y-axis. - checkYAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1273,7 +1239,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - checkXAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1334,7 +1300,7 @@ void main() { // Since the front camera is in portrait mode, we expect the camera // preview to be mirrored across the y-axis. - checkYAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1395,7 +1361,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - checkXAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1498,7 +1464,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - checkXAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1559,7 +1525,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - checkXAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; @@ -1620,7 +1586,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - checkXAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; @@ -1683,7 +1649,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - checkXAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; @@ -1804,7 +1770,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - checkXAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = @@ -1918,9 +1884,9 @@ void main() { // across the y-axis. if (currentDeviceOrientation == DeviceOrientation.landscapeLeft || currentDeviceOrientation == DeviceOrientation.landscapeRight) { - checkXAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); } else { - checkYAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); } expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = @@ -2186,7 +2152,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - checkXAxisIsMirrored(transformedPreviewMatrix); + expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, From 013926390789c0a631900e933b828bbd91bfed6c Mon Sep 17 00:00:00 2001 From: jesswrd Date: Wed, 11 Jun 2025 14:20:18 -0700 Subject: [PATCH 08/12] added helper function. has to check entire matrix (take 2) --- .../test/preview_rotation_test.dart | 60 +++++++++++++++---- 1 file changed, 47 insertions(+), 13 deletions(-) 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 4512a47be65..dd0c7184ebc 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, Transform; +import 'package:flutter/widgets.dart' show MatrixUtils, RotatedBox, Texture, Transform; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -380,6 +380,40 @@ void main() { ) => 'Expected the preview to be rotated by $expectedQuarterTurns quarter turns (which is ${expectedQuarterTurns * 90} degrees clockwise) but instead was rotated $actualQuarterTurns quarter turns.'; + /// Checks that the transform matrix (Matrix4) mirrors across the x-axis by + /// confirming the following to be the transformation matrix: + /// [[-1.0, 0.0, 0.0, 0.0], + /// [ 0.0, 1.0, 0.0, 0.0], + /// [ 0.0, 0.0, 1.0, 0.0], + /// [ 0.0, 0.0, 0.0, 1.0]] + void checkXAxisIsMirrored(Matrix4 transformationMatrix) { + final Matrix4 mirrorAcrossXMatrix = Matrix4( + -1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + + expect(MatrixUtils.matrixEquals(mirrorAcrossXMatrix, transformationMatrix), isTrue); + } + + /// Checks that the transform matrix (Matrix4) mirrors across the y-axis by + /// confirming the following to be the transformation matrix: + /// [[1.0, 0.0, 0.0, 0.0], + /// [ 0.0, -1.0, 0.0, 0.0], + /// [ 0.0, 0.0, 1.0, 0.0], + /// [ 0.0, 0.0, 0.0, 1.0]] + void checkYAxisIsMirrored(Matrix4 transformationMatrix) { + final Matrix4 mirrorAcrossYMatrix = Matrix4( + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + + expect(MatrixUtils.matrixEquals(mirrorAcrossYMatrix, transformationMatrix), isTrue); + } + group('when handlesCropAndRotation is true', () { // Test that preview rotation responds to initial default display rotation: group('initial device orientation is landscapeRight,', () { @@ -1178,7 +1212,7 @@ void main() { // Since the front camera is in portrait mode, we expect the camera // preview to be mirrored across the y-axis. - expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + checkYAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1239,7 +1273,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1300,7 +1334,7 @@ void main() { // Since the front camera is in portrait mode, we expect the camera // preview to be mirrored across the y-axis. - expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + checkYAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1361,7 +1395,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1464,7 +1498,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, @@ -1525,7 +1559,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; @@ -1586,7 +1620,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; @@ -1649,7 +1683,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = rotatedBox.quarterTurns + 4; @@ -1770,7 +1804,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = @@ -1884,9 +1918,9 @@ void main() { // across the y-axis. if (currentDeviceOrientation == DeviceOrientation.landscapeLeft || currentDeviceOrientation == DeviceOrientation.landscapeRight) { - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); } else { - expect(transformedPreviewMatrix.storage[5], closeTo(-1.0, 0.0001)); + checkYAxisIsMirrored(transformedPreviewMatrix); } expect((transformedPreview.child! as Texture).textureId, cameraId); final int clockwiseQuarterTurns = @@ -2152,7 +2186,7 @@ void main() { // Since the front camera is in landscape mode, we expect the camera // preview to be mirrored across the x-axis. - expect(transformedPreviewMatrix.storage[0], closeTo(-1.0, 0.0001)); + checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); expect( rotatedBox.quarterTurns, From ad702cc4ad14450e4d9d97466c2fd26316520483 Mon Sep 17 00:00:00 2001 From: jesswrd Date: Wed, 11 Jun 2025 16:31:28 -0700 Subject: [PATCH 09/12] formatted tests --- .../test/preview_rotation_test.dart | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) 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 dd0c7184ebc..e6921b29077 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,8 @@ 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 MatrixUtils, RotatedBox, Texture, Transform; +import 'package:flutter/widgets.dart' + show MatrixUtils, RotatedBox, Texture, Transform; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -388,13 +389,28 @@ void main() { /// [ 0.0, 0.0, 0.0, 1.0]] void checkXAxisIsMirrored(Matrix4 transformationMatrix) { final Matrix4 mirrorAcrossXMatrix = Matrix4( - -1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 + -1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, ); - expect(MatrixUtils.matrixEquals(mirrorAcrossXMatrix, transformationMatrix), isTrue); + expect( + MatrixUtils.matrixEquals(mirrorAcrossXMatrix, transformationMatrix), + isTrue, + ); } /// Checks that the transform matrix (Matrix4) mirrors across the y-axis by @@ -405,13 +421,28 @@ void main() { /// [ 0.0, 0.0, 0.0, 1.0]] void checkYAxisIsMirrored(Matrix4 transformationMatrix) { final Matrix4 mirrorAcrossYMatrix = Matrix4( - 1.0, 0.0, 0.0, 0.0, - 0.0, -1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, ); - expect(MatrixUtils.matrixEquals(mirrorAcrossYMatrix, transformationMatrix), isTrue); + expect( + MatrixUtils.matrixEquals(mirrorAcrossYMatrix, transformationMatrix), + isTrue, + ); } group('when handlesCropAndRotation is true', () { From de19674cfa15c8692cb510ba27c035f36407ba97 Mon Sep 17 00:00:00 2001 From: jesswrd Date: Wed, 11 Jun 2025 16:32:35 -0700 Subject: [PATCH 10/12] updated changelog --- packages/camera/camera_android_camerax/CHANGELOG.md | 4 ++++ packages/camera/camera_android_camerax/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index effa6ef6379..0361ce451e8 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.18+3 + +* Fixes incorrect camera preview mirroring for front cameras of devices using the Impeller backend. + ## 0.6.18+2 * Fixes premature garbage collection of native objects when app is under memory pressure. diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index dc0f0ad90a8..0bf7e8a986a 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.18+2 +version: 0.6.18+3 environment: sdk: ^3.7.0 From 0061ab06b3d313d1215e9cd53fa301f89866adbd Mon Sep 17 00:00:00 2001 From: Reid Baker <1063596+reidbaker@users.noreply.github.com> Date: Thu, 12 Jun 2025 10:45:39 -0400 Subject: [PATCH 11/12] Update packages/camera/camera_android_camerax/CHANGELOG.md Co-authored-by: Camille Simon <43054281+camsim99@users.noreply.github.com> --- packages/camera/camera_android_camerax/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 0361ce451e8..5f9c3c2a36c 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.6.18+3 -* Fixes incorrect camera preview mirroring for front cameras of devices using the Impeller backend. +* Fixes incorrect camera preview mirroring for front cameras of devices using the Impeller backend. ## 0.6.18+2 From 541f53740f2af4e78d34734732d0d37f265b9306 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Thu, 12 Jun 2025 07:59:41 -0700 Subject: [PATCH 12/12] add comment --- .../lib/src/image_reader_rotated_preview.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 f588ad06ce3..e1c42b4bd59 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,7 +152,8 @@ final class _ImageReaderRotatedPreviewState sign: widget.facingSign, ); - // If the camera is front facing, mirror the camera preview + // If the camera is front facing (widget.facingSign is 1 for front + // cameras, -1 for back cameras), mirror the camera preview // according to the current device orientation. Widget cameraPreview = widget.child; if (widget.facingSign == 1) {