mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-04 03:27:21 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			248 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			248 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System.Threading.Tasks;
 | 
						|
using ImageMagickSharp;
 | 
						|
using MediaBrowser.Common.Configuration;
 | 
						|
using MediaBrowser.Common.Net;
 | 
						|
using MediaBrowser.Controller.Drawing;
 | 
						|
using MediaBrowser.Model.Drawing;
 | 
						|
using MediaBrowser.Model.Logging;
 | 
						|
using System;
 | 
						|
using System.IO;
 | 
						|
using System.Linq;
 | 
						|
using MediaBrowser.Common.IO;
 | 
						|
 | 
						|
namespace Emby.Drawing.ImageMagick
 | 
						|
{
 | 
						|
    public class ImageMagickEncoder : IImageEncoder
 | 
						|
    {
 | 
						|
        private readonly ILogger _logger;
 | 
						|
        private readonly IApplicationPaths _appPaths;
 | 
						|
		private readonly IHttpClient _httpClient;
 | 
						|
		private readonly IFileSystem _fileSystem;
 | 
						|
 | 
						|
        public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IFileSystem fileSystem)
 | 
						|
        {
 | 
						|
            _logger = logger;
 | 
						|
            _appPaths = appPaths;
 | 
						|
            _httpClient = httpClient;
 | 
						|
			_fileSystem = fileSystem;
 | 
						|
 | 
						|
            LogImageMagickVersion();
 | 
						|
        }
 | 
						|
 | 
						|
        public string[] SupportedInputFormats
 | 
						|
        {
 | 
						|
            get
 | 
						|
            {
 | 
						|
                // Some common file name extensions for RAW picture files include: .cr2, .crw, .dng, .nef, .orf, .rw2, .pef, .arw, .sr2, .srf, and .tif.
 | 
						|
                return new[]
 | 
						|
                {
 | 
						|
                    "tiff", 
 | 
						|
                    "jpeg", 
 | 
						|
                    "jpg", 
 | 
						|
                    "png", 
 | 
						|
                    "aiff", 
 | 
						|
                    "cr2", 
 | 
						|
                    "crw", 
 | 
						|
                    "dng", 
 | 
						|
                    "nef", 
 | 
						|
                    "orf", 
 | 
						|
                    "pef", 
 | 
						|
                    "arw", 
 | 
						|
                    "webp",
 | 
						|
                    "gif",
 | 
						|
                    "bmp"
 | 
						|
                };
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public ImageFormat[] SupportedOutputFormats
 | 
						|
        {
 | 
						|
            get
 | 
						|
            {
 | 
						|
                if (_webpAvailable)
 | 
						|
                {
 | 
						|
                    return new[] { ImageFormat.Webp, ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png };
 | 
						|
                }
 | 
						|
                return new[] { ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png };
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private void LogImageMagickVersion()
 | 
						|
        {
 | 
						|
            _logger.Info("ImageMagick version: " + Wand.VersionString);
 | 
						|
            TestWebp();
 | 
						|
            Wand.SetMagickThreadCount(1);
 | 
						|
        }
 | 
						|
 | 
						|
        private bool _webpAvailable = true;
 | 
						|
        private void TestWebp()
 | 
						|
        {
 | 
						|
            try
 | 
						|
            {
 | 
						|
                var tmpPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".webp");
 | 
						|
				_fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath));
 | 
						|
 | 
						|
                using (var wand = new MagickWand(1, 1, new PixelWand("none", 1)))
 | 
						|
                {
 | 
						|
                    wand.SaveImage(tmpPath);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.ErrorException("Error loading webp: ", ex);
 | 
						|
                _webpAvailable = false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public void CropWhiteSpace(string inputPath, string outputPath)
 | 
						|
        {
 | 
						|
            CheckDisposed();
 | 
						|
 | 
						|
            using (var wand = new MagickWand(inputPath))
 | 
						|
            {
 | 
						|
                wand.CurrentImage.TrimImage(10);
 | 
						|
                wand.SaveImage(outputPath);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public ImageSize GetImageSize(string path)
 | 
						|
        {
 | 
						|
            CheckDisposed();
 | 
						|
 | 
						|
            using (var wand = new MagickWand())
 | 
						|
            {
 | 
						|
                wand.PingImage(path);
 | 
						|
                var img = wand.CurrentImage;
 | 
						|
 | 
						|
                return new ImageSize
 | 
						|
                {
 | 
						|
                    Width = img.Width,
 | 
						|
                    Height = img.Height
 | 
						|
                };
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private bool HasTransparency(string path)
 | 
						|
        {
 | 
						|
            var ext = Path.GetExtension(path);
 | 
						|
 | 
						|
            return string.Equals(ext, ".png", StringComparison.OrdinalIgnoreCase) ||
 | 
						|
                string.Equals(ext, ".webp", StringComparison.OrdinalIgnoreCase);
 | 
						|
        }
 | 
						|
 | 
						|
        public void EncodeImage(string inputPath, string outputPath, int width, int height, int quality, ImageProcessingOptions options)
 | 
						|
        {
 | 
						|
            if (string.IsNullOrWhiteSpace(options.BackgroundColor) || !HasTransparency(inputPath))
 | 
						|
            {
 | 
						|
                using (var originalImage = new MagickWand(inputPath))
 | 
						|
                {
 | 
						|
                    originalImage.CurrentImage.ResizeImage(width, height);
 | 
						|
 | 
						|
                    DrawIndicator(originalImage, width, height, options);
 | 
						|
 | 
						|
                    originalImage.CurrentImage.CompressionQuality = quality;
 | 
						|
 | 
						|
                    originalImage.SaveImage(outputPath);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                using (var wand = new MagickWand(width, height, options.BackgroundColor))
 | 
						|
                {
 | 
						|
                    using (var originalImage = new MagickWand(inputPath))
 | 
						|
                    {
 | 
						|
                        originalImage.CurrentImage.ResizeImage(width, height);
 | 
						|
 | 
						|
                        wand.CurrentImage.CompositeImage(originalImage, CompositeOperator.OverCompositeOp, 0, 0);
 | 
						|
                        DrawIndicator(wand, width, height, options);
 | 
						|
 | 
						|
                        wand.CurrentImage.CompressionQuality = quality;
 | 
						|
 | 
						|
                        wand.SaveImage(outputPath);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Draws the indicator.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="wand">The wand.</param>
 | 
						|
        /// <param name="imageWidth">Width of the image.</param>
 | 
						|
        /// <param name="imageHeight">Height of the image.</param>
 | 
						|
        /// <param name="options">The options.</param>
 | 
						|
        private void DrawIndicator(MagickWand wand, int imageWidth, int imageHeight, ImageProcessingOptions options)
 | 
						|
        {
 | 
						|
            if (!options.AddPlayedIndicator && !options.UnplayedCount.HasValue && options.PercentPlayed.Equals(0))
 | 
						|
            {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                if (options.AddPlayedIndicator)
 | 
						|
                {
 | 
						|
                    var currentImageSize = new ImageSize(imageWidth, imageHeight);
 | 
						|
 | 
						|
					var task = new PlayedIndicatorDrawer(_appPaths, _httpClient, _fileSystem).DrawPlayedIndicator(wand, currentImageSize);
 | 
						|
                    Task.WaitAll(task);
 | 
						|
                }
 | 
						|
                else if (options.UnplayedCount.HasValue)
 | 
						|
                {
 | 
						|
                    var currentImageSize = new ImageSize(imageWidth, imageHeight);
 | 
						|
 | 
						|
                    new UnplayedCountIndicator(_appPaths, _fileSystem).DrawUnplayedCountIndicator(wand, currentImageSize, options.UnplayedCount.Value);
 | 
						|
                }
 | 
						|
 | 
						|
                if (options.PercentPlayed > 0)
 | 
						|
                {
 | 
						|
                    new PercentPlayedDrawer().Process(wand, options.PercentPlayed);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _logger.ErrorException("Error drawing indicator overlay", ex);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public void CreateImageCollage(ImageCollageOptions options)
 | 
						|
        {
 | 
						|
            double ratio = options.Width;
 | 
						|
            ratio /= options.Height;
 | 
						|
 | 
						|
            if (ratio >= 1.4)
 | 
						|
            {
 | 
						|
                new StripCollageBuilder(_appPaths, _fileSystem).BuildThumbCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text);
 | 
						|
            }
 | 
						|
            else if (ratio >= .9)
 | 
						|
            {
 | 
						|
                new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                new StripCollageBuilder(_appPaths, _fileSystem).BuildPosterCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public string Name
 | 
						|
        {
 | 
						|
            get { return "ImageMagick"; }
 | 
						|
        }
 | 
						|
 | 
						|
        private bool _disposed;
 | 
						|
        public void Dispose()
 | 
						|
        {
 | 
						|
            _disposed = true;
 | 
						|
            Wand.CloseEnvironment();
 | 
						|
        }
 | 
						|
 | 
						|
        private void CheckDisposed()
 | 
						|
        {
 | 
						|
            if (_disposed)
 | 
						|
            {
 | 
						|
                throw new ObjectDisposedException(GetType().Name);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |