react-native-camera_edit/windows/ReactNativeCameraCPP/CameraRotationHelper.cpp
2025-07-09 11:33:00 +09:00

243 lines
10 KiB
C++

#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<int>(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<bool> const& handler)
{
return m_orientationChangedEvent.add(handler);
}
void CameraRotationHelper::OrientationChanged(winrt::event_token const& token) noexcept
{
m_orientationChangedEvent.remove(token);
}
}