Selaa lähdekoodia

Merge pull request #3772 from EraYaN/master

Updated SkiaSharp to 2.80.1 and replace resize code to fix bad quality
Joshua M. Boniface 4 vuotta sitten
vanhempi
sitoutus
612d513d86

+ 2 - 3
Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj

@@ -20,9 +20,8 @@
   <ItemGroup>
     <PackageReference Include="BlurHashSharp" Version="1.1.0" />
     <PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.1.0" />
-    <PackageReference Include="SkiaSharp" Version="1.68.3" />
-    <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.3" />
-    <PackageReference Include="Jellyfin.SkiaSharp.NativeAssets.LinuxArm" Version="1.68.1" />
+    <PackageReference Include="SkiaSharp" Version="2.80.1" />
+    <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.80.1" />
   </ItemGroup>
 
   <ItemGroup>

+ 54 - 4
Jellyfin.Drawing.Skia/SkiaEncoder.cs

@@ -395,6 +395,56 @@ namespace Jellyfin.Drawing.Skia
             return rotated;
         }
 
+        /// <summary>
+        /// Resizes an image on the CPU, by utilizing a surface and canvas.
+        ///
+        /// The convolutional matrix kernel used in this resize function gives a (light) sharpening effect.
+        /// This technique is similar to effect that can be created using for example the [Convolution matrix filter in GIMP](https://docs.gimp.org/2.10/en/gimp-filter-convolution-matrix.html).
+        /// </summary>
+        /// <param name="source">The source bitmap.</param>
+        /// <param name="targetInfo">This specifies the target size and other information required to create the surface.</param>
+        /// <param name="isAntialias">This enables anti-aliasing on the SKPaint instance.</param>
+        /// <param name="isDither">This enables dithering on the SKPaint instance.</param>
+        /// <returns>The resized image.</returns>
+        internal static SKImage ResizeImage(SKBitmap source, SKImageInfo targetInfo, bool isAntialias = false, bool isDither = false)
+        {
+            using var surface = SKSurface.Create(targetInfo);
+            using var canvas = surface.Canvas;
+            using var paint = new SKPaint
+            {
+                FilterQuality = SKFilterQuality.High,
+                IsAntialias = isAntialias,
+                IsDither = isDither
+            };
+
+            var kernel = new float[9]
+            {
+                0,    -.1f,    0,
+                -.1f, 1.4f, -.1f,
+                0,    -.1f,    0,
+            };
+
+            var kernelSize = new SKSizeI(3, 3);
+            var kernelOffset = new SKPointI(1, 1);
+
+            paint.ImageFilter = SKImageFilter.CreateMatrixConvolution(
+                kernelSize,
+                kernel,
+                1f,
+                0f,
+                kernelOffset,
+                SKShaderTileMode.Clamp,
+                false);
+
+            canvas.DrawBitmap(
+                source,
+                SKRect.Create(0, 0, source.Width, source.Height),
+                SKRect.Create(0, 0, targetInfo.Width, targetInfo.Height),
+                paint);
+
+            return surface.Snapshot();
+        }
+
         /// <inheritdoc/>
         public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
         {
@@ -436,9 +486,9 @@ namespace Jellyfin.Drawing.Skia
             var width = newImageSize.Width;
             var height = newImageSize.Height;
 
-            using var resizedBitmap = new SKBitmap(width, height, bitmap.ColorType, bitmap.AlphaType);
-            // scale image
-            bitmap.ScalePixels(resizedBitmap, SKFilterQuality.High);
+            // scale image (the FromImage creates a copy)
+            var imageInfo = new SKImageInfo(width, height, bitmap.ColorType, bitmap.AlphaType, bitmap.ColorSpace);
+            using var resizedBitmap = SKBitmap.FromImage(ResizeImage(bitmap, imageInfo));
 
             // If all we're doing is resizing then we can stop now
             if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
@@ -446,7 +496,7 @@ namespace Jellyfin.Drawing.Skia
                 Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
                 using var outputStream = new SKFileWStream(outputPath);
                 using var pixmap = new SKPixmap(new SKImageInfo(width, height), resizedBitmap.GetPixels());
-                pixmap.Encode(outputStream, skiaOutputFormat, quality);
+                resizedBitmap.Encode(outputStream, skiaOutputFormat, quality);
                 return outputPath;
             }
 

+ 6 - 8
Jellyfin.Drawing.Skia/StripCollageBuilder.cs

@@ -115,15 +115,13 @@ namespace Jellyfin.Drawing.Skia
 
                 // resize to the same aspect as the original
                 int iWidth = Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
-                using var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType);
-                currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
+                using var resizedImage = SkiaEncoder.ResizeImage(bitmap, new SKImageInfo(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType, currentBitmap.ColorSpace));
 
                 // crop image
                 int ix = Math.Abs((iWidth - iSlice) / 2);
-                using var image = SKImage.FromBitmap(resizeBitmap);
-                using var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight));
+                using var subset = resizedImage.Subset(SKRectI.Create(ix, 0, iSlice, iHeight));
                 // draw image onto canvas
-                canvas.DrawImage(subset ?? image, iSlice * i, 0);
+                canvas.DrawImage(subset ?? resizedImage, iSlice * i, 0);
             }
 
             return bitmap;
@@ -177,9 +175,9 @@ namespace Jellyfin.Drawing.Skia
                         continue;
                     }
 
-                    using var resizedBitmap = new SKBitmap(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType);
-                    // scale image
-                    currentBitmap.ScalePixels(resizedBitmap, SKFilterQuality.High);
+                    // Scale image. The FromBitmap creates a copy
+                    var imageInfo = new SKImageInfo(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType, currentBitmap.ColorSpace);
+                    using var resizedBitmap = SKBitmap.FromImage(SkiaEncoder.ResizeImage(bitmap, imageInfo));
 
                     // draw this image into the strip at the next position
                     var xPos = x * cellWidth;