Explorar o código

Improved image processing

LukePulverenti Luke Pulverenti luke pulverenti %!s(int64=12) %!d(string=hai) anos
pai
achega
e76ff3bf16

+ 7 - 1
MediaBrowser.Api/HttpHandlers/ImageHandler.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Logging;
+using System.Drawing.Imaging;
+using MediaBrowser.Common.Logging;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
@@ -128,6 +129,11 @@ namespace MediaBrowser.Api.HttpHandlers
                 return null;
             }
 
+            if (Kernel.Instance.ImageProcessors.Any(i => i.RequiresTransparency))
+            {
+                return MimeTypes.GetMimeType(".png");
+            }
+
             return MimeTypes.GetMimeType(await GetImagePath().ConfigureAwait(false));
         }
 

+ 54 - 2
MediaBrowser.Controller/Drawing/BaseImageProcessor.cs

@@ -1,6 +1,8 @@
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Entities;
+using System.ComponentModel.Composition;
 using System.Drawing;
+using System.Drawing.Drawing2D;
 
 namespace MediaBrowser.Controller.Drawing
 {
@@ -15,19 +17,69 @@ namespace MediaBrowser.Controller.Drawing
         /// <summary>
         /// Processes the primary image for a BaseEntity (Person, Studio, User, etc)
         /// </summary>
+        /// <param name="originalImage">The original Image, before re-sizing</param>
         /// <param name="bitmap">The bitmap holding the original image, after re-sizing</param>
         /// <param name="graphics">The graphics surface on which the output is drawn</param>
         /// <param name="entity">The entity that owns the image</param>
-        public abstract void ProcessImage(Bitmap bitmap, Graphics graphics, BaseEntity entity);
+        public abstract void ProcessImage(Image originalImage, Bitmap bitmap, Graphics graphics, BaseEntity entity);
 
         /// <summary>
         /// Processes an image for a BaseItem
         /// </summary>
+        /// <param name="originalImage">The original Image, before re-sizing</param>
         /// <param name="bitmap">The bitmap holding the original image, after re-sizing</param>
         /// <param name="graphics">The graphics surface on which the output is drawn</param>
         /// <param name="entity">The entity that owns the image</param>
         /// <param name="imageType">The image type</param>
         /// <param name="imageIndex">The image index (currently only used with backdrops)</param>
-        public abstract void ProcessImage(Bitmap bitmap, Graphics graphics, BaseItem entity, ImageType imageType, int imageIndex);
+        public abstract void ProcessImage(Image originalImage, Bitmap bitmap, Graphics graphics, BaseItem entity, ImageType imageType, int imageIndex);
+
+        /// <summary>
+        /// If true, the image output format will be forced to png, resulting in an output size that will generally run larger than jpg
+        /// </summary>
+        public virtual bool RequiresTransparency
+        {
+            get
+            {
+                return false;
+            }
+        }
+    }
+
+    /// <summary>
+    /// This is demo-ware and should be deleted eventually
+    /// </summary>
+    //[Export(typeof(BaseImageProcessor))]
+    public class MyRoundedCornerImageProcessor : BaseImageProcessor
+    {
+        public override void ProcessImage(Image originalImage, Bitmap bitmap, Graphics g, BaseEntity entity)
+        {
+            var CornerRadius = 20;
+
+            g.Clear(Color.Transparent);
+                        
+            using (GraphicsPath gp = new GraphicsPath())
+            {
+                gp.AddArc(0, 0, CornerRadius, CornerRadius, 180, 90);
+                gp.AddArc(0 + bitmap.Width - CornerRadius, 0, CornerRadius, CornerRadius, 270, 90);
+                gp.AddArc(0 + bitmap.Width - CornerRadius, 0 + bitmap.Height - CornerRadius, CornerRadius, CornerRadius, 0, 90);
+                gp.AddArc(0, 0 + bitmap.Height - CornerRadius, CornerRadius, CornerRadius, 90, 90);
+                
+                g.SetClip(gp);
+                g.DrawImage(originalImage, 0, 0, bitmap.Width, bitmap.Height);
+            }
+        }
+
+        public override void ProcessImage(Image originalImage, Bitmap bitmap, Graphics graphics, BaseItem entity, ImageType imageType, int imageIndex)
+        {
+        }
+
+        public override bool RequiresTransparency
+        {
+            get
+            {
+                return true;
+            }
+        }
     }
 }

+ 18 - 9
MediaBrowser.Controller/Drawing/ImageProcessor.cs

@@ -43,6 +43,8 @@ namespace MediaBrowser.Controller.Drawing
                 thumbnail = new Bitmap(newSize.Width, newSize.Height, originalImage.PixelFormat);
             }
 
+            thumbnail.MakeTransparent();
+
             // Preserve the original resolution
             thumbnail.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution);
 
@@ -56,14 +58,21 @@ namespace MediaBrowser.Controller.Drawing
 
             thumbnailGraph.DrawImage(originalImage, 0, 0, newSize.Width, newSize.Height);
 
+            ImageFormat outputFormat = originalImage.RawFormat;
+
             // Run Kernel image processors
             if (Kernel.Instance.ImageProcessors.Any())
             {
-                ExecuteAdditionalImageProcessors(thumbnail, thumbnailGraph, entity, imageType, imageIndex);
+                ExecuteAdditionalImageProcessors(originalImage, thumbnail, thumbnailGraph, entity, imageType, imageIndex);
+
+                if (Kernel.Instance.ImageProcessors.Any(i => i.RequiresTransparency))
+                {
+                    outputFormat = ImageFormat.Png;
+                }
             }
 
             // Write to the output stream
-            SaveImage(originalImage.RawFormat, thumbnail, toStream, quality);
+            SaveImage(outputFormat, thumbnail, toStream, quality);
 
             thumbnailGraph.Dispose();
             thumbnail.Dispose();
@@ -78,7 +87,7 @@ namespace MediaBrowser.Controller.Drawing
         /// <param name="entity">The entity that owns the image</param>
         /// <param name="imageType">The image type</param>
         /// <param name="imageIndex">The image index (currently only used with backdrops)</param>
-        private static void ExecuteAdditionalImageProcessors(Bitmap bitmap, Graphics graphics, BaseEntity entity, ImageType imageType, int imageIndex)
+        private static void ExecuteAdditionalImageProcessors(Image originalImage, Bitmap bitmap, Graphics graphics, BaseEntity entity, ImageType imageType, int imageIndex)
         {
             var baseItem = entity as BaseItem;
 
@@ -86,33 +95,33 @@ namespace MediaBrowser.Controller.Drawing
             {
                 foreach (var processor in Kernel.Instance.ImageProcessors)
                 {
-                    processor.ProcessImage(bitmap, graphics, baseItem, imageType, imageIndex);
+                    processor.ProcessImage(originalImage, bitmap, graphics, baseItem, imageType, imageIndex);
                 }
             }
             else
             {
                 foreach (var processor in Kernel.Instance.ImageProcessors)
                 {
-                    processor.ProcessImage(bitmap, graphics, entity);
+                    processor.ProcessImage(originalImage, bitmap, graphics, entity);
                 }
             }
         }
 
-        public static void SaveImage(ImageFormat originalImageRawFormat, Image newImage, Stream toStream, int? quality)
+        public static void SaveImage(ImageFormat outputFormat, Image newImage, Stream toStream, int? quality)
         {
             // Use special save methods for jpeg and png that will result in a much higher quality image
             // All other formats use the generic Image.Save
-            if (ImageFormat.Jpeg.Equals(originalImageRawFormat))
+            if (ImageFormat.Jpeg.Equals(outputFormat))
             {
                 SaveJpeg(newImage, toStream, quality);
             }
-            else if (ImageFormat.Png.Equals(originalImageRawFormat))
+            else if (ImageFormat.Png.Equals(outputFormat))
             {
                 newImage.Save(toStream, ImageFormat.Png);
             }
             else
             {
-                newImage.Save(toStream, originalImageRawFormat);
+                newImage.Save(toStream, outputFormat);
             }
         }
 

+ 1 - 1
MediaBrowser.Controller/Kernel.cs

@@ -81,7 +81,7 @@ namespace MediaBrowser.Controller
         /// Gets the list of currently registered entity resolvers
         /// </summary>
         [ImportMany(typeof(BaseImageProcessor))]
-        internal IEnumerable<BaseImageProcessor> ImageProcessors { get; private set; }
+        public IEnumerable<BaseImageProcessor> ImageProcessors { get; private set; }
 
         /// <summary>
         /// Creates a kernel based on a Data path, which is akin to our current programdata path