Skip to content

Commit 0b372b3

Browse files
committed
Fix: camera rotation
1 parent 780b035 commit 0b372b3

File tree

2 files changed

+107
-15
lines changed

2 files changed

+107
-15
lines changed

modules/camera/camera_android.cpp

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
#include "core/os/os.h"
3434
#include "platform/android/display_server_android.h"
35+
#include "platform/android/java_godot_io_wrapper.h"
36+
#include "platform/android/os_android.h"
3537

3638
//////////////////////////////////////////////////////////////////////////
3739
// Helper functions
@@ -94,22 +96,35 @@ CameraFeedAndroid::~CameraFeedAndroid() {
9496
}
9597

9698
void CameraFeedAndroid::_set_rotation() {
97-
int display_rotation = DisplayServerAndroid::get_singleton()->get_display_rotation();
98-
// reverse rotation
99-
switch (display_rotation) {
100-
case 90:
101-
display_rotation = 270;
102-
break;
103-
case 270:
104-
display_rotation = 90;
105-
break;
106-
default:
107-
break;
108-
}
99+
CameraRotationParams params;
100+
params.sensorOrientation = orientation;
101+
params.cameraFacing = (position == CameraFeed::FEED_FRONT) ? CameraFacing::FRONT : CameraFacing::BACK;
102+
params.displayRotation = get_app_orientation();
103+
params.needsMirror = false;
104+
105+
RotationResult result = calculate_rotation(params);
109106

110-
int sign = position == CameraFeed::FEED_FRONT ? 1 : -1;
111-
float imageRotation = (orientation - display_rotation * sign + 360) % 360;
112-
transform.set_rotation(real_t(Math::deg_to_rad(imageRotation)));
107+
if (result.isValid) {
108+
float imageRotation = static_cast<float>(result.rotationAngle);
109+
transform.set_rotation(real_t(Math::deg_to_rad(imageRotation)));
110+
} else {
111+
// Fallback.
112+
int display_rotation = DisplayServerAndroid::get_singleton()->get_display_rotation();
113+
switch (display_rotation) {
114+
case 90:
115+
display_rotation = 270;
116+
break;
117+
case 270:
118+
display_rotation = 90;
119+
break;
120+
default:
121+
break;
122+
}
123+
124+
int sign = position == CameraFeed::FEED_FRONT ? 1 : -1;
125+
float imageRotation = (orientation - display_rotation * sign + 360) % 360;
126+
transform.set_rotation(real_t(Math::deg_to_rad(imageRotation)));
127+
}
113128
}
114129

115130
void CameraFeedAndroid::_add_formats() {
@@ -594,3 +609,57 @@ void CameraAndroid::set_monitoring_feeds(bool p_monitoring_feeds) {
594609
CameraAndroid::~CameraAndroid() {
595610
remove_all_feeds();
596611
}
612+
613+
//////////////////////////////////////////////////////////////////////////
614+
// Camera rotation calculation
615+
// Based on https://developer.android.com/media/camera/camerax/orientation-rotation
616+
617+
RotationResult CameraFeedAndroid::calculate_rotation(const CameraRotationParams &params) {
618+
RotationResult result = { 0, false, false };
619+
620+
if (params.sensorOrientation < 0 || params.sensorOrientation > 270 ||
621+
params.sensorOrientation % 90 != 0) {
622+
return result;
623+
}
624+
625+
int rotationAngle = params.sensorOrientation - params.displayRotation;
626+
627+
result.rotationAngle = normalize_angle(rotationAngle);
628+
result.shouldMirror = params.needsMirror || (params.cameraFacing == CameraFacing::FRONT);
629+
result.isValid = true;
630+
631+
return result;
632+
}
633+
634+
int CameraFeedAndroid::normalize_angle(int angle) {
635+
while (angle < 0) {
636+
angle += 360;
637+
}
638+
return angle % 360;
639+
}
640+
641+
int CameraFeedAndroid::get_display_rotation() {
642+
return DisplayServerAndroid::get_singleton()->get_display_rotation();
643+
}
644+
645+
int CameraFeedAndroid::get_app_orientation() {
646+
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
647+
ERR_FAIL_NULL_V(godot_io_java, 0);
648+
649+
int orientation = godot_io_java->get_screen_orientation();
650+
switch (orientation) {
651+
case 0: // SCREEN_LANDSCAPE
652+
return 90;
653+
case 1: // SCREEN_PORTRAIT
654+
return 0;
655+
case 2: // SCREEN_REVERSE_LANDSCAPE
656+
return 270;
657+
case 3: // SCREEN_REVERSE_PORTRAIT
658+
return 180;
659+
case 4: // SCREEN_SENSOR_LANDSCAPE
660+
case 5: // SCREEN_SENSOR_PORTRAIT
661+
case 6: // SCREEN_SENSOR
662+
default:
663+
return get_display_rotation();
664+
}
665+
}

modules/camera/camera_android.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,24 @@
4141
#include <camera/NdkCameraMetadataTags.h>
4242
#include <media/NdkImageReader.h>
4343

44+
enum class CameraFacing {
45+
BACK = 0,
46+
FRONT = 1
47+
};
48+
49+
struct CameraRotationParams {
50+
int sensorOrientation;
51+
CameraFacing cameraFacing;
52+
int displayRotation;
53+
bool needsMirror;
54+
};
55+
56+
struct RotationResult {
57+
int rotationAngle;
58+
bool shouldMirror;
59+
bool isValid;
60+
};
61+
4462
class CameraFeedAndroid : public CameraFeed {
4563
GDSOFTCLASS(CameraFeedAndroid, CameraFeed);
4664

@@ -66,6 +84,11 @@ class CameraFeedAndroid : public CameraFeed {
6684
void _set_rotation();
6785

6886
static void compact_stride_inplace(uint8_t *data, size_t width, int height, size_t stride);
87+
88+
static RotationResult calculate_rotation(const CameraRotationParams &params);
89+
static int normalize_angle(int angle);
90+
static int get_display_rotation();
91+
static int get_app_orientation();
6992
static void onError(void *context, ACameraDevice *p_device, int error);
7093
static void onDisconnected(void *context, ACameraDevice *p_device);
7194
static void onImage(void *context, AImageReader *p_reader);

0 commit comments

Comments
 (0)