Prerequisites
Reproduction
https://github.com/Melon-Technologies/vc-camera-orientation-fromdegrees-repro
Steps to reproduce
- Clone https://github.com/Melon-Technologies/vc-camera-orientation-fromdegrees-repro
kotlinc camera_orientation_fromdegrees_test.kt -include-runtime -d test.jar
kotlin -classpath test.jar Camera_orientation_fromdegrees_testKt
- Observe:
LEFT.degrees=270 -> fromDegrees(270)=RIGHT and RIGHT.degrees=90 -> fromDegrees(90)=LEFT
What did you expect to happen?
CameraOrientation.Companion.fromDegrees(degrees) should be the inverse of CameraOrientation.degrees β i.e. fromDegrees(o.degrees) == o for every CameraOrientation value.
What actually happened?
It only round-trips for UP and DOWN. For LEFT and RIGHT it returns the wrong value:
val CameraOrientation.degrees: Int
get() = when (this) {
UP -> 0; DOWN -> 180; LEFT -> 270; RIGHT -> 90
}
fun CameraOrientation.Companion.fromDegrees(degrees: Int): CameraOrientation {
val normalizedDegrees = normalizeDegrees(degrees)
return when (normalizedDegrees) {
in 45..135 -> CameraOrientation.LEFT // should be RIGHT (RIGHT.degrees == 90, inside this bucket)
in 135..225 -> CameraOrientation.DOWN
in 225..315 -> CameraOrientation.RIGHT // should be LEFT (LEFT.degrees == 270, inside this bucket)
else -> CameraOrientation.UP
}
}
fromDegrees(90) returns LEFT instead of RIGHT; fromDegrees(270) returns RIGHT instead of LEFT. The LEFT/RIGHT buckets are swapped relative to .degrees' own mapping.
Why it matters: ImageProxy.orientation calls fromDegrees(imageInfo.rotationDegrees) to label every incoming camera frame:
val ImageProxy.orientation: CameraOrientation
get() {
val degrees = imageInfo.rotationDegrees
return CameraOrientation.fromDegrees(degrees)
}
A front-facing sensor held in portrait commonly reports rotationDegrees = 270. Pre-fix, fromDegrees(270) returns RIGHT instead of the self-consistent LEFT β a 180-degree-equivalent mislabeling. That wrong orientation value feeds react-native-vision-camera-resizer's rotationDegrees push constant downstream.
Observed live: on a real Android device (Samsung Galaxy A54 5G, Android 15), a detected face's bounding box was drawn over an unrelated part of the frame instead of the actual face, while the identical JS/native pipeline on iOS (iPhone 12, iOS 16.2) drew it correctly β which is what pointed at this Android-only orientation mapping rather than the detector model itself.
Fix
Swap the LEFT/RIGHT buckets to match .degrees:
fun CameraOrientation.Companion.fromDegrees(degrees: Int): CameraOrientation {
val normalizedDegrees = normalizeDegrees(degrees)
return when (normalizedDegrees) {
- in 45..135 -> CameraOrientation.LEFT
+ in 45..135 -> CameraOrientation.RIGHT
in 135..225 -> CameraOrientation.DOWN
- in 225..315 -> CameraOrientation.RIGHT
+ in 225..315 -> CameraOrientation.LEFT
else -> CameraOrientation.UP
}
}
We've been running this as a local patch-package patch to react-native-vision-camera@5.1.1 in production.
Affected platforms
Android (device)
Device(s) affected
Observed on Samsung Galaxy A54 5G (Android 15). Root cause confirmed by direct source inspection and a pure JVM round-trip test β the bug is deterministic enum-mapping logic independent of specific hardware.
VisionCamera version
5.1.1
React Native version
0.86
React Native architecture
New Architecture (Fabric / bridgeless)
Features being used
Relevant logs / stack trace
This is a logic/correctness bug, not a crash β there is no stack trace. See the reproduction repo's console output:
CameraOrientation.fromDegrees round-trip test (issue: LEFT/RIGHT swapped)
BUGGY fromDegrees (pristine upstream code):
UP.degrees=0 -> fromDegreesBuggy(0)=UP OK
DOWN.degrees=180 -> fromDegreesBuggy(180)=DOWN OK
LEFT.degrees=270 -> fromDegreesBuggy(270)=RIGHT MISMATCH (bug reproduced)
RIGHT.degrees=90 -> fromDegreesBuggy(90)=LEFT MISMATCH (bug reproduced)
-> all orientations round-trip correctly: NO -- bug confirmed
FIXED fromDegrees (LEFT/RIGHT buckets swapped):
UP.degrees=0 -> fromDegreesFixed(0)=UP OK
DOWN.degrees=180 -> fromDegreesFixed(180)=DOWN OK
LEFT.degrees=270 -> fromDegreesFixed(270)=LEFT OK
RIGHT.degrees=90 -> fromDegreesFixed(90)=RIGHT OK
-> all orientations round-trip correctly: yes, bug is fixed
RESULT: PASS -- bug reproduced on unpatched code, confirmed fixed on patched code.
Additional context
No response
Submission
Prerequisites
Reproduction
https://github.com/Melon-Technologies/vc-camera-orientation-fromdegrees-repro
Steps to reproduce
kotlinc camera_orientation_fromdegrees_test.kt -include-runtime -d test.jarkotlin -classpath test.jar Camera_orientation_fromdegrees_testKtLEFT.degrees=270 -> fromDegrees(270)=RIGHTandRIGHT.degrees=90 -> fromDegrees(90)=LEFTWhat did you expect to happen?
CameraOrientation.Companion.fromDegrees(degrees)should be the inverse ofCameraOrientation.degreesβ i.e.fromDegrees(o.degrees) == ofor everyCameraOrientationvalue.What actually happened?
It only round-trips for
UPandDOWN. ForLEFTandRIGHTit returns the wrong value:fromDegrees(90)returnsLEFTinstead ofRIGHT;fromDegrees(270)returnsRIGHTinstead ofLEFT. TheLEFT/RIGHTbuckets are swapped relative to.degrees' own mapping.Why it matters:
ImageProxy.orientationcallsfromDegrees(imageInfo.rotationDegrees)to label every incoming camera frame:A front-facing sensor held in portrait commonly reports
rotationDegrees = 270. Pre-fix,fromDegrees(270)returnsRIGHTinstead of the self-consistentLEFTβ a 180-degree-equivalent mislabeling. That wrong orientation value feedsreact-native-vision-camera-resizer'srotationDegreespush constant downstream.Observed live: on a real Android device (Samsung Galaxy A54 5G, Android 15), a detected face's bounding box was drawn over an unrelated part of the frame instead of the actual face, while the identical JS/native pipeline on iOS (iPhone 12, iOS 16.2) drew it correctly β which is what pointed at this Android-only orientation mapping rather than the detector model itself.
Fix
Swap the
LEFT/RIGHTbuckets to match.degrees:fun CameraOrientation.Companion.fromDegrees(degrees: Int): CameraOrientation { val normalizedDegrees = normalizeDegrees(degrees) return when (normalizedDegrees) { - in 45..135 -> CameraOrientation.LEFT + in 45..135 -> CameraOrientation.RIGHT in 135..225 -> CameraOrientation.DOWN - in 225..315 -> CameraOrientation.RIGHT + in 225..315 -> CameraOrientation.LEFT else -> CameraOrientation.UP } }We've been running this as a local
patch-packagepatch toreact-native-vision-camera@5.1.1in production.Affected platforms
Android (device)
Device(s) affected
Observed on Samsung Galaxy A54 5G (Android 15). Root cause confirmed by direct source inspection and a pure JVM round-trip test β the bug is deterministic enum-mapping logic independent of specific hardware.
VisionCamera version
5.1.1
React Native version
0.86
React Native architecture
New Architecture (Fabric / bridgeless)
Features being used
Relevant logs / stack trace
Additional context
No response
Submission