[BUG] CamaraView Photo Rotate
Is there an existing issue for this?
- [X] I have searched the existing issues
Did you read the "Reporting a bug" section on Contributing file?
- [X] I have read the "Reporting a bug" section on Contributing file: https://github.com/CommunityToolkit/Maui/blob/main/CONTRIBUTING.md#reporting-a-bug
Current Behavior
Under certain circumstances photos are taken and displayed rotated.
Expected Behavior
Photos are expected to be saved/returned with the same orientation as they were captured with cameraview.
Steps To Reproduce
Using (https://github.com/CommunityToolkit/Maui/blob/main/samples/CommunityToolkit.Maui.Sample/Pages/Views/CameraView/CameraViewPage.xaml.cs)
How to reproduce the error?
1 - Position your device in portrait and do not turn the device until further notice. 2 - Enter CameraView Page 3 - Take a photo --> the photo will be displayed correctly in the image. 4 - Put the device in landscape 5 - Take a photo --> the photo will be displayed incorrectly in the image.
Take the photo without errors.
1 - Position your device in portrait and do not turn the device until further notice. 2 - Touch CameraView Page 3 - Take a photo --> the photo will be displayed correctly in the image. 4 - Exit the CameraViewPage 5 - Put the device in landscape 6 - Enter CameraView Page 3 - Take a photo --> the photo will be displayed correctly in the image.
The problem is that when taking the photo it saves the exif horientation that the device had before entering the CameraViewPage. At least that is my conclusion.
Link to public reproduction project repository
https://github.com/CommunityToolkit/Maui/blob/main/samples/CommunityToolkit.Maui.Sample/Pages/Views/CameraView/CameraViewPage.xaml.cs
Environment
- .NET MAUI CommunityToolkit:8.0.1
- OS:Windows 10 Build 10.0.19041.0
- .NET MAUI: 8.0
. (Samsung Flip 5 Android 14 / Samsung S24 Android 14 / Samsung Galaxy A03 Core Android 13 )
Anything else?
No response
In the following table you can see the results obtained from the different tests carried out to determine the image recording position according to the position of the device.
Description of the columns:
Start orientation: Position in which the device is located before entering the camera view.
Camera view orientation: Position in which the device is located when capturing the image.
Exif.Rotation: Orientation that is recorded in the exif.
Image:* Example of how the image looks WITH the exif.
Image WITHOUT Exif: Example of how the image looks WITHOUT the exif.
Necessary rotation: Rotation necessary for the image to be correctly oriented.
Exif.Rotation: Orientation that is recorded in the exif.
Image: Example of how the image looks WITHOUT the exif.
Subtraction logic Rear: Logic used so that the image is always in view in a well-positioned position. In which “Camera view orientation” is subtracted from “Start orientation”.
Samsung equipment used for the tests: Samsung s24, z flip, s8.
Motorola equipment used for the test: Motorola e22.
Tables:
Rear Camera:
| Samsung s24, z flip, s8 Camera with Exif Data. | Motorola e22 Camera without Exif Data | |||||||
|---|---|---|---|---|---|---|---|---|
| Device | Image 1 | Image 2 | Logic | Image 1 | Logic | |||
| Orientation start | Orientation camera view | Exif.Rotation | Image | Image without Exif | Necessary rotation | Exif.Rotation | Exif.Image | Necessary rotation |
![]() |
![]() |
90 | ![]() |
![]() |
90 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
90 | ![]() |
![]() |
0 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
90 | ![]() |
![]() |
-90 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
90 | ![]() |
![]() |
180 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
0 | ![]() |
![]() |
90 | Sin rotacion | ![]() |
Subtraction logic Rear |
![]() |
![]() |
0 | ![]() |
![]() |
0 | Sin rotacion | ![]() |
Subtraction logic Rear |
![]() |
![]() |
0 | ![]() |
![]() |
-90 | Sin rotacion | ![]() |
Subtraction logic Rear |
![]() |
![]() |
0 | ![]() |
![]() |
180 | Sin rotacion | ![]() |
Subtraction logic Rear |
![]() |
![]() |
-90 | ![]() |
![]() |
90 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
-90 | ![]() |
![]() |
0 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
-90 | ![]() |
![]() |
-90 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
-90 | ![]() |
![]() |
180 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
180 | ![]() |
![]() |
90 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
180 | ![]() |
![]() |
0 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
180 | ![]() |
![]() |
-90 | Not detected | ![]() |
Subtraction logic Rear |
![]() |
![]() |
180 | ![]() |
![]() |
180 | Not detected | ![]() |
Subtraction logic Rear |
Camara Frontal:
Front Subtraction Logic: Logic used to keep the image always in the correct position. In which the orientation of the device when taking the photo with the front camera is subtracted from the orientation of the device before touching the camera switch button.
| Samsung | Motorola | |||||||
|---|---|---|---|---|---|---|---|---|
| Device | Image 1 | Image 2 | Rotacion | Image 1 | Rotacion | |||
| Vista camara trasera | Vista camara frontal | Exif.Rotation | Image | Image without Exif | Necessary rotation | Exif.Rotation | Image | Necessary rotation |
![]() |
![]() |
-90 | ![]() |
![]() |
-90 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
-90 | ![]() |
![]() |
0 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
-90 | ![]() |
![]() |
90 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
-90 | ![]() |
![]() |
180 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
0 | ![]() |
![]() |
-90 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
0 | ![]() |
![]() |
0 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
0 | ![]() |
![]() |
90 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
0 | ![]() |
![]() |
180 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
90 | ![]() |
![]() |
-90 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
90 | ![]() |
![]() |
0 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
90 | ![]() |
![]() |
90 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
90 | ![]() |
![]() |
180 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
180 | ![]() |
![]() |
-90 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
180 | ![]() |
![]() |
0 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
180 | ![]() |
![]() |
90 | Not detected | ![]() |
Front Subtraction Logic |
![]() |
![]() |
180 | ![]() |
![]() |
180 | Not detected | ![]() |
Front Subtraction Logic |
To solve the problem on Android, after obtaining the image I leave you a code extract
private void MyCamera_MediaCaptured(object sender, CommunityToolkit.Maui.Views.MediaCapturedEventArgs e)
{
try
{
if (Dispatcher.IsDispatchRequired)
{
Dispatcher.Dispatch(() =>
{
using (var memoryStream = new MemoryStream())
{
MediaCaptured(e);
}
});
return;
}
MediaCaptured(e);
}
catch (Exception ex)
{
var a = ex.ToString();
}
}
private void MediaCaptured(CommunityToolkit.Maui.Views.MediaCapturedEventArgs e)
{
int ExifOrientation = 0;
using (var memoryStream = new MemoryStream())
{
//Toma la foto
e.Media.CopyTo(memoryStream);
memoryStream.Position = 0;
MyImage.Source = ImageSource.FromStream(() => new MemoryStream(memoryStream.ToArray()));
memoryStream.Position = 0;
ExifOrientation = ExifOrientation_Get(memoryStream);
deviceOrientationPreviusPage = tPhotoDto.OrientationPreviusPage;
int OrientationPreviusPageDegrees = Utils.Tools.ConvertDeviceDisplayRotationToDegrees(deviceOrientationPreviusPage);
int OrientationCameraViewPageDegrees = Utils.Tools.ConvertDeviceDisplayRotationToDegrees(DeviceOrientationCameraViewPage);
int ExifOrientationDegrees = ConvertExitOrientationToDegress(ExifOrientation);
PhotoRotateLogic(ExifOrientation, memoryStream);
}
}
private void PhotoRotateLogic(int ExifOrientation, MemoryStream memoryStream)
{
memoryStream.Position = 0;
if (ExifOrientation != 0)
{
PhotoRotateLogicWithExif(memoryStream);
}
else
{
PhotoRotateLogicUnExif(memoryStream);
}
MyImageRotate.Source = ImageSource.FromStream(() => new MemoryStream(fotoBytes));
}
private void PhotoRotateLogicWithExif(MemoryStream memoryStream)
{
int rotateDegrees = 0;
//se eliminalos exif. En funcion de la orientacion de esta page
//se rota la foto, ya que el cameraView tiene un bug que
//no se orienta la foto como se correctamente.
if (MyCamera.SelectedCamera?.Position == CameraPosition.Rear)
{
switch (DeviceOrientationCameraViewPage)
{
case 1:
rotateDegrees = 90;
break;
case 2:
rotateDegrees = 0;
break;
case 3:
rotateDegrees = -90;
break;
case 4:
rotateDegrees = 180;
break;
}
}
else
{
switch (DeviceOrientationCameraViewPage)
{
case 1:
rotateDegrees = -90;
break;
case 2:
rotateDegrees = 0;
break;
case 3:
rotateDegrees = 90;
break;
case 4:
rotateDegrees = 180;
break;
}
}
fotoBytes = RotateImage(memoryStream, rotateDegrees).ToArray();
}
private void PhotoRotateLogicUnExif(MemoryStream memoryStream)
{
int rotateDegrees = 0;
if (MyCamera.SelectedCamera?.Position == CameraPosition.Rear)
{
rotateDegrees = Utils.Tools.ConvertDeviceDisplayRotationToDegrees(deviceOrientationPreviusPage) - Utils.Tools.ConvertDeviceDisplayRotationToDegrees(DeviceOrientationCameraViewPage);
}
else
{
rotateDegrees = Utils.Tools.ConvertDeviceDisplayRotationToDegrees(DeviceOrientationCameraViewPage) - Utils.Tools.ConvertDeviceDisplayRotationToDegrees(DeviceOrientationWhenSeletedCameraFront);
}
fotoBytes = RotateImage(memoryStream, rotateDegrees).ToArray();
}
public static MemoryStream RotateImage(Stream imageStream, int rotateDegrees)
{
if (imageStream.CanSeek)
{
imageStream.Seek(0, SeekOrigin.Begin);
}
// Cargar la imagen original desde el stream
using (var original = SKBitmap.Decode(imageStream))
{
SKBitmap rotatedBitmap;
rotatedBitmap = RotateBitmap(original, rotateDegrees);
// Crear un nuevo MemoryStream para la imagen rotada
var rotatedStream = new MemoryStream();
rotatedBitmap.Encode(rotatedStream, SKEncodedImageFormat.Jpeg, 100);
rotatedStream.Seek(0, SeekOrigin.Begin);
rotatedStream.Position = 0;
return rotatedStream;
}
}
private static SKBitmap RotateBitmap(SKBitmap original, float degrees)
{
int width = degrees == 90 || degrees == -90 || degrees == 270 || degrees == -270 ? original.Height : original.Width;
int height = degrees == 90 || degrees == -90 || degrees == 270 || degrees == -270 ? original.Width : original.Height;
var rotatedBitmap = new SKBitmap(width, height);
using (var surface = SKSurface.Create(new SKImageInfo(width, height)))
{
var canvas = surface.Canvas;
canvas.Clear(SKColors.Transparent);
canvas.Translate(width / 2, height / 2);
canvas.RotateDegrees(degrees);
canvas.Translate(-original.Width / 2, -original.Height / 2);
canvas.DrawBitmap(original, new SKPoint(0, 0));
using (var img = surface.Snapshot())
using (var pixmap = img.PeekPixels())
{
pixmap.ReadPixels(rotatedBitmap.Info, rotatedBitmap.GetPixels(), rotatedBitmap.RowBytes);
}
}
return rotatedBitmap;
}
Sorry we are primarily English speaking on this team. It looks like you have provided a lot of detail but I don't understand it. Would you mind translating it into English please?
Sorry we are primarily English speaking on this team. It looks like you have provided a lot of detail but I don't understand it. Would you mind translating it into English please?
@bijington Done!! :)
That is really helpful, thank you
That is really helpful, thank you @bijington If you need a testing project, ask me and I will prepare it!
I have the same problem. It behaves totally differently on different devices, so it is impossible to fix it with a simple rotate.
I have the same problem. It behaves totally differently on different devices, so it is impossible to fix it with a simple rotate.
Look at the solution, I think we covered all the possibilities there
@AugPav what is Utils.Tools that you are using?
@AugPav what is Utils.Tools that you are using?
It is only used to convert the position of the device to degrees and display it on the screen for analysis, nothing relevant
This issue still persists in .net 9, updating all packages. Does Maui people really think this problem is not relevant?
This issue still persists in .net 9, updating all packages. Does Maui people really think this problem is not relevant?
This is an open source project worked on by the community. We openly accept PRs so please feel free to get involved and help fix it
@AugPav would you mind testing your use cases against #2523 and letting me know if it resolves the issue. Thanks!
I'm encountering the same issue on both iOS and Android. It would be awesome to see the PRs that fix this case approved soon!







