diff --git a/src/ImageProcessorCore/Samplers/AutoOrient.cs b/src/ImageProcessorCore/Samplers/AutoOrient.cs
new file mode 100644
index 000000000..62c7a5584
--- /dev/null
+++ b/src/ImageProcessorCore/Samplers/AutoOrient.cs
@@ -0,0 +1,84 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Adjusts an image so that its orientation is suitable for viewing.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ /// The image to crop.
+ /// The
+ public static Image AutoOrient(this Image source, ProgressEventHandler progressHandler = null)
+ where T : IPackedVector
+ where TP : struct
+ {
+ Orientation orientation = GetExifOrientation(source);
+
+ switch (orientation)
+ {
+ case Orientation.Unknown:
+ case Orientation.TopLeft:
+ default:
+ return source;
+
+ case Orientation.TopRight:
+ return source.Flip(FlipType.Horizontal, progressHandler);
+
+ case Orientation.BottomRight:
+ return source.Rotate(RotateType.Rotate180, progressHandler);
+
+ case Orientation.BottomLeft:
+ return source.Flip(FlipType.Vertical, progressHandler);
+
+ case Orientation.LeftTop:
+ return source
+ .Rotate(RotateType.Rotate90, progressHandler)
+ .Flip(FlipType.Horizontal, progressHandler);
+
+ case Orientation.RightTop:
+ return source.Rotate(RotateType.Rotate90, progressHandler);
+
+ case Orientation.RightBottom:
+ return source
+ .Flip(FlipType.Vertical, progressHandler)
+ .Rotate(RotateType.Rotate270, progressHandler);
+
+ case Orientation.LeftBottom:
+ return source.Rotate(RotateType.Rotate270, progressHandler);
+ }
+ }
+
+ private static Orientation GetExifOrientation(Image source)
+ where T : IPackedVector
+ where TP : struct
+ {
+ if (source.ExifProfile == null)
+ {
+ return Orientation.Unknown;
+ }
+
+ ExifValue value = source.ExifProfile.GetValue(ExifTag.Orientation);
+ if (value == null)
+ {
+ return Orientation.Unknown;
+ }
+
+ Orientation orientation = (Orientation)value.Value;
+
+ source.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft);
+
+ return orientation;
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Samplers/Options/Orientation.cs b/src/ImageProcessorCore/Samplers/Options/Orientation.cs
new file mode 100644
index 000000000..7ace8ab2d
--- /dev/null
+++ b/src/ImageProcessorCore/Samplers/Options/Orientation.cs
@@ -0,0 +1,20 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore
+{
+ internal enum Orientation : ushort
+ {
+ Unknown = 0,
+ TopLeft = 1,
+ TopRight = 2,
+ BottomRight = 3,
+ BottomLeft = 4,
+ LeftTop = 5,
+ RightTop = 6,
+ RightBottom = 7,
+ LeftBottom = 8
+ }
+}
diff --git a/tests/ImageProcessorCore.Tests/Processors/Samplers/AutoOrientTests.cs b/tests/ImageProcessorCore.Tests/Processors/Samplers/AutoOrientTests.cs
new file mode 100644
index 000000000..e9d93ac3c
--- /dev/null
+++ b/tests/ImageProcessorCore.Tests/Processors/Samplers/AutoOrientTests.cs
@@ -0,0 +1,60 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Tests
+{
+ using System.IO;
+ using Xunit;
+
+ public class AutoOrientTests : FileTestBase
+ {
+ public static readonly TheoryData OrientationValues
+ = new TheoryData
+ {
+ { RotateType.None, FlipType.None, 0 },
+ { RotateType.None, FlipType.None, 1 },
+ { RotateType.None, FlipType.Horizontal, 2 },
+ { RotateType.Rotate180, FlipType.None, 3 },
+ { RotateType.Rotate180, FlipType.Horizontal, 4 },
+ { RotateType.Rotate90, FlipType.Horizontal, 5 },
+ { RotateType.Rotate270, FlipType.None, 6 },
+ { RotateType.Rotate90, FlipType.Vertical, 7 },
+ { RotateType.Rotate90, FlipType.None, 8 },
+ };
+
+ [Theory]
+ [MemberData("OrientationValues")]
+ public void ImageShouldFlip(RotateType rotateType, FlipType flipType, ushort orientation)
+ {
+ const string path = "TestOutput/AutoOrient";
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+
+ string file = TestImages.Bmp.F;
+
+ using (FileStream stream = File.OpenRead(file))
+ {
+ string filename = Path.GetFileNameWithoutExtension(file) + "-" + orientation + Path.GetExtension(file);
+
+ Image image = new Image(stream);
+ image.ExifProfile = new ExifProfile();
+ image.ExifProfile.SetValue(ExifTag.Orientation, orientation);
+
+ using (FileStream before = File.OpenWrite($"{path}/before-{filename}"))
+ {
+ using (FileStream after = File.OpenWrite($"{path}/after-{filename}"))
+ {
+ image.RotateFlip(rotateType, flipType)
+ .Save(before)
+ .AutoOrient()
+ .Save(after);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageProcessorCore.Tests/TestImages.cs b/tests/ImageProcessorCore.Tests/TestImages.cs
index f9c5bb243..f8379d9f0 100644
--- a/tests/ImageProcessorCore.Tests/TestImages.cs
+++ b/tests/ImageProcessorCore.Tests/TestImages.cs
@@ -38,6 +38,9 @@ namespace ImageProcessorCore.Tests
private static readonly string folder = "TestImages/Formats/Bmp/";
public static string Car { get { return folder + "Car.bmp"; } }
+
+ public static string F { get { return folder + "F.bmp"; } }
+
public static string Neg_height { get { return folder + "neg_height.bmp"; } }
}
diff --git a/tests/ImageProcessorCore.Tests/TestImages/Formats/Bmp/F.bmp b/tests/ImageProcessorCore.Tests/TestImages/Formats/Bmp/F.bmp
new file mode 100644
index 000000000..d95598bef
--- /dev/null
+++ b/tests/ImageProcessorCore.Tests/TestImages/Formats/Bmp/F.bmp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6da008d2b285b2db946e6d4ebf8569b0ddd4a05ef273b38304cb65afccac87b3
+size 65502