Quellcode durchsuchen

Merge pull request #3772 from EraYaN/master

Updated SkiaSharp to 2.80.1 and replace resize code to fix bad quality
Joshua M. Boniface vor 4 Jahren
Ursprung
Commit
612d513d86

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

@@ -20,9 +20,8 @@
   <ItemGroup>
   <ItemGroup>
     <PackageReference Include="BlurHashSharp" Version="1.1.0" />
     <PackageReference Include="BlurHashSharp" Version="1.1.0" />
     <PackageReference Include="BlurHashSharp.SkiaSharp" 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>
 
 
   <ItemGroup>
   <ItemGroup>

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

@@ -395,6 +395,56 @@ namespace Jellyfin.Drawing.Skia
             return rotated;
             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/>
         /// <inheritdoc/>
         public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
         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 width = newImageSize.Width;
             var height = newImageSize.Height;
             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 all we're doing is resizing then we can stop now
             if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
             if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
@@ -446,7 +496,7 @@ namespace Jellyfin.Drawing.Skia
                 Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
                 Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
                 using var outputStream = new SKFileWStream(outputPath);
                 using var outputStream = new SKFileWStream(outputPath);
                 using var pixmap = new SKPixmap(new SKImageInfo(width, height), resizedBitmap.GetPixels());
                 using var pixmap = new SKPixmap(new SKImageInfo(width, height), resizedBitmap.GetPixels());
-                pixmap.Encode(outputStream, skiaOutputFormat, quality);
+                resizedBitmap.Encode(outputStream, skiaOutputFormat, quality);
                 return outputPath;
                 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
                 // resize to the same aspect as the original
                 int iWidth = Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
                 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
                 // crop image
                 int ix = Math.Abs((iWidth - iSlice) / 2);
                 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
                 // draw image onto canvas
-                canvas.DrawImage(subset ?? image, iSlice * i, 0);
+                canvas.DrawImage(subset ?? resizedImage, iSlice * i, 0);
             }
             }
 
 
             return bitmap;
             return bitmap;
@@ -177,9 +175,9 @@ namespace Jellyfin.Drawing.Skia
                         continue;
                         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
                     // draw this image into the strip at the next position
                     var xPos = x * cellWidth;
                     var xPos = x * cellWidth;