using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
namespace MediaBrowser.Controller.Drawing
{
public static class ImageProcessor
{
///
/// Processes an image by resizing to target dimensions
///
/// The stream containing the source image
/// The stream to save the new image to
/// Use if a fixed width is required. Aspect ratio will be preserved.
/// Use if a fixed height is required. Aspect ratio will be preserved.
/// Use if a max width is required. Aspect ratio will be preserved.
/// Use if a max height is required. Aspect ratio will be preserved.
/// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice.
/// The entity that owns the image
/// The image type
/// The image index (currently only used with backdrops)
public static void ProcessImage(Stream sourceImageStream, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality, BaseEntity entity, ImageType imageType, int imageIndex)
{
Image originalImage = Image.FromStream(sourceImageStream);
// Determine the output size based on incoming parameters
Size newSize = DrawingUtils.Resize(originalImage.Size, width, height, maxWidth, maxHeight);
Bitmap thumbnail;
// Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here
if (originalImage.PixelFormat.HasFlag(PixelFormat.Indexed))
{
thumbnail = new Bitmap(originalImage, newSize.Width, newSize.Height);
}
else
{
thumbnail = new Bitmap(newSize.Width, newSize.Height, originalImage.PixelFormat);
}
// Preserve the original resolution
thumbnail.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution);
Graphics thumbnailGraph = Graphics.FromImage(thumbnail);
thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality;
thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality;
thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
thumbnailGraph.CompositingMode = CompositingMode.SourceOver;
thumbnailGraph.DrawImage(originalImage, 0, 0, newSize.Width, newSize.Height);
// Run Kernel image processors
if (Kernel.Instance.ImageProcessors.Any())
{
ExecuteAdditionalImageProcessors(thumbnail, thumbnailGraph, entity, imageType, imageIndex);
}
// Write to the output stream
SaveImage(originalImage.RawFormat, thumbnail, toStream, quality);
thumbnailGraph.Dispose();
thumbnail.Dispose();
originalImage.Dispose();
}
///
/// Executes additional image processors that are registered with the Kernel
///
/// The bitmap holding the original image, after re-sizing
/// The graphics surface on which the output is drawn
/// The entity that owns the image
/// The image type
/// The image index (currently only used with backdrops)
private static void ExecuteAdditionalImageProcessors(Bitmap bitmap, Graphics graphics, BaseEntity entity, ImageType imageType, int imageIndex)
{
var baseItem = entity as BaseItem;
if (baseItem != null)
{
foreach (var processor in Kernel.Instance.ImageProcessors)
{
processor.ProcessImage(bitmap, graphics, baseItem, imageType, imageIndex);
}
}
else
{
foreach (var processor in Kernel.Instance.ImageProcessors)
{
processor.ProcessImage(bitmap, graphics, entity);
}
}
}
public static void SaveImage(ImageFormat originalImageRawFormat, 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))
{
SaveJpeg(newImage, toStream, quality);
}
else if (ImageFormat.Png.Equals(originalImageRawFormat))
{
newImage.Save(toStream, ImageFormat.Png);
}
else
{
newImage.Save(toStream, originalImageRawFormat);
}
}
public static void SaveJpeg(Image image, Stream target, int? quality)
{
if (!quality.HasValue)
{
quality = 90;
}
using (var encoderParameters = new EncoderParameters(1))
{
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality.Value);
image.Save(target, GetImageCodecInfo("image/jpeg"), encoderParameters);
}
}
public static ImageCodecInfo GetImageCodecInfo(string mimeType)
{
ImageCodecInfo[] info = ImageCodecInfo.GetImageEncoders();
for (int i = 0; i < info.Length; i++)
{
ImageCodecInfo ici = info[i];
if (ici.MimeType.Equals(mimeType, StringComparison.OrdinalIgnoreCase))
{
return ici;
}
}
return info[1];
}
}
}