diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageExtensions.cs b/MediaBrowser.Server.Implementations/Drawing/ImageExtensions.cs deleted file mode 100644 index 28ea26a322..0000000000 --- a/MediaBrowser.Server.Implementations/Drawing/ImageExtensions.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using System.IO; - -namespace MediaBrowser.Server.Implementations.Drawing -{ - /// - /// Class ImageExtensions - /// - public static class ImageExtensions - { - /// - /// Saves the image. - /// - /// The output format. - /// The image. - /// To stream. - /// The quality. - public static void Save(this Image image, ImageFormat outputFormat, 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(outputFormat)) - { - SaveAsJpeg(image, toStream, quality); - } - else if (ImageFormat.Png.Equals(outputFormat)) - { - image.Save(toStream, ImageFormat.Png); - } - else - { - image.Save(toStream, outputFormat); - } - } - - /// - /// Saves the JPEG. - /// - /// The image. - /// The target. - /// The quality. - public static void SaveAsJpeg(this Image image, Stream target, int quality) - { - using (var encoderParameters = new EncoderParameters(1)) - { - encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality); - image.Save(target, GetImageCodecInfo("image/jpeg"), encoderParameters); - } - } - - private static readonly ImageCodecInfo[] Encoders = ImageCodecInfo.GetImageEncoders(); - - /// - /// Gets the image codec info. - /// - /// Type of the MIME. - /// ImageCodecInfo. - private static ImageCodecInfo GetImageCodecInfo(string mimeType) - { - foreach (var encoder in Encoders) - { - if (string.Equals(encoder.MimeType, mimeType, StringComparison.OrdinalIgnoreCase)) - { - return encoder; - } - } - - return Encoders.Length == 0 ? null : Encoders[0]; - } - - /// - /// Crops an image by removing whitespace and transparency from the edges - /// - /// The BMP. - /// Bitmap. - /// - public static Bitmap CropWhitespace(this Bitmap bmp) - { - var width = bmp.Width; - var height = bmp.Height; - - var topmost = 0; - for (int row = 0; row < height; ++row) - { - if (IsAllWhiteRow(bmp, row, width)) - topmost = row; - else break; - } - - int bottommost = 0; - for (int row = height - 1; row >= 0; --row) - { - if (IsAllWhiteRow(bmp, row, width)) - bottommost = row; - else break; - } - - int leftmost = 0, rightmost = 0; - for (int col = 0; col < width; ++col) - { - if (IsAllWhiteColumn(bmp, col, height)) - leftmost = col; - else - break; - } - - for (int col = width - 1; col >= 0; --col) - { - if (IsAllWhiteColumn(bmp, col, height)) - rightmost = col; - else - break; - } - - if (rightmost == 0) rightmost = width; // As reached left - if (bottommost == 0) bottommost = height; // As reached top. - - var croppedWidth = rightmost - leftmost; - var croppedHeight = bottommost - topmost; - - if (croppedWidth == 0) // No border on left or right - { - leftmost = 0; - croppedWidth = width; - } - - if (croppedHeight == 0) // No border on top or bottom - { - topmost = 0; - croppedHeight = height; - } - - // Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here - var thumbnail = new Bitmap(croppedWidth, croppedHeight, PixelFormat.Format32bppPArgb); - - // Preserve the original resolution - TrySetResolution(thumbnail, bmp.HorizontalResolution, bmp.VerticalResolution); - - using (var thumbnailGraph = Graphics.FromImage(thumbnail)) - { - thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality; - thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality; - thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic; - thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality; - thumbnailGraph.CompositingMode = CompositingMode.SourceCopy; - - thumbnailGraph.DrawImage(bmp, - new RectangleF(0, 0, croppedWidth, croppedHeight), - new RectangleF(leftmost, topmost, croppedWidth, croppedHeight), - GraphicsUnit.Pixel); - } - return thumbnail; - } - - /// - /// Tries the set resolution. - /// - /// The BMP. - /// The x. - /// The y. - private static void TrySetResolution(Bitmap bmp, float x, float y) - { - if (x > 0 && y > 0) - { - bmp.SetResolution(x, y); - } - } - - /// - /// Determines whether or not a row of pixels is all whitespace - /// - /// The BMP. - /// The row. - /// The width. - /// true if [is all white row] [the specified BMP]; otherwise, false. - private static bool IsAllWhiteRow(Bitmap bmp, int row, int width) - { - for (var i = 0; i < width; ++i) - { - if (!IsWhiteSpace(bmp.GetPixel(i, row))) - { - return false; - } - } - return true; - } - - /// - /// Determines whether or not a column of pixels is all whitespace - /// - /// The BMP. - /// The col. - /// The height. - /// true if [is all white column] [the specified BMP]; otherwise, false. - private static bool IsAllWhiteColumn(Bitmap bmp, int col, int height) - { - for (var i = 0; i < height; ++i) - { - if (!IsWhiteSpace(bmp.GetPixel(col, i))) - { - return false; - } - } - return true; - } - - /// - /// Determines if a color is whitespace - /// - /// The color. - /// true if [is white space] [the specified color]; otherwise, false. - private static bool IsWhiteSpace(Color color) - { - return (color.R == 255 && color.G == 255 && color.B == 255) || color.A == 0; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs index e9c67bf48a..81d4a786aa 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.IO; +using ImageMagickSharp; +using MediaBrowser.Common.IO; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Logging; using System; @@ -61,27 +62,15 @@ namespace MediaBrowser.Server.Implementations.Drawing logger.Info("Failed to read image header for {0}. Doing it the slow way.", path); } - // Buffer to memory stream to avoid image locking file - using (var fs = fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var wand = new MagickWand(path)) { - using (var memoryStream = new MemoryStream()) + var img = wand.CurrentImage; + + return new ImageSize { - fs.CopyTo(memoryStream); - - memoryStream.Position = 0; - - // Co it the old fashioned way - using (var b = System.Drawing.Image.FromStream(memoryStream, true, false)) - { - var size = b.Size; - - return new ImageSize - { - Width = size.Width, - Height = size.Height - }; - } - } + Width = img.Width, + Height = img.Height + }; } } diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 5055d2750f..e942b183bc 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -1,10 +1,9 @@ -using Imazen.WebP; +using ImageMagickSharp; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; @@ -13,9 +12,6 @@ using MediaBrowser.Model.Serialization; using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Linq; @@ -54,14 +50,12 @@ namespace MediaBrowser.Server.Implementations.Drawing private readonly IFileSystem _fileSystem; private readonly IJsonSerializer _jsonSerializer; private readonly IServerApplicationPaths _appPaths; - private readonly IMediaEncoder _mediaEncoder; - public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder) + public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer) { _logger = logger; _fileSystem = fileSystem; _jsonSerializer = jsonSerializer; - _mediaEncoder = mediaEncoder; _appPaths = appPaths; _saveImageSizeTimer = new Timer(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite); @@ -92,7 +86,7 @@ namespace MediaBrowser.Server.Implementations.Drawing _cachedImagedSizes = new ConcurrentDictionary(sizeDictionary); - LogWebPVersion(); + LogImageMagickVersionVersion(); } private string ResizedImageCachePath @@ -134,13 +128,9 @@ namespace MediaBrowser.Server.Implementations.Drawing } } - public Model.Drawing.ImageFormat[] GetSupportedImageOutputFormats() + public ImageFormat[] GetSupportedImageOutputFormats() { - if (_webpAvailable) - { - return new[] { Model.Drawing.ImageFormat.Webp, Model.Drawing.ImageFormat.Gif, Model.Drawing.ImageFormat.Jpg, Model.Drawing.ImageFormat.Png }; - } - return new[] { Model.Drawing.ImageFormat.Gif, Model.Drawing.ImageFormat.Jpg, Model.Drawing.ImageFormat.Png }; + return new[] { ImageFormat.Webp, ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png }; } public async Task ProcessImage(ImageProcessingOptions options) @@ -212,77 +202,42 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed > 0; + var newWidth = Convert.ToInt32(newSize.Width); + var newHeight = Convert.ToInt32(newSize.Height); - using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) + Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); + + if (string.IsNullOrWhiteSpace(options.BackgroundColor)) { - // Copy to memory stream to avoid Image locking file - using (var memoryStream = new MemoryStream()) + using (var originalImage = new MagickWand(originalImagePath)) { - await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false); + originalImage.CurrentImage.ResizeImage(newWidth, newHeight); - using (var originalImage = Image.FromStream(memoryStream, true, false)) + DrawIndicator(originalImage, newWidth, newHeight, options); + + originalImage.CurrentImage.CompressionQuality = quality; + + originalImage.SaveImage(cacheFilePath); + + return cacheFilePath; + } + } + else + { + using (var wand = new MagickWand(newWidth, newHeight, options.BackgroundColor)) + { + using (var originalImage = new MagickWand(originalImagePath)) { - var newWidth = Convert.ToInt32(newSize.Width); - var newHeight = Convert.ToInt32(newSize.Height); + originalImage.CurrentImage.ResizeImage(newWidth, newHeight); - var selectedOutputFormat = options.OutputFormat; + wand.CurrentImage.CompositeImage(originalImage, CompositeOperator.OverCompositeOp, 0, 0); + DrawIndicator(wand, newWidth, newHeight, options); - _logger.Debug("Processing image to {0}", selectedOutputFormat); + wand.CurrentImage.CompressionQuality = quality; - // 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 - var pixelFormat = selectedOutputFormat == Model.Drawing.ImageFormat.Webp - ? PixelFormat.Format32bppArgb - : PixelFormat.Format32bppPArgb; - - using (var thumbnail = new Bitmap(newWidth, newHeight, pixelFormat)) - { - // Mono throw an exeception if assign 0 to SetResolution - if (originalImage.HorizontalResolution > 0 && originalImage.VerticalResolution > 0) - { - // Preserve the original resolution - thumbnail.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution); - } - - using (var thumbnailGraph = Graphics.FromImage(thumbnail)) - { - thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality; - thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality; - thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic; - thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality; - thumbnailGraph.CompositingMode = !hasPostProcessing ? - CompositingMode.SourceCopy : - CompositingMode.SourceOver; - - SetBackgroundColor(thumbnailGraph, options); - - thumbnailGraph.DrawImage(originalImage, 0, 0, newWidth, newHeight); - - DrawIndicator(thumbnailGraph, newWidth, newHeight, options); - - var outputFormat = GetOutputFormat(originalImage, selectedOutputFormat); - - Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); - - // Save to the cache location - using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) - { - if (selectedOutputFormat == Model.Drawing.ImageFormat.Webp) - { - SaveToWebP(thumbnail, cacheFileStream, quality); - } - else - { - // Save to the memory stream - thumbnail.Save(outputFormat, cacheFileStream, quality); - } - } - - return cacheFilePath; - } - } + wand.SaveImage(cacheFilePath); + return cacheFilePath; } } } @@ -293,59 +248,26 @@ namespace MediaBrowser.Server.Implementations.Drawing } } - private void SaveToWebP(Bitmap thumbnail, Stream toStream, int quality) - { - new SimpleEncoder().Encode(thumbnail, toStream, quality); - } - - private bool _webpAvailable = true; - private void LogWebPVersion() + private void LogImageMagickVersionVersion() { try { - _logger.Info("libwebp version: " + SimpleEncoder.GetEncoderVersion()); + _logger.Info("ImageMagick version: " + Wand.VersionString); } catch (Exception ex) { - _logger.ErrorException("Error loading libwebp: ", ex); - _webpAvailable = false; - } - } - - /// - /// Sets the color of the background. - /// - /// The graphics. - /// The options. - private void SetBackgroundColor(Graphics graphics, ImageProcessingOptions options) - { - var color = options.BackgroundColor; - - if (!string.IsNullOrEmpty(color)) - { - Color drawingColor; - - try - { - drawingColor = ColorTranslator.FromHtml(color); - } - catch - { - drawingColor = ColorTranslator.FromHtml("#" + color); - } - - graphics.Clear(drawingColor); + _logger.ErrorException("Error loading ImageMagick: ", ex); } } /// /// Draws the indicator. /// - /// The graphics. + /// The wand. /// Width of the image. /// Height of the image. /// The options. - private void DrawIndicator(Graphics graphics, int imageWidth, int imageHeight, ImageProcessingOptions options) + private void DrawIndicator(MagickWand wand, int imageWidth, int imageHeight, ImageProcessingOptions options) { if (!options.AddPlayedIndicator && !options.UnplayedCount.HasValue && options.PercentPlayed.Equals(0)) { @@ -356,22 +278,20 @@ namespace MediaBrowser.Server.Implementations.Drawing { if (options.AddPlayedIndicator) { - var currentImageSize = new Size(imageWidth, imageHeight); + var currentImageSize = new ImageSize(imageWidth, imageHeight); - new PlayedIndicatorDrawer().DrawPlayedIndicator(graphics, currentImageSize); + new PlayedIndicatorDrawer().DrawPlayedIndicator(wand, currentImageSize); } else if (options.UnplayedCount.HasValue) { - var currentImageSize = new Size(imageWidth, imageHeight); + var currentImageSize = new ImageSize(imageWidth, imageHeight); - new UnplayedCountIndicator().DrawUnplayedCountIndicator(graphics, currentImageSize, options.UnplayedCount.Value); + new UnplayedCountIndicator().DrawUnplayedCountIndicator(wand, currentImageSize, options.UnplayedCount.Value); } if (options.PercentPlayed > 0) { - var currentImageSize = new Size(imageWidth, imageHeight); - - new PercentPlayedDrawer().Process(graphics, currentImageSize, options.PercentPlayed); + new PercentPlayedDrawer().Process(wand, options.PercentPlayed); } } catch (Exception ex) @@ -380,29 +300,6 @@ namespace MediaBrowser.Server.Implementations.Drawing } } - /// - /// Gets the output format. - /// - /// The image. - /// The output format. - /// ImageFormat. - private System.Drawing.Imaging.ImageFormat GetOutputFormat(Image image, Model.Drawing.ImageFormat outputFormat) - { - switch (outputFormat) - { - case Model.Drawing.ImageFormat.Bmp: - return System.Drawing.Imaging.ImageFormat.Bmp; - case Model.Drawing.ImageFormat.Gif: - return System.Drawing.Imaging.ImageFormat.Gif; - case Model.Drawing.ImageFormat.Jpg: - return System.Drawing.Imaging.ImageFormat.Jpeg; - case Model.Drawing.ImageFormat.Png: - return System.Drawing.Imaging.ImageFormat.Png; - default: - return image.RawFormat; - } - } - /// /// Crops whitespace from an image, caches the result, and returns the cached path /// @@ -429,28 +326,12 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) + Directory.CreateDirectory(Path.GetDirectoryName(croppedImagePath)); + + using (var wand = new MagickWand(originalImagePath)) { - // Copy to memory stream to avoid Image locking file - using (var memoryStream = new MemoryStream()) - { - await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false); - - using (var originalImage = (Bitmap)Image.FromStream(memoryStream, true, false)) - { - var outputFormat = originalImage.RawFormat; - - using (var croppedImage = originalImage.CropWhitespace()) - { - Directory.CreateDirectory(Path.GetDirectoryName(croppedImagePath)); - - using (var outputStream = _fileSystem.GetFileStream(croppedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) - { - croppedImage.Save(outputFormat, outputStream, 100); - } - } - } - } + wand.CurrentImage.TrimImage(10); + wand.SaveImage(croppedImagePath); } } catch (Exception ex) @@ -476,7 +357,7 @@ namespace MediaBrowser.Server.Implementations.Drawing /// /// Gets the cache file path based on a set of parameters /// - private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, Model.Drawing.ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor) + private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor) { var filename = originalPath; @@ -727,7 +608,7 @@ namespace MediaBrowser.Server.Implementations.Drawing } /// - /// Runs an image through the image enhancers, caches the result, and returns the cached path + /// Gets the enhanced image internal. /// /// The original image path. /// The item. @@ -735,8 +616,12 @@ namespace MediaBrowser.Server.Implementations.Drawing /// Index of the image. /// The supported enhancers. /// The cache unique identifier. - /// System.String. - /// originalImagePath + /// Task<System.String>. + /// + /// originalImagePath + /// or + /// item + /// private async Task GetEnhancedImageInternal(string originalImagePath, IHasImages item, ImageType imageType, @@ -770,51 +655,8 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) - { - // Copy to memory stream to avoid Image locking file - using (var memoryStream = new MemoryStream()) - { - await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false); - - memoryStream.Position = 0; - - var imageStream = new ImageStream - { - Stream = memoryStream, - Format = GetFormat(originalImagePath) - }; - - //Pass the image through registered enhancers - using (var newImageStream = await ExecuteImageEnhancers(supportedEnhancers, imageStream, item, imageType, imageIndex).ConfigureAwait(false)) - { - var parentDirectory = Path.GetDirectoryName(enhancedImagePath); - - Directory.CreateDirectory(parentDirectory); - - // Save as png - if (newImageStream.Format == Model.Drawing.ImageFormat.Png) - { - //And then save it in the cache - using (var outputStream = _fileSystem.GetFileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) - { - await newImageStream.Stream.CopyToAsync(outputStream).ConfigureAwait(false); - } - } - else - { - using (var newImage = Image.FromStream(newImageStream.Stream, true, false)) - { - //And then save it in the cache - using (var outputStream = _fileSystem.GetFileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) - { - newImage.Save(System.Drawing.Imaging.ImageFormat.Png, outputStream, 100); - } - } - } - } - } - } + Directory.CreateDirectory(Path.GetDirectoryName(enhancedImagePath)); + await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false); } finally { @@ -824,43 +666,42 @@ namespace MediaBrowser.Server.Implementations.Drawing return enhancedImagePath; } - private Model.Drawing.ImageFormat GetFormat(string path) + private ImageFormat GetFormat(string path) { var extension = Path.GetExtension(path); if (string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase)) { - return Model.Drawing.ImageFormat.Png; + return ImageFormat.Png; } if (string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase)) { - return Model.Drawing.ImageFormat.Gif; + return ImageFormat.Gif; } if (string.Equals(extension, ".webp", StringComparison.OrdinalIgnoreCase)) { - return Model.Drawing.ImageFormat.Webp; + return ImageFormat.Webp; } if (string.Equals(extension, ".bmp", StringComparison.OrdinalIgnoreCase)) { - return Model.Drawing.ImageFormat.Bmp; + return ImageFormat.Bmp; } - return Model.Drawing.ImageFormat.Jpg; + return ImageFormat.Jpg; } /// /// Executes the image enhancers. /// /// The image enhancers. - /// The original image. + /// The input path. + /// The output path. /// The item. /// Type of the image. /// Index of the image. /// Task{EnhancedImage}. - private async Task ExecuteImageEnhancers(IEnumerable imageEnhancers, ImageStream originalImage, IHasImages item, ImageType imageType, int imageIndex) + private async Task ExecuteImageEnhancers(IEnumerable imageEnhancers, string inputPath, string outputPath, IHasImages item, ImageType imageType, int imageIndex) { - var result = originalImage; - // Run the enhancers sequentially in order of priority foreach (var enhancer in imageEnhancers) { @@ -868,7 +709,7 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - result = await enhancer.EnhanceImageAsync(item, result, imageType, imageIndex).ConfigureAwait(false); + await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false); } catch (Exception ex) { @@ -876,9 +717,10 @@ namespace MediaBrowser.Server.Implementations.Drawing throw; } - } - return result; + // Feed the output into the next enhancer as input + inputPath = outputPath; + } } /// diff --git a/MediaBrowser.Server.Implementations/Drawing/PercentPlayedDrawer.cs b/MediaBrowser.Server.Implementations/Drawing/PercentPlayedDrawer.cs index 68f66e9772..20c2ab93be 100644 --- a/MediaBrowser.Server.Implementations/Drawing/PercentPlayedDrawer.cs +++ b/MediaBrowser.Server.Implementations/Drawing/PercentPlayedDrawer.cs @@ -1,5 +1,5 @@ -using System; -using System.Drawing; +using ImageMagickSharp; +using System; namespace MediaBrowser.Server.Implementations.Drawing { @@ -7,26 +7,32 @@ namespace MediaBrowser.Server.Implementations.Drawing { private const int IndicatorHeight = 8; - public void Process(Graphics graphics, Size imageSize, double percent) + public void Process(MagickWand wand, double percent) { - var y = imageSize.Height - IndicatorHeight; + var currentImage = wand.CurrentImage; + var height = currentImage.Height; - using (var backdroundBrush = new SolidBrush(Color.FromArgb(225, 0, 0, 0))) + using (var draw = new DrawingWand()) { - const int innerX = 0; - var innerY = y; - var innerWidth = imageSize.Width; - var innerHeight = imageSize.Height; - - graphics.FillRectangle(backdroundBrush, innerX, innerY, innerWidth, innerHeight); - - using (var foregroundBrush = new SolidBrush(Color.FromArgb(82, 181, 75))) + using (PixelWand pixel = new PixelWand()) { - double foregroundWidth = innerWidth; + var endX = currentImage.Width - 1; + var endY = height - 1; + + pixel.Color = "black"; + pixel.Opacity = 0.4; + draw.FillColor = pixel; + draw.DrawRectangle(0, endY - IndicatorHeight, endX, endY); + + double foregroundWidth = endX; foregroundWidth *= percent; foregroundWidth /= 100; - graphics.FillRectangle(foregroundBrush, innerX, innerY, Convert.ToInt32(Math.Round(foregroundWidth)), innerHeight); + pixel.Color = "#52B54B"; + pixel.Opacity = 0; + draw.FillColor = pixel; + draw.DrawRectangle(0, endY - IndicatorHeight, Convert.ToInt32(Math.Round(foregroundWidth)), endY); + wand.CurrentImage.DrawImage(draw); } } } diff --git a/MediaBrowser.Server.Implementations/Drawing/PlayedIndicatorDrawer.cs b/MediaBrowser.Server.Implementations/Drawing/PlayedIndicatorDrawer.cs index fa4231612b..6dd0b0fe76 100644 --- a/MediaBrowser.Server.Implementations/Drawing/PlayedIndicatorDrawer.cs +++ b/MediaBrowser.Server.Implementations/Drawing/PlayedIndicatorDrawer.cs @@ -1,31 +1,41 @@ -using System.Drawing; +using ImageMagickSharp; +using MediaBrowser.Model.Drawing; namespace MediaBrowser.Server.Implementations.Drawing { public class PlayedIndicatorDrawer { - private const int IndicatorHeight = 40; - public const int IndicatorWidth = 40; - private const int FontSize = 40; - private const int OffsetFromTopRightCorner = 10; + private const int FontSize = 52; + private const int OffsetFromTopRightCorner = 38; - public void DrawPlayedIndicator(Graphics graphics, Size imageSize) + public void DrawPlayedIndicator(MagickWand wand, ImageSize imageSize) { - var x = imageSize.Width - IndicatorWidth - OffsetFromTopRightCorner; + var x = imageSize.Width - OffsetFromTopRightCorner; - using (var backdroundBrush = new SolidBrush(Color.FromArgb(225, 82, 181, 75))) + using (var draw = new DrawingWand()) { - graphics.FillEllipse(backdroundBrush, x, OffsetFromTopRightCorner, IndicatorWidth, IndicatorHeight); - - x = imageSize.Width - 45 - OffsetFromTopRightCorner; - - using (var font = new Font("Webdings", FontSize, FontStyle.Regular, GraphicsUnit.Pixel)) + using (PixelWand pixel = new PixelWand()) { - using (var fontBrush = new SolidBrush(Color.White)) - { - graphics.DrawString("a", font, fontBrush, x, OffsetFromTopRightCorner - 2); - } + pixel.Color = "#52B54B"; + pixel.Opacity = 0.2; + draw.FillColor = pixel; + draw.DrawCircle(x, OffsetFromTopRightCorner, x - 20, OffsetFromTopRightCorner - 20); + + pixel.Opacity = 0; + pixel.Color = "white"; + draw.FillColor = pixel; + draw.Font = "Webdings"; + draw.FontSize = FontSize; + draw.FontStyle = FontStyleType.NormalStyle; + draw.TextAlignment = TextAlignType.CenterAlign; + draw.FontWeight = FontWeightType.RegularStyle; + draw.TextAntialias = true; + draw.DrawAnnotation(x + 4, OffsetFromTopRightCorner + 14, "a"); + + draw.FillColor = pixel; + wand.CurrentImage.DrawImage(draw); } + } } } diff --git a/MediaBrowser.Server.Implementations/Drawing/UnplayedCountIndicator.cs b/MediaBrowser.Server.Implementations/Drawing/UnplayedCountIndicator.cs index 695c6390a7..c10084c9b2 100644 --- a/MediaBrowser.Server.Implementations/Drawing/UnplayedCountIndicator.cs +++ b/MediaBrowser.Server.Implementations/Drawing/UnplayedCountIndicator.cs @@ -1,49 +1,61 @@ -using System.Drawing; +using System.Globalization; +using ImageMagickSharp; +using MediaBrowser.Model.Drawing; namespace MediaBrowser.Server.Implementations.Drawing { public class UnplayedCountIndicator { - private const int IndicatorHeight = 41; - public const int IndicatorWidth = 41; - private const int OffsetFromTopRightCorner = 10; + private const int OffsetFromTopRightCorner = 38; - public void DrawUnplayedCountIndicator(Graphics graphics, Size imageSize, int count) + public void DrawUnplayedCountIndicator(MagickWand wand, ImageSize imageSize, int count) { - var x = imageSize.Width - IndicatorWidth - OffsetFromTopRightCorner; + var x = imageSize.Width - OffsetFromTopRightCorner; + var text = count.ToString(CultureInfo.InvariantCulture); - using (var backdroundBrush = new SolidBrush(Color.FromArgb(225, 82, 181, 75))) + using (var draw = new DrawingWand()) { - graphics.FillEllipse(backdroundBrush, x, OffsetFromTopRightCorner, IndicatorWidth, IndicatorHeight); - - var text = count.ToString(); - - x = imageSize.Width - IndicatorWidth - OffsetFromTopRightCorner; - var y = OffsetFromTopRightCorner + 6; - var fontSize = 24; - - if (text.Length == 1) + using (PixelWand pixel = new PixelWand()) { - x += 10; - } - else if (text.Length == 2) - { - x += 3; - } - else if (text.Length == 3) - { - x += 1; - y += 1; - fontSize = 20; - } + pixel.Color = "#52B54B"; + pixel.Opacity = 0.2; + draw.FillColor = pixel; + draw.DrawCircle(x, OffsetFromTopRightCorner, x - 20, OffsetFromTopRightCorner - 20); - using (var font = new Font("Sans-Serif", fontSize, FontStyle.Regular, GraphicsUnit.Pixel)) - { - using (var fontBrush = new SolidBrush(Color.White)) + pixel.Opacity = 0; + pixel.Color = "white"; + draw.FillColor = pixel; + draw.Font = "Sans-Serif"; + draw.FontStyle = FontStyleType.NormalStyle; + draw.TextAlignment = TextAlignType.CenterAlign; + draw.FontWeight = FontWeightType.RegularStyle; + draw.TextAntialias = true; + + var fontSize = 30; + var y = OffsetFromTopRightCorner + 11; + + if (text.Length == 1) { - graphics.DrawString(text, font, fontBrush, x, y); + x += 2; } + else if (text.Length == 2) + { + x += 1; + } + else if (text.Length >= 3) + { + x += 1; + y -= 2; + fontSize = 24; + } + + draw.FontSize = fontSize; + draw.DrawAnnotation(x, y, text); + + draw.FillColor = pixel; + wand.CurrentImage.DrawImage(draw); } + } } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 1db1f8f5b4..eb11355f71 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -45,9 +45,8 @@ v4.5 - - False - ..\ThirdParty\libwebp\Imazen.WebP.dll + + ..\packages\ImageMagickSharp.1.0.0.0\lib\net45\ImageMagickSharp.dll False @@ -77,9 +76,9 @@ ..\ThirdParty\System.Data.SQLite.ManagedOnly\1.0.94.0\System.Data.SQLite.dll - + @@ -126,7 +125,6 @@ - diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 55023c5650..d4aedc7704 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,5 +1,6 @@  +