Browse Source

fix photo orientation

Luke Pulverenti 8 years ago
parent
commit
d76bcd8473

+ 1 - 1
Emby.Drawing.ImageMagick/ImageMagickEncoder.cs

@@ -130,7 +130,7 @@ namespace Emby.Drawing.ImageMagick
                 string.Equals(ext, ".webp", StringComparison.OrdinalIgnoreCase);
         }
 
-        public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
+        public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
         {
             // Even if the caller specified 100, don't use it because it takes forever
             quality = Math.Min(quality, 99);

+ 112 - 9
Emby.Drawing.Skia/SkiaEncoder.cs

@@ -184,7 +184,7 @@ namespace Emby.Drawing.Skia
         }
 
         private string[] TransparentImageTypes = new string[] { ".png", ".gif", ".webp" };
-        private SKBitmap Decode(string path, bool forceCleanBitmap = false)
+        private SKBitmap Decode(string path, bool forceCleanBitmap, out SKCodecOrigin origin)
         {
             var requiresTransparencyHack = TransparentImageTypes.Contains(Path.GetExtension(path) ?? string.Empty);
 
@@ -199,6 +199,8 @@ namespace Emby.Drawing.Skia
                     // decode
                     codec.GetPixels(bitmap.Info, bitmap.GetPixels());
 
+                    origin = codec.Origin;
+
                     return bitmap;
                 }
             }
@@ -207,7 +209,7 @@ namespace Emby.Drawing.Skia
 
             if (resultBitmap == null)
             {
-                return Decode(path, true);
+                return Decode(path, true, out origin);
             }
 
             // If we have to resize these they often end up distorted
@@ -215,27 +217,128 @@ namespace Emby.Drawing.Skia
             {
                 using (resultBitmap)
                 {
-                    return Decode(path, true);
+                    return Decode(path, true, out origin);
                 }
             }
 
+            origin = SKCodecOrigin.TopLeft;
             return resultBitmap;
         }
 
-        private SKBitmap GetBitmap(string path, bool cropWhitespace)
+        private SKBitmap GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, out SKCodecOrigin origin)
         {
             if (cropWhitespace)
             {
-                using (var bitmap = Decode(path))
+                using (var bitmap = Decode(path, forceAnalyzeBitmap, out origin))
                 {
                     return CropWhiteSpace(bitmap);
                 }
             }
 
-            return Decode(path);
+            return Decode(path, forceAnalyzeBitmap, out origin);
+        }
+
+        private SKBitmap GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation)
+        {
+            SKCodecOrigin origin;
+
+            if (autoOrient)
+            {
+                var bitmap = GetBitmap(path, cropWhitespace, true, out origin);
+
+                if (origin != SKCodecOrigin.TopLeft)
+                {
+                    using (bitmap)
+                    {
+                        return RotateAndFlip(bitmap, origin);
+                    }
+                }
+
+                return bitmap;
+            }
+
+            return GetBitmap(path, cropWhitespace, false, out origin);
+        }
+
+        private SKBitmap RotateAndFlip(SKBitmap original, SKCodecOrigin origin)
+        {
+            // these are the origins that represent a 90 degree turn in some fashion
+            var differentOrientations = new SKCodecOrigin[]
+            {
+                SKCodecOrigin.LeftBottom,
+                SKCodecOrigin.LeftTop,
+                SKCodecOrigin.RightBottom,
+                SKCodecOrigin.RightTop
+            };
+
+            // check if we need to turn the image
+            bool isDifferentOrientation = differentOrientations.Any(o => o == origin);
+
+            // define new width/height
+            var width = isDifferentOrientation ? original.Height : original.Width;
+            var height = isDifferentOrientation ? original.Width : original.Height;
+
+            var bitmap = new SKBitmap(width, height, true);
+
+            // todo: the stuff in this switch statement should be rewritten to use pointers
+            switch (origin)
+            {
+                case SKCodecOrigin.LeftBottom:
+
+                    for (var x = 0; x < original.Width; x++)
+                        for (var y = 0; y < original.Height; y++)
+                            bitmap.SetPixel(y, original.Width - 1 - x, original.GetPixel(x, y));
+                    break;
+
+                case SKCodecOrigin.RightTop:
+
+                    for (var x = 0; x < original.Width; x++)
+                        for (var y = 0; y < original.Height; y++)
+                            bitmap.SetPixel(original.Height - 1 - y, x, original.GetPixel(x, y));
+                    break;
+
+                case SKCodecOrigin.RightBottom:
+
+                    for (var x = 0; x < original.Width; x++)
+                        for (var y = 0; y < original.Height; y++)
+                            bitmap.SetPixel(original.Height - 1 - y, original.Width - 1 - x, original.GetPixel(x, y));
+
+                    break;
+
+                case SKCodecOrigin.LeftTop:
+
+                    for (var x = 0; x < original.Width; x++)
+                        for (var y = 0; y < original.Height; y++)
+                            bitmap.SetPixel(y, x, original.GetPixel(x, y));
+                    break;
+
+                case SKCodecOrigin.BottomLeft:
+
+                    for (var x = 0; x < original.Width; x++)
+                        for (var y = 0; y < original.Height; y++)
+                            bitmap.SetPixel(x, original.Height - 1 - y, original.GetPixel(x, y));
+                    break;
+
+                case SKCodecOrigin.BottomRight:
+
+                    for (var x = 0; x < original.Width; x++)
+                        for (var y = 0; y < original.Height; y++)
+                            bitmap.SetPixel(original.Width - 1 - x, original.Height - 1 - y, original.GetPixel(x, y));
+                    break;
+
+                case SKCodecOrigin.TopRight:
+
+                    for (var x = 0; x < original.Width; x++)
+                        for (var y = 0; y < original.Height; y++)
+                            bitmap.SetPixel(original.Width - 1 - x, y, original.GetPixel(x, y));
+                    break;
+
+            }
+
+            return bitmap;
         }
 
-        public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
+        public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
         {
             if (string.IsNullOrWhiteSpace(inputPath))
             {
@@ -253,7 +356,7 @@ namespace Emby.Drawing.Skia
             var blur = options.Blur ?? 0;
             var hasIndicator = options.AddPlayedIndicator || options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0);
 
-            using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace))
+            using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient, orientation))
             {
                 if (bitmap == null)
                 {
@@ -265,7 +368,7 @@ namespace Emby.Drawing.Skia
                 var originalImageSize = new ImageSize(bitmap.Width, bitmap.Height);
                 ImageHelper.SaveImageSize(inputPath, dateModified, originalImageSize);
 
-                if (!options.CropWhiteSpace && options.HasDefaultOptions(inputPath, originalImageSize))
+                if (!options.CropWhiteSpace && options.HasDefaultOptions(inputPath, originalImageSize) && !autoOrient)
                 {
                     // Just spit out the original file if all the options are default
                     return inputPath;

+ 13 - 15
Emby.Drawing/ImageProcessor.cs

@@ -217,14 +217,23 @@ namespace Emby.Drawing
                 dateModified = tuple.Item2;
             }
 
-            if (options.HasDefaultOptions(originalImagePath))
+            var photo = item as Photo;
+            var autoOrient = false;
+            ImageOrientation? orientation = null;
+            if (photo != null && photo.Orientation.HasValue && photo.Orientation.Value != ImageOrientation.TopLeft)
+            {
+                autoOrient = true;
+                orientation = photo.Orientation;
+            }
+
+            if (options.HasDefaultOptions(originalImagePath) && !autoOrient)
             {
                 // Just spit out the original file if all the options are default
                 return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
 
             ImageSize? originalImageSize = GetSavedImageSize(originalImagePath, dateModified);
-            if (originalImageSize.HasValue && options.HasDefaultOptions(originalImagePath, originalImageSize.Value))
+            if (originalImageSize.HasValue && options.HasDefaultOptions(originalImagePath, originalImageSize.Value) && !autoOrient)
             {
                 // Just spit out the original file if all the options are default
                 _logger.Info("Returning original image {0}", originalImagePath);
@@ -243,7 +252,6 @@ namespace Emby.Drawing
 
                 if (!_fileSystem.FileExists(cacheFilePath))
                 {
-                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath));
                     var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(cacheFilePath));
                     _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath));
 
@@ -252,13 +260,14 @@ namespace Emby.Drawing
                         item = _libraryManager().GetItemById(options.ItemId);
                     }
 
-                    var resultPath =_imageEncoder.EncodeImage(originalImagePath, dateModified, tmpPath, AutoOrient(item), quality, options, outputFormat);
+                    var resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, tmpPath, autoOrient, orientation, quality, options, outputFormat);
 
                     if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase))
                     {
                         return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
                     }
 
+                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath));
                     CopyFile(tmpPath, cacheFilePath);
 
                     return new Tuple<string, string, DateTime>(tmpPath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(tmpPath));
@@ -288,17 +297,6 @@ namespace Emby.Drawing
             }
         }
 
-        private bool AutoOrient(IHasImages item)
-        {
-            var photo = item as Photo;
-            if (photo != null && photo.Orientation.HasValue)
-            {
-                return true;
-            }
-
-            return false;
-        }
-
         //private static  int[][] OPERATIONS = new int[][] {
         // TopLeft
         //new int[] {  0, NONE},

+ 1 - 1
Emby.Drawing/NullImageEncoder.cs

@@ -32,7 +32,7 @@ namespace Emby.Drawing
             throw new NotImplementedException();
         }
 
-        public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
+        public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
         {
             throw new NotImplementedException();
         }

+ 1 - 1
MediaBrowser.Controller/Drawing/IImageEncoder.cs

@@ -19,7 +19,7 @@ namespace MediaBrowser.Controller.Drawing
         /// <summary>
         /// Encodes the image.
         /// </summary>
-        string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
+        string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
 
         /// <summary>
         /// Creates the image collage.