Prechádzať zdrojové kódy

Merge pull request #5556 from oddstr13/image-fill-resize

(cherry picked from commit 790f7430aa1218a240fdcc0d3b3f9226bf8b9774)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
Bond-009 4 rokov pred
rodič
commit
7d1d159b8a

+ 96 - 17
Emby.Drawing/ImageProcessor.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
+using System.Text;
 using System.Threading.Tasks;
 using Jellyfin.Data.Entities;
 using MediaBrowser.Common.Extensions;
@@ -171,11 +172,26 @@ namespace Emby.Drawing
                 return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
 
-            ImageDimensions newSize = ImageHelper.GetNewImageSize(options, null);
             int quality = options.Quality;
 
             ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
-            string cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer);
+            string cacheFilePath = GetCacheFilePath(
+                originalImagePath,
+                options.Width,
+                options.Height,
+                options.MaxWidth,
+                options.MaxHeight,
+                options.FillWidth,
+                options.FillHeight,
+                quality,
+                dateModified,
+                outputFormat,
+                options.AddPlayedIndicator,
+                options.PercentPlayed,
+                options.UnplayedCount,
+                options.Blur,
+                options.BackgroundColor,
+                options.ForegroundLayer);
 
             try
             {
@@ -246,48 +262,111 @@ namespace Emby.Drawing
         /// <summary>
         /// Gets the cache file path based on a set of parameters.
         /// </summary>
-        private string GetCacheFilePath(string originalPath, ImageDimensions outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer)
+        private string GetCacheFilePath(
+            string originalPath,
+            int? width,
+            int? height,
+            int? maxWidth,
+            int? maxHeight,
+            int? fillWidth,
+            int? fillHeight,
+            int quality,
+            DateTime dateModified,
+            ImageFormat format,
+            bool addPlayedIndicator,
+            double percentPlayed,
+            int? unwatchedCount,
+            int? blur,
+            string backgroundColor,
+            string foregroundLayer)
         {
-            var filename = originalPath
-                + "width=" + outputSize.Width
-                + "height=" + outputSize.Height
-                + "quality=" + quality
-                + "datemodified=" + dateModified.Ticks
-                + "f=" + format;
+            var filename = new StringBuilder(256);
+            filename.Append(originalPath);
+
+            filename.Append(",quality=");
+            filename.Append(quality);
+
+            filename.Append(",datemodified=");
+            filename.Append(dateModified.Ticks);
+
+            filename.Append(",f=");
+            filename.Append(format);
+
+            if (width.HasValue)
+            {
+                filename.Append(",width=");
+                filename.Append(width.Value);
+            }
+
+            if (height.HasValue)
+            {
+                filename.Append(",height=");
+                filename.Append(height.Value);
+            }
+
+            if (maxWidth.HasValue)
+            {
+                filename.Append(",maxwidth=");
+                filename.Append(maxWidth.Value);
+            }
+
+            if (maxHeight.HasValue)
+            {
+                filename.Append(",maxheight=");
+                filename.Append(maxHeight.Value);
+            }
+
+            if (fillWidth.HasValue)
+            {
+                filename.Append(",fillwidth=");
+                filename.Append(fillWidth.Value);
+            }
+
+            if (fillHeight.HasValue)
+            {
+                filename.Append(",fillheight=");
+                filename.Append(fillHeight.Value);
+            }
 
             if (addPlayedIndicator)
             {
-                filename += "pl=true";
+                filename.Append(",pl=true");
             }
 
             if (percentPlayed > 0)
             {
-                filename += "p=" + percentPlayed;
+                filename.Append(",p=");
+                filename.Append(percentPlayed);
             }
 
             if (unwatchedCount.HasValue)
             {
-                filename += "p=" + unwatchedCount.Value;
+                filename.Append(",p=");
+                filename.Append(unwatchedCount.Value);
             }
 
             if (blur.HasValue)
             {
-                filename += "blur=" + blur.Value;
+                filename.Append(",blur=");
+                filename.Append(blur.Value);
             }
 
             if (!string.IsNullOrEmpty(backgroundColor))
             {
-                filename += "b=" + backgroundColor;
+                filename.Append(",b=");
+                filename.Append(backgroundColor);
             }
 
             if (!string.IsNullOrEmpty(foregroundLayer))
             {
-                filename += "fl=" + foregroundLayer;
+                filename.Append(",fl=");
+                filename.Append(foregroundLayer);
             }
 
-            filename += "v=" + Version;
+            filename.Append(",v=");
+            filename.Append(Version);
 
-            return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLowerInvariant());
+            return GetCachePath(ResizedImageCachePath, filename.ToString(), "." + format.ToString().ToLowerInvariant());
         }
 
         /// <inheritdoc />

+ 96 - 4
Jellyfin.Api/Controllers/ImageController.cs

@@ -480,6 +480,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="tag">Optional. Supply the cache tag from the item object to receive strong caching headers.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="format">Optional. The <see cref="ImageFormat"/> of the returned image.</param>
@@ -509,6 +511,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] string? tag,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] ImageFormat? format,
@@ -539,6 +543,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -560,6 +566,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="tag">Optional. Supply the cache tag from the item object to receive strong caching headers.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="format">Optional. The <see cref="ImageFormat"/> of the returned image.</param>
@@ -589,6 +597,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] string? tag,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] ImageFormat? format,
@@ -618,6 +628,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -638,6 +650,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="tag">Optional. Supply the cache tag from the item object to receive strong caching headers.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="format">Determines the output format of the image - original,gif,jpg,png.</param>
@@ -667,6 +681,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromRoute, Required] string tag,
             [FromQuery] bool? cropWhitespace,
             [FromRoute, Required] ImageFormat format,
@@ -697,6 +713,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -721,6 +739,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -750,6 +770,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -776,6 +798,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -800,6 +824,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -829,6 +855,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -855,6 +883,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -880,6 +910,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -909,6 +941,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -934,6 +968,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -958,6 +994,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -987,6 +1025,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -1013,6 +1053,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -1038,6 +1080,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -1067,6 +1111,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -1092,6 +1138,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -1116,6 +1164,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -1145,6 +1195,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -1171,6 +1223,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -1196,6 +1250,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -1225,6 +1281,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -1250,6 +1308,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -1274,6 +1334,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -1303,6 +1365,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -1329,6 +1393,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -1354,6 +1420,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -1383,6 +1451,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -1408,6 +1478,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -1432,6 +1504,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -1461,6 +1535,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -1504,6 +1580,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -1530,6 +1608,8 @@ namespace Jellyfin.Api.Controllers
         /// <param name="width">The fixed image width to return.</param>
         /// <param name="height">The fixed image height to return.</param>
         /// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
+        /// <param name="fillWidth">Width of box to fill.</param>
+        /// <param name="fillHeight">Height of box to fill.</param>
         /// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
         /// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
         /// <param name="blur">Optional. Blur image.</param>
@@ -1559,6 +1639,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? width,
             [FromQuery] int? height,
             [FromQuery] int? quality,
+            [FromQuery] int? fillWidth,
+            [FromQuery] int? fillHeight,
             [FromQuery] bool? cropWhitespace,
             [FromQuery] bool? addPlayedIndicator,
             [FromQuery] int? blur,
@@ -1601,6 +1683,8 @@ namespace Jellyfin.Api.Controllers
                     width,
                     height,
                     quality,
+                    fillWidth,
+                    fillHeight,
                     cropWhitespace,
                     addPlayedIndicator,
                     blur,
@@ -1685,6 +1769,8 @@ namespace Jellyfin.Api.Controllers
             int? width,
             int? height,
             int? quality,
+            int? fillWidth,
+            int? fillHeight,
             bool? cropWhitespace,
             bool? addPlayedIndicator,
             int? blur,
@@ -1748,11 +1834,13 @@ namespace Jellyfin.Api.Controllers
                 item,
                 itemId,
                 imageIndex,
+                width,
                 height,
-                maxHeight,
                 maxWidth,
+                maxHeight,
+                fillWidth,
+                fillHeight,
                 quality,
-                width,
                 addPlayedIndicator,
                 percentPlayed,
                 unplayedCount,
@@ -1847,11 +1935,13 @@ namespace Jellyfin.Api.Controllers
             BaseItem? item,
             Guid itemId,
             int? index,
+            int? width,
             int? height,
-            int? maxHeight,
             int? maxWidth,
+            int? maxHeight,
+            int? fillWidth,
+            int? fillHeight,
             int? quality,
-            int? width,
             bool? addPlayedIndicator,
             double? percentPlayed,
             int? unplayedCount,
@@ -1880,6 +1970,8 @@ namespace Jellyfin.Api.Controllers
                 ItemId = itemId,
                 MaxHeight = maxHeight,
                 MaxWidth = maxWidth,
+                FillHeight = fillHeight,
+                FillWidth = fillWidth,
                 Quality = quality ?? 100,
                 Width = width,
                 AddPlayedIndicator = addPlayedIndicator ?? false,

+ 6 - 60
MediaBrowser.Controller/Drawing/ImageHelper.cs

@@ -1,4 +1,5 @@
 #pragma warning disable CS1591
+#nullable enable
 
 using System;
 using MediaBrowser.Controller.Entities;
@@ -9,67 +10,12 @@ namespace MediaBrowser.Controller.Drawing
 {
     public static class ImageHelper
     {
-        public static ImageDimensions GetNewImageSize(ImageProcessingOptions options, ImageDimensions? originalImageSize)
+        public static ImageDimensions GetNewImageSize(ImageProcessingOptions options, ImageDimensions originalImageSize)
         {
-            if (originalImageSize.HasValue)
-            {
-                // Determine the output size based on incoming parameters
-                var newSize = DrawingUtils.Resize(originalImageSize.Value, options.Width ?? 0, options.Height ?? 0, options.MaxWidth ?? 0, options.MaxHeight ?? 0);
-
-                return newSize;
-            }
-
-            return GetSizeEstimate(options);
-        }
-
-        private static ImageDimensions GetSizeEstimate(ImageProcessingOptions options)
-        {
-            if (options.Width.HasValue && options.Height.HasValue)
-            {
-                return new ImageDimensions(options.Width.Value, options.Height.Value);
-            }
-
-            double aspect = GetEstimatedAspectRatio(options.Image.Type, options.Item);
-
-            int? width = options.Width ?? options.MaxWidth;
-
-            if (width.HasValue)
-            {
-                int heightValue = Convert.ToInt32((double)width.Value / aspect);
-                return new ImageDimensions(width.Value, heightValue);
-            }
-
-            var height = options.Height ?? options.MaxHeight ?? 200;
-            int widthValue = Convert.ToInt32(aspect * height);
-            return new ImageDimensions(widthValue, height);
-        }
-
-        private static double GetEstimatedAspectRatio(ImageType type, BaseItem 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:
-                case ImageType.Profile:
-                    return 1;
-                case ImageType.Logo:
-                    return 2.58;
-                case ImageType.Primary:
-                    double defaultPrimaryImageAspectRatio = item.GetDefaultPrimaryImageAspectRatio();
-                    return defaultPrimaryImageAspectRatio > 0 ? defaultPrimaryImageAspectRatio : 2.0 / 3;
-                default:
-                    return 1;
-            }
+            // Determine the output size based on incoming parameters
+            var newSize = DrawingUtils.Resize(originalImageSize, options.Width ?? 0, options.Height ?? 0, options.MaxWidth ?? 0, options.MaxHeight ?? 0);
+            newSize = DrawingUtils.ResizeFill(newSize, options.FillWidth, options.FillHeight);
+            return newSize;
         }
     }
 }

+ 9 - 0
MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs

@@ -34,6 +34,10 @@ namespace MediaBrowser.Controller.Drawing
 
         public int? MaxHeight { get; set; }
 
+        public int? FillWidth { get; set; }
+
+        public int? FillHeight { get; set; }
+
         public int Quality { get; set; }
 
         public IReadOnlyCollection<ImageFormat> SupportedOutputFormats { get; set; }
@@ -95,6 +99,11 @@ namespace MediaBrowser.Controller.Drawing
                 return false;
             }
 
+            if (sizeValue.Width > FillWidth || sizeValue.Height > FillHeight)
+            {
+                return false;
+            }
+
             return true;
         }
 

+ 46 - 0
MediaBrowser.Model/Drawing/DrawingUtils.cs

@@ -57,6 +57,52 @@ namespace MediaBrowser.Model.Drawing
             return new ImageDimensions(newWidth, newHeight);
         }
 
+        /// <summary>
+        /// Scale down to fill box.
+        /// Returns original size if both width and height are null or zero.
+        /// </summary>
+        /// <param name="size">The original size object.</param>
+        /// <param name="fillWidth">A new fixed width, if desired.</param>
+        /// <param name="fillHeight">A new fixed height, if desired.</param>
+        /// <returns>A new size object or size.</returns>
+        public static ImageDimensions ResizeFill(
+            ImageDimensions size,
+            int? fillWidth,
+            int? fillHeight)
+        {
+            // Return original size if input is invalid.
+            if ((fillWidth == null || fillWidth == 0)
+                && (fillHeight == null || fillHeight == 0))
+            {
+                return size;
+            }
+
+            if (fillWidth == null || fillWidth == 0)
+            {
+                fillWidth = 1;
+            }
+
+            if (fillHeight == null || fillHeight == 0)
+            {
+                fillHeight = 1;
+            }
+
+            double widthRatio = size.Width / (double)fillWidth;
+            double heightRatio = size.Height / (double)fillHeight;
+            double scaleRatio = Math.Min(widthRatio, heightRatio);
+
+            // Clamp to current size.
+            if (scaleRatio < 1)
+            {
+                return size;
+            }
+
+            int newWidth = Convert.ToInt32(Math.Ceiling(size.Width / scaleRatio));
+            int newHeight = Convert.ToInt32(Math.Ceiling(size.Height / scaleRatio));
+
+            return new ImageDimensions(newWidth, newHeight);
+        }
+
         /// <summary>
         /// Gets the new width.
         /// </summary>