#include "pch.h" #include "CameraRotationHelper.h" #include "CameraRotationHelper.g.cpp" namespace winrt { using namespace Windows::Devices::Enumeration; using namespace Windows::Devices::Sensors; using namespace Windows::Storage::FileProperties; using namespace Windows::Graphics::Display; } //namespace winrt namespace winrt::ReactNativeCameraCPP::implementation { CameraRotationHelper::CameraRotationHelper(EnclosureLocation location) { m_cameraEnclosureLocation = location; if (!IsEnclosureLocationExternal(m_cameraEnclosureLocation) && m_orientationSensor != nullptr) { m_sensorOrientationChanged_revoker = m_orientationSensor.OrientationChanged(winrt::auto_revoke, [ref = get_weak()](auto const& sender, auto const& args) { if (auto self = ref.get()) { self->SimpleOrientationSensor_OrientationChanged(sender, args); } }); } m_displayOrientationChanged_revoker = m_displayInformation.OrientationChanged(winrt::auto_revoke, [ref = get_weak()](auto const& sender, auto const& args) { if (auto self = ref.get()) { self->DisplayInformation_OrientationChanged(sender, args); } }); } PhotoOrientation CameraRotationHelper::GetConvertedCameraCaptureOrientation() { auto orientation = GetCameraCaptureOrientation(); return ConvertSimpleOrientationToPhotoOrientation(orientation); } SimpleOrientation CameraRotationHelper::GetCameraCaptureOrientation() { if (IsEnclosureLocationExternal(m_cameraEnclosureLocation)) { // Cameras that are not attached to the device do not rotate along with it, so apply no rotation return SimpleOrientation::NotRotated; } // Get the device orientation offset by the camera hardware offset auto deviceOrientation = m_orientationSensor ? m_orientationSensor.GetCurrentOrientation() : SimpleOrientation::NotRotated; auto result = SubtractOrientations(deviceOrientation, GetCameraOrientationRelativeToNativeOrientation()); // If the preview is being mirrored for a front-facing camera, then the rotation should be inverted if (ShouldMirrorPreview()) { result = MirrorOrientation(result); } return result; } SimpleOrientation CameraRotationHelper::GetCameraPreviewOrientation() { if (IsEnclosureLocationExternal(m_cameraEnclosureLocation)) { // Cameras that are not attached to the device do not rotate along with it, so apply no rotation return SimpleOrientation::NotRotated; } // Get the app display rotation offset by the camera hardware offset auto result = ConvertDisplayOrientationToSimpleOrientation(m_displayInformation.CurrentOrientation()); result = SubtractOrientations(result, GetCameraOrientationRelativeToNativeOrientation()); // If the preview is being mirrored for a front-facing camera, then the rotation should be inverted if (ShouldMirrorPreview()) { result = MirrorOrientation(result); } return result; } int CameraRotationHelper::GetCameraPreviewClockwiseDegrees() { auto rotation = GetCameraPreviewOrientation(); return ConvertSimpleOrientationToClockwiseDegrees(rotation); } PhotoOrientation CameraRotationHelper::ConvertSimpleOrientationToPhotoOrientation(SimpleOrientation orientation) { switch (orientation) { case SimpleOrientation::Rotated90DegreesCounterclockwise: return PhotoOrientation::Rotate90; case SimpleOrientation::Rotated180DegreesCounterclockwise: return PhotoOrientation::Rotate180; case SimpleOrientation::Rotated270DegreesCounterclockwise: return PhotoOrientation::Rotate270; case SimpleOrientation::NotRotated: default: return PhotoOrientation::Normal; } } int CameraRotationHelper::ConvertSimpleOrientationToClockwiseDegrees(SimpleOrientation orientation) { switch (orientation) { case SimpleOrientation::Rotated90DegreesCounterclockwise: return 270; case SimpleOrientation::Rotated180DegreesCounterclockwise: return 180; case SimpleOrientation::Rotated270DegreesCounterclockwise: return 90; case SimpleOrientation::NotRotated: default: return 0; } } SimpleOrientation CameraRotationHelper::ConvertDisplayOrientationToSimpleOrientation(DisplayOrientations orientation) { SimpleOrientation result; switch (orientation) { case DisplayOrientations::Landscape: result = SimpleOrientation::NotRotated; break; case DisplayOrientations::PortraitFlipped: result = SimpleOrientation::Rotated90DegreesCounterclockwise; break; case DisplayOrientations::LandscapeFlipped: result = SimpleOrientation::Rotated180DegreesCounterclockwise; break; case DisplayOrientations::Portrait: default: result = SimpleOrientation::Rotated270DegreesCounterclockwise; break; } // Above assumes landscape; offset is needed if native orientation is portrait if (m_displayInformation.NativeOrientation() == DisplayOrientations::Portrait) { result = AddOrientations(result, SimpleOrientation::Rotated90DegreesCounterclockwise); } return result; } SimpleOrientation CameraRotationHelper::SubtractOrientations(SimpleOrientation a, SimpleOrientation b) { auto aRot = ConvertSimpleOrientationToClockwiseDegrees(a); auto bRot = ConvertSimpleOrientationToClockwiseDegrees(b); // Add 360 to ensure the modulus operator does not operate on a negative auto result = (360 + (aRot - bRot)) % 360; return ConvertClockwiseDegreesToSimpleOrientation(result); } SimpleOrientation CameraRotationHelper::MirrorOrientation(SimpleOrientation orientation) { // This only affects the 90 and 270 degree cases, because rotating 0 and 180 degrees is the same clockwise and counter-clockwise switch (orientation) { case SimpleOrientation::Rotated90DegreesCounterclockwise: return SimpleOrientation::Rotated270DegreesCounterclockwise; case SimpleOrientation::Rotated270DegreesCounterclockwise: return SimpleOrientation::Rotated90DegreesCounterclockwise; } return orientation; } SimpleOrientation CameraRotationHelper::AddOrientations(SimpleOrientation a, SimpleOrientation b) { auto aRot = ConvertSimpleOrientationToClockwiseDegrees(a); auto bRot = ConvertSimpleOrientationToClockwiseDegrees(b); auto result = (aRot + bRot) % 360; return ConvertClockwiseDegreesToSimpleOrientation(result); } SimpleOrientation CameraRotationHelper::ConvertClockwiseDegreesToSimpleOrientation(int orientation) { switch (orientation) { case 270: return SimpleOrientation::Rotated90DegreesCounterclockwise; case 180: return SimpleOrientation::Rotated180DegreesCounterclockwise; case 90: return SimpleOrientation::Rotated270DegreesCounterclockwise; case 0: default: return SimpleOrientation::NotRotated; } } void CameraRotationHelper::SimpleOrientationSensor_OrientationChanged(IInspectable const&, IInspectable const&) { if (m_orientationSensor.GetCurrentOrientation() != SimpleOrientation::Faceup && m_orientationSensor.GetCurrentOrientation() != SimpleOrientation::Facedown) { // Only raise the OrientationChanged event if the device is not parallel to the ground. This allows users to take pictures of documents (FaceUp) // or the ceiling (FaceDown) in portrait or landscape, by first holding the device in the desired orientation, and then pointing the camera // either up or down, at the desired subject. //Note: This assumes that the camera is either facing the same way as the screen, or the opposite way. For devices with cameras mounted // on other panels, this logic should be adjusted. m_orientationChangedEvent(*this, false); } } void CameraRotationHelper::DisplayInformation_OrientationChanged(IInspectable const&, IInspectable const&) { m_orientationChangedEvent(*this, true); } bool CameraRotationHelper::ShouldMirrorPreview() { // It is recommended that applications mirror the preview for front-facing cameras, as it gives users a more natural experience, since it behaves more like a mirror return (m_cameraEnclosureLocation.Panel() == Panel::Front); } SimpleOrientation CameraRotationHelper::GetCameraOrientationRelativeToNativeOrientation() { // Get the rotation angle of the camera enclosure as it is mounted in the device hardware auto enclosureAngle = ConvertClockwiseDegreesToSimpleOrientation(static_cast(m_cameraEnclosureLocation.RotationAngleInDegreesClockwise())); // Account for the fact that, on portrait-first devices, the built in camera sensor is read at a 90 degree offset to the native orientation if (m_displayInformation.NativeOrientation() == DisplayOrientations::Portrait && !IsEnclosureLocationExternal(m_cameraEnclosureLocation)) { enclosureAngle = AddOrientations(SimpleOrientation::Rotated90DegreesCounterclockwise, enclosureAngle); } return enclosureAngle; } winrt::event_token CameraRotationHelper::OrientationChanged(Windows::Foundation::EventHandler const& handler) { return m_orientationChangedEvent.add(handler); } void CameraRotationHelper::OrientationChanged(winrt::event_token const& token) noexcept { m_orientationChangedEvent.remove(token); } }