diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs')
| -rw-r--r-- | MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs | 155 |
1 files changed, 110 insertions, 45 deletions
diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index d222c584e..967c78c50 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -78,6 +78,11 @@ namespace MediaBrowser.Server.Implementations.Drawing // No biggie sizeDictionary = new Dictionary<Guid, ImageSize>(); } + catch (DirectoryNotFoundException) + { + // No biggie + sizeDictionary = new Dictionary<Guid, ImageSize>(); + } catch (Exception ex) { logger.ErrorException("Error parsing image size cache file", ex); @@ -86,6 +91,8 @@ namespace MediaBrowser.Server.Implementations.Drawing } _cachedImagedSizes = new ConcurrentDictionary<Guid, ImageSize>(sizeDictionary); + + LogWebPVersion(); } private string ResizedImageCachePath @@ -127,6 +134,15 @@ namespace MediaBrowser.Server.Implementations.Drawing } } + public Model.Drawing.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 }; + } + public async Task<string> ProcessImage(ImageProcessingOptions options) { if (options == null) @@ -210,9 +226,13 @@ namespace MediaBrowser.Server.Implementations.Drawing var newWidth = Convert.ToInt32(newSize.Width); var newHeight = Convert.ToInt32(newSize.Height); + var selectedOutputFormat = options.OutputFormat; + + _logger.Debug("Processing image to {0}", selectedOutputFormat); + // 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 = options.OutputFormat == ImageOutputFormat.Webp + var pixelFormat = selectedOutputFormat == Model.Drawing.ImageFormat.Webp ? PixelFormat.Format32bppArgb : PixelFormat.Format32bppPArgb; @@ -241,16 +261,16 @@ namespace MediaBrowser.Server.Implementations.Drawing DrawIndicator(thumbnailGraph, newWidth, newHeight, options); - var outputFormat = GetOutputFormat(originalImage, options.OutputFormat); + 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 (options.OutputFormat == ImageOutputFormat.Webp) + if (selectedOutputFormat == Model.Drawing.ImageFormat.Webp) { - new SimpleEncoder().Encode(thumbnail, cacheFileStream, quality, false); + SaveToWebP(thumbnail, cacheFileStream, quality); } else { @@ -273,6 +293,25 @@ 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() + { + try + { + _logger.Info("libwebp version: " + SimpleEncoder.GetEncoderVersion()); + } + catch (Exception ex) + { + _logger.ErrorException("Error loading libwebp: ", ex); + _webpAvailable = false; + } + } + /// <summary> /// Sets the color of the background. /// </summary> @@ -347,17 +386,17 @@ namespace MediaBrowser.Server.Implementations.Drawing /// <param name="image">The image.</param> /// <param name="outputFormat">The output format.</param> /// <returns>ImageFormat.</returns> - private System.Drawing.Imaging.ImageFormat GetOutputFormat(Image image, ImageOutputFormat outputFormat) + private System.Drawing.Imaging.ImageFormat GetOutputFormat(Image image, Model.Drawing.ImageFormat outputFormat) { switch (outputFormat) { - case ImageOutputFormat.Bmp: + case Model.Drawing.ImageFormat.Bmp: return System.Drawing.Imaging.ImageFormat.Bmp; - case ImageOutputFormat.Gif: + case Model.Drawing.ImageFormat.Gif: return System.Drawing.Imaging.ImageFormat.Gif; - case ImageOutputFormat.Jpg: + case Model.Drawing.ImageFormat.Jpg: return System.Drawing.Imaging.ImageFormat.Jpeg; - case ImageOutputFormat.Png: + case Model.Drawing.ImageFormat.Png: return System.Drawing.Imaging.ImageFormat.Png; default: return image.RawFormat; @@ -437,7 +476,7 @@ namespace MediaBrowser.Server.Implementations.Drawing /// <summary> /// Gets the cache file path based on a set of parameters /// </summary> - private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageOutputFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor) + private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, Model.Drawing.ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor) { var filename = originalPath; @@ -449,10 +488,7 @@ namespace MediaBrowser.Server.Implementations.Drawing filename += "datemodified=" + dateModified.Ticks; - if (format != ImageOutputFormat.Original) - { - filename += "f=" + format; - } + filename += "f=" + format; var hasIndicator = false; @@ -484,7 +520,7 @@ namespace MediaBrowser.Server.Implementations.Drawing filename += "b=" + backgroundColor; } - return GetCachePath(ResizedImageCachePath, filename, Path.GetExtension(originalPath)); + return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower()); } /// <summary> @@ -658,15 +694,15 @@ namespace MediaBrowser.Server.Implementations.Drawing return result.Item1; } - private async Task<Tuple<string, DateTime>> GetEnhancedImage(ItemImageInfo image, - IHasImages item, + private async Task<Tuple<string, DateTime>> GetEnhancedImage(ItemImageInfo image, + IHasImages item, int imageIndex, List<IImageEnhancer> enhancers) { var originalImagePath = image.Path; var dateModified = image.DateModified; var imageType = image.Type; - + try { var cacheGuid = GetImageCacheTag(item, image, enhancers); @@ -701,10 +737,10 @@ namespace MediaBrowser.Server.Implementations.Drawing /// <param name="cacheGuid">The cache unique identifier.</param> /// <returns>System.String.</returns> /// <exception cref="System.ArgumentNullException">originalImagePath</exception> - private async Task<string> GetEnhancedImageInternal(string originalImagePath, - IHasImages item, - ImageType imageType, - int imageIndex, + private async Task<string> GetEnhancedImageInternal(string originalImagePath, + IHasImages item, + ImageType imageType, + int imageIndex, IEnumerable<IImageEnhancer> supportedEnhancers, string cacheGuid) { @@ -741,19 +777,39 @@ namespace MediaBrowser.Server.Implementations.Drawing { await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false); - using (var originalImage = Image.FromStream(memoryStream, true, false)) + memoryStream.Position = 0; + + var imageStream = new ImageStream { - //Pass the image through registered enhancers - using (var newImage = await ExecuteImageEnhancers(supportedEnhancers, originalImage, item, imageType, imageIndex).ConfigureAwait(false)) - { - var parentDirectory = Path.GetDirectoryName(enhancedImagePath); + 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); + 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)) { - newImage.Save(System.Drawing.Imaging.ImageFormat.Png, outputStream, 100); + 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); + } } } } @@ -768,6 +824,30 @@ namespace MediaBrowser.Server.Implementations.Drawing return enhancedImagePath; } + private Model.Drawing.ImageFormat GetFormat(string path) + { + var extension = Path.GetExtension(path); + + if (string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase)) + { + return Model.Drawing.ImageFormat.Png; + } + if (string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase)) + { + return Model.Drawing.ImageFormat.Gif; + } + if (string.Equals(extension, ".webp", StringComparison.OrdinalIgnoreCase)) + { + return Model.Drawing.ImageFormat.Webp; + } + if (string.Equals(extension, ".bmp", StringComparison.OrdinalIgnoreCase)) + { + return Model.Drawing.ImageFormat.Bmp; + } + + return Model.Drawing.ImageFormat.Jpg; + } + /// <summary> /// Executes the image enhancers. /// </summary> @@ -777,7 +857,7 @@ namespace MediaBrowser.Server.Implementations.Drawing /// <param name="imageType">Type of the image.</param> /// <param name="imageIndex">Index of the image.</param> /// <returns>Task{EnhancedImage}.</returns> - private async Task<Image> ExecuteImageEnhancers(IEnumerable<IImageEnhancer> imageEnhancers, Image originalImage, IHasImages item, ImageType imageType, int imageIndex) + private async Task<ImageStream> ExecuteImageEnhancers(IEnumerable<IImageEnhancer> imageEnhancers, ImageStream originalImage, IHasImages item, ImageType imageType, int imageIndex) { var result = originalImage; @@ -804,21 +884,6 @@ namespace MediaBrowser.Server.Implementations.Drawing /// <summary> /// The _semaphoreLocks /// </summary> - private readonly ConcurrentDictionary<string, object> _locks = new ConcurrentDictionary<string, object>(); - - /// <summary> - /// Gets the lock. - /// </summary> - /// <param name="filename">The filename.</param> - /// <returns>System.Object.</returns> - private object GetObjectLock(string filename) - { - return _locks.GetOrAdd(filename, key => new object()); - } - - /// <summary> - /// The _semaphoreLocks - /// </summary> private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks = new ConcurrentDictionary<string, SemaphoreSlim>(); /// <summary> |
