From 2f4f8c105e57c11c48a6c840e6f3cbb11538d82d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 14 May 2017 22:27:58 -0400 Subject: [PATCH] update image processing --- .../ImageMagickEncoder.cs | 26 ++++++- Emby.Drawing.Net/GDIImageEncoder.cs | 20 +++--- Emby.Drawing.Skia/SkiaEncoder.cs | 12 +++- Emby.Drawing/ImageProcessor.cs | 67 +----------------- Emby.Drawing/NullImageEncoder.cs | 2 +- .../Drawing/IImageEncoder.cs | 11 +-- .../Drawing/ImageHelper.cs | 69 +++++++++++++++++++ .../MediaBrowser.Controller.csproj | 1 + MediaBrowser.ServerApplication/MainStartup.cs | 4 -- 9 files changed, 121 insertions(+), 91 deletions(-) create mode 100644 MediaBrowser.Controller/Drawing/ImageHelper.cs diff --git a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs index f603c49509..13bde3ca5d 100644 --- a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs +++ b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs @@ -130,7 +130,7 @@ namespace Emby.Drawing.ImageMagick string.Equals(ext, ".webp", StringComparison.OrdinalIgnoreCase); } - public void EncodeImage(string inputPath, string outputPath, bool autoOrient, int width, int height, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) + public void EncodeImage(string inputPath, ImageSize? originalImageSize, string outputPath, bool autoOrient, 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); @@ -144,6 +144,16 @@ namespace Emby.Drawing.ImageMagick originalImage.CurrentImage.TrimImage(10); } + if (options.CropWhiteSpace || !originalImageSize.HasValue) + { + originalImageSize = new ImageSize(originalImage.CurrentImage.Width, originalImage.CurrentImage.Height); + } + + var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize); + + var width = Convert.ToInt32(Math.Round(newImageSize.Width)); + var height = Convert.ToInt32(Math.Round(newImageSize.Height)); + ScaleImage(originalImage, width, height, options.Blur ?? 0); if (autoOrient) @@ -162,9 +172,19 @@ namespace Emby.Drawing.ImageMagick } else { - using (var wand = new MagickWand(width, height, options.BackgroundColor)) + using (var originalImage = new MagickWand(inputPath)) { - using (var originalImage = new MagickWand(inputPath)) + if (options.CropWhiteSpace || !originalImageSize.HasValue) + { + originalImageSize = new ImageSize(originalImage.CurrentImage.Width, originalImage.CurrentImage.Height); + } + + var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize); + + var width = Convert.ToInt32(Math.Round(newImageSize.Width)); + var height = Convert.ToInt32(Math.Round(newImageSize.Height)); + + using (var wand = new MagickWand(width, height, options.BackgroundColor)) { ScaleImage(originalImage, width, height, options.Blur ?? 0); diff --git a/Emby.Drawing.Net/GDIImageEncoder.cs b/Emby.Drawing.Net/GDIImageEncoder.cs index e710baaa77..02e7657ddf 100644 --- a/Emby.Drawing.Net/GDIImageEncoder.cs +++ b/Emby.Drawing.Net/GDIImageEncoder.cs @@ -11,6 +11,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using ImageFormat = MediaBrowser.Model.Drawing.ImageFormat; +using Emby.Drawing; namespace Emby.Drawing.Net { @@ -88,14 +89,19 @@ namespace Emby.Drawing.Net return Image.FromFile(path); } - public void EncodeImage(string inputPath, string cacheFilePath, bool autoOrient, int width, int height, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) + public void EncodeImage(string inputPath, ImageSize? originalImageSize, string outputPath, bool autoOrient, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) { - var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed > 0; - using (var originalImage = GetImage(inputPath, options.CropWhiteSpace)) { - var newWidth = Convert.ToInt32(width); - var newHeight = Convert.ToInt32(height); + if (options.CropWhiteSpace || !originalImageSize.HasValue) + { + originalImageSize = new ImageSize(originalImage.Width, originalImage.Height); + } + + var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize); + + var newWidth = Convert.ToInt32(Math.Round(newImageSize.Width)); + var newHeight = Convert.ToInt32(Math.Round(newImageSize.Height)); // Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here // Also, Webp only supports Format32bppArgb and Format32bppRgb @@ -132,10 +138,8 @@ namespace Emby.Drawing.Net var outputFormat = GetOutputFormat(originalImage, selectedOutputFormat); - _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath)); - // Save to the cache location - using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, false)) + using (var cacheFileStream = _fileSystem.GetFileStream(outputPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, false)) { // Save to the memory stream thumbnail.Save(outputFormat, cacheFileStream, quality); diff --git a/Emby.Drawing.Skia/SkiaEncoder.cs b/Emby.Drawing.Skia/SkiaEncoder.cs index 74ceb75910..018de5bc96 100644 --- a/Emby.Drawing.Skia/SkiaEncoder.cs +++ b/Emby.Drawing.Skia/SkiaEncoder.cs @@ -226,7 +226,7 @@ namespace Emby.Drawing.Skia return Decode(path); } - public void EncodeImage(string inputPath, string outputPath, bool autoOrient, int width, int height, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) + public void EncodeImage(string inputPath, ImageSize? originalImageSize, string outputPath, bool autoOrient, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) { if (string.IsNullOrWhiteSpace(inputPath)) { @@ -246,6 +246,16 @@ namespace Emby.Drawing.Skia using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace)) { + if (options.CropWhiteSpace || !originalImageSize.HasValue) + { + originalImageSize = new ImageSize(bitmap.Width, bitmap.Height); + } + + var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize); + + var width = Convert.ToInt32(Math.Round(newImageSize.Width)); + var height = Convert.ToInt32(Math.Round(newImageSize.Height)); + using (var resizedBitmap = new SKBitmap(width, height))//, bitmap.ColorType, bitmap.AlphaType)) { // scale image diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index ad6ca7d70b..3fa6f64503 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -227,7 +227,7 @@ namespace Emby.Drawing originalImageSize = null; } - var newSize = GetNewImageSize(options, originalImageSize); + var newSize = ImageHelper.GetNewImageSize(options, originalImageSize); var quality = options.Quality; var outputFormat = GetOutputFormat(options.SupportedOutputFormats[0]); @@ -239,14 +239,11 @@ namespace Emby.Drawing if (!_fileSystem.FileExists(cacheFilePath)) { - var newWidth = Convert.ToInt32(Math.Round(newSize.Width)); - var newHeight = Convert.ToInt32(Math.Round(newSize.Height)); - _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)); - _imageEncoder.EncodeImage(originalImagePath, tmpPath, AutoOrient(options.Item), newWidth, newHeight, quality, options, outputFormat); + _imageEncoder.EncodeImage(originalImagePath, originalImageSize, tmpPath, AutoOrient(options.Item), quality, options, outputFormat); CopyFile(tmpPath, cacheFilePath); return new Tuple(tmpPath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(tmpPath)); @@ -328,66 +325,6 @@ namespace Emby.Drawing return MimeTypes.GetMimeType(path); } - private ImageSize GetNewImageSize(ImageProcessingOptions options, ImageSize? originalImageSize) - { - if (originalImageSize.HasValue) - { - // Determine the output size based on incoming parameters - var newSize = DrawingUtils.Resize(originalImageSize.Value, options.Width, options.Height, options.MaxWidth, options.MaxHeight); - - return newSize; - } - return GetSizeEstimate(options); - } - - private ImageSize GetSizeEstimate(ImageProcessingOptions options) - { - if (options.Width.HasValue && options.Height.HasValue) - { - return new ImageSize(options.Width.Value, options.Height.Value); - } - - var aspect = GetEstimatedAspectRatio(options.Image.Type, options.Item); - - var width = options.Width ?? options.MaxWidth; - - if (width.HasValue) - { - var heightValue = width.Value / aspect; - return new ImageSize(width.Value, heightValue); - } - - var height = options.Height ?? options.MaxHeight ?? 200; - var widthValue = aspect * height; - return new ImageSize(widthValue, height); - } - - private double GetEstimatedAspectRatio(ImageType type, IHasImages item) - { - switch (type) - { - case ImageType.Art: - case ImageType.Backdrop: - case ImageType.Chapter: - case ImageType.Screenshot: - case ImageType.Thumb: - return 1.78; - case ImageType.Banner: - return 5.4; - case ImageType.Box: - case ImageType.BoxRear: - case ImageType.Disc: - case ImageType.Menu: - return 1; - case ImageType.Logo: - return 2.58; - case ImageType.Primary: - return item.GetDefaultPrimaryImageAspectRatio() ?? .667; - default: - return 1; - } - } - private ImageFormat GetOutputFormat(ImageFormat requestedFormat) { if (requestedFormat == ImageFormat.Webp && !_imageEncoder.SupportedOutputFormats.Contains(ImageFormat.Webp)) diff --git a/Emby.Drawing/NullImageEncoder.cs b/Emby.Drawing/NullImageEncoder.cs index c7d365fb2c..1723e0637b 100644 --- a/Emby.Drawing/NullImageEncoder.cs +++ b/Emby.Drawing/NullImageEncoder.cs @@ -32,7 +32,7 @@ namespace Emby.Drawing throw new NotImplementedException(); } - public void EncodeImage(string inputPath, string outputPath, bool autoOrient, int width, int height, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) + public void EncodeImage(string inputPath, ImageSize? originalImageSize, string outputPath, bool autoOrient, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs index 830093fcf2..64d997dba0 100644 --- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs +++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs @@ -15,18 +15,11 @@ namespace MediaBrowser.Controller.Drawing /// /// The supported output formats. ImageFormat[] SupportedOutputFormats { get; } + /// /// Encodes the image. /// - /// The input path. - /// The output path. - /// if set to true [automatic orient]. - /// The width. - /// The height. - /// The quality. - /// The options. - /// The output format. - void EncodeImage(string inputPath, string outputPath, bool autoOrient, int width, int height, int quality, ImageProcessingOptions options, ImageFormat outputFormat); + void EncodeImage(string inputPath, ImageSize? originalImageSize, string outputPath, bool autoOrient, int quality, ImageProcessingOptions options, ImageFormat outputFormat); /// /// Creates the image collage. diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs new file mode 100644 index 0000000000..30c4e90fb0 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs @@ -0,0 +1,69 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Controller.Drawing +{ + public static class ImageHelper + { + public static ImageSize GetNewImageSize(ImageProcessingOptions options, ImageSize? originalImageSize) + { + if (originalImageSize.HasValue) + { + // Determine the output size based on incoming parameters + var newSize = DrawingUtils.Resize(originalImageSize.Value, options.Width, options.Height, options.MaxWidth, options.MaxHeight); + + return newSize; + } + return GetSizeEstimate(options); + } + + private static ImageSize GetSizeEstimate(ImageProcessingOptions options) + { + if (options.Width.HasValue && options.Height.HasValue) + { + return new ImageSize(options.Width.Value, options.Height.Value); + } + + var aspect = GetEstimatedAspectRatio(options.Image.Type, options.Item); + + var width = options.Width ?? options.MaxWidth; + + if (width.HasValue) + { + var heightValue = width.Value / aspect; + return new ImageSize(width.Value, heightValue); + } + + var height = options.Height ?? options.MaxHeight ?? 200; + var widthValue = aspect * height; + return new ImageSize(widthValue, height); + } + + private static double GetEstimatedAspectRatio(ImageType type, IHasImages item) + { + switch (type) + { + case ImageType.Art: + case ImageType.Backdrop: + case ImageType.Chapter: + case ImageType.Screenshot: + case ImageType.Thumb: + return 1.78; + case ImageType.Banner: + return 5.4; + case ImageType.Box: + case ImageType.BoxRear: + case ImageType.Disc: + case ImageType.Menu: + return 1; + case ImageType.Logo: + return 2.58; + case ImageType.Primary: + return item.GetDefaultPrimaryImageAspectRatio() ?? .667; + default: + return 1; + } + } + } +} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 38cff6d674..b3a29bafad 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -91,6 +91,7 @@ + diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 272054609f..37c71545a4 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -775,8 +775,6 @@ namespace MediaBrowser.ServerApplication return; } - MessageBox.Show("The Visual C++ 2013 Runtime will now be installed.", "Install Visual C++ Runtime", MessageBoxButtons.OK, MessageBoxIcon.Information); - try { await InstallVcredist(GetVcredist2013Url()).ConfigureAwait(false); @@ -829,8 +827,6 @@ namespace MediaBrowser.ServerApplication return; } - MessageBox.Show("The Visual C++ 2015 Runtime will now be installed.", "Install Visual C++ Runtime", MessageBoxButtons.OK, MessageBoxIcon.Information); - try { await InstallVcredist(GetVcredist2015Url()).ConfigureAwait(false);