From c0c8b16e43390ed369b755ad07354addca9cae7f Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Sun, 12 Apr 2020 15:10:14 +0800 Subject: [PATCH] android/camera: Implement image flipping We use libyuv's Mirror function to handle horizontal flip. Regarding the vertical flip, libyuv doc states that 'just set a negative height' --- .../app/src/main/jni/camera/ndk_camera.cpp | 35 +++++++++++++++---- .../app/src/main/jni/camera/ndk_camera.h | 8 ++++- .../main/jni/camera/still_image_camera.cpp | 25 ++++++++++--- .../src/main/jni/camera/still_image_camera.h | 8 ++++- 4 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/android/app/src/main/jni/camera/ndk_camera.cpp b/src/android/app/src/main/jni/camera/ndk_camera.cpp index 124a8ef3b..7f9658750 100644 --- a/src/android/app/src/main/jni/camera/ndk_camera.cpp +++ b/src/android/app/src/main/jni/camera/ndk_camera.cpp @@ -210,8 +210,13 @@ CaptureSession::CaptureSession(ACameraManager* manager, const std::string& id) { #undef CAMERA_CALL #undef CREATE -Interface::Interface(Factory& factory_, const std::string& id_, const Service::CAM::Flip& flip_) - : factory(factory_), id(id_), flip(flip_) {} +Interface::Interface(Factory& factory_, const std::string& id_, const Service::CAM::Flip& flip) + : factory(factory_), id(id_) { + mirror = base_mirror = + flip == Service::CAM::Flip::Horizontal || flip == Service::CAM::Flip::Reverse; + invert = base_invert = + flip == Service::CAM::Flip::Vertical || flip == Service::CAM::Flip::Reverse; +} Interface::~Interface() = default; @@ -227,8 +232,11 @@ void Interface::SetResolution(const Service::CAM::Resolution& resolution_) { resolution = resolution_; } -void Interface::SetFlip(Service::CAM::Flip flip_) { - flip = flip_; +void Interface::SetFlip(Service::CAM::Flip flip) { + mirror = base_mirror ^ + (flip == Service::CAM::Flip::Horizontal || flip == Service::CAM::Flip::Reverse); + invert = + base_invert ^ (flip == Service::CAM::Flip::Vertical || flip == Service::CAM::Flip::Reverse); } void Interface::SetFormat(Service::CAM::OutputFormat format_) { @@ -265,19 +273,32 @@ std::vector Interface::ReceiveFrame() { scaled_y.data(), resolution.width, scaled_u.data(), resolution.width / 4, scaled_v.data(), resolution.width / 4, resolution.width, resolution.height, libyuv::kFilterBilinear); - // TODO: Record and apply flip + + if (mirror) { + std::vector mirrored_y(scaled_y.size()); + std::vector mirrored_u(scaled_u.size()); + std::vector mirrored_v(scaled_v.size()); + libyuv::I420Mirror(scaled_y.data(), resolution.width, scaled_u.data(), resolution.width / 4, + scaled_v.data(), resolution.width / 4, mirrored_y.data(), + resolution.width, mirrored_u.data(), resolution.width / 4, + mirrored_v.data(), resolution.width / 4, resolution.width, + resolution.height); + scaled_y.swap(mirrored_y); + scaled_u.swap(mirrored_u); + scaled_v.swap(mirrored_v); + } std::vector output(resolution.width * resolution.height); if (format == Service::CAM::OutputFormat::RGB565) { libyuv::I420ToRGB565(scaled_y.data(), resolution.width, scaled_u.data(), resolution.width / 4, scaled_v.data(), resolution.width / 4, reinterpret_cast(output.data()), resolution.width * 2, - resolution.width, resolution.height); + resolution.width, invert ? -resolution.height : resolution.height); } else { libyuv::I420ToYUY2(scaled_y.data(), resolution.width, scaled_u.data(), resolution.width / 4, scaled_v.data(), resolution.width / 4, reinterpret_cast(output.data()), resolution.width * 2, - resolution.width, resolution.height); + resolution.width, invert ? -resolution.height : resolution.height); } return output; } diff --git a/src/android/app/src/main/jni/camera/ndk_camera.h b/src/android/app/src/main/jni/camera/ndk_camera.h index 56156f209..349304be4 100644 --- a/src/android/app/src/main/jni/camera/ndk_camera.h +++ b/src/android/app/src/main/jni/camera/ndk_camera.h @@ -37,7 +37,13 @@ private: std::string id; Service::CAM::Resolution resolution; - Service::CAM::Flip flip; + + // Flipping parameters. mirror = horizontal, invert = vertical. + bool base_mirror{}; + bool base_invert{}; + bool mirror{}; + bool invert{}; + Service::CAM::OutputFormat format; // std::vector image; // Data fetched from the frontend // bool opened{}; // Whether the camera was successfully opened diff --git a/src/android/app/src/main/jni/camera/still_image_camera.cpp b/src/android/app/src/main/jni/camera/still_image_camera.cpp index 5ae4a3b0a..699033641 100644 --- a/src/android/app/src/main/jni/camera/still_image_camera.cpp +++ b/src/android/app/src/main/jni/camera/still_image_camera.cpp @@ -29,7 +29,12 @@ void CleanupJNI(JNIEnv* env) { env->DeleteGlobalRef(s_still_image_camera_helper_class); } -Interface::Interface(jstring path_, const Service::CAM::Flip& flip_) : path(path_), flip(flip_) {} +Interface::Interface(jstring path_, const Service::CAM::Flip& flip) : path(path_) { + mirror = base_mirror = + flip == Service::CAM::Flip::Horizontal || flip == Service::CAM::Flip::Reverse; + invert = base_invert = + flip == Service::CAM::Flip::Vertical || flip == Service::CAM::Flip::Reverse; +} Interface::~Interface() { Factory::last_path = nullptr; @@ -70,13 +75,20 @@ void Interface::StartCapture() { info.width, info.height); BITMAP_CALL(unlockPixels(env, bitmap)); + if (mirror) { + std::vector mirrored(data.size()); + libyuv::ARGBMirror(data.data(), info.stride, mirrored.data(), info.stride, info.width, + info.height); + data.swap(mirrored); + } + image.resize(info.height * info.width); if (format == Service::CAM::OutputFormat::RGB565) { libyuv::ARGBToRGB565(data.data(), info.stride, reinterpret_cast(image.data()), - info.width * 2, info.width, info.height); + info.width * 2, info.width, invert ? -info.height : info.height); } else { libyuv::ARGBToYUY2(data.data(), info.stride, reinterpret_cast(image.data()), - info.width * 2, info.width, info.height); + info.width * 2, info.width, invert ? -info.height : info.height); } opened = true; @@ -87,8 +99,11 @@ void Interface::SetResolution(const Service::CAM::Resolution& resolution_) { resolution = resolution_; } -void Interface::SetFlip(Service::CAM::Flip flip_) { - flip = flip_; +void Interface::SetFlip(Service::CAM::Flip flip) { + mirror = base_mirror ^ + (flip == Service::CAM::Flip::Horizontal || flip == Service::CAM::Flip::Reverse); + invert = + base_invert ^ (flip == Service::CAM::Flip::Vertical || flip == Service::CAM::Flip::Reverse); } void Interface::SetFormat(Service::CAM::OutputFormat format_) { diff --git a/src/android/app/src/main/jni/camera/still_image_camera.h b/src/android/app/src/main/jni/camera/still_image_camera.h index 89d40beb9..01a201096 100644 --- a/src/android/app/src/main/jni/camera/still_image_camera.h +++ b/src/android/app/src/main/jni/camera/still_image_camera.h @@ -31,7 +31,13 @@ public: private: jstring path; Service::CAM::Resolution resolution; - Service::CAM::Flip flip; + + // Flipping parameters. mirror = horizontal, invert = vertical. + bool base_mirror{}; + bool base_invert{}; + bool mirror{}; + bool invert{}; + Service::CAM::OutputFormat format; std::vector image; // Data fetched from the frontend bool opened{}; // Whether the camera was successfully opened