diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
index 988ac364ae..febb1adabc 100644
--- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
+++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
@@ -4,6 +4,7 @@
netstandard2.1
false
true
+ true
@@ -22,4 +23,16 @@
+
+
+
+
+
+
+
+
+
+ ../jellyfin.ruleset
+
+
diff --git a/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs b/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs
index c72f295fdd..f2df066ec8 100644
--- a/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs
+++ b/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs
@@ -4,10 +4,19 @@ using SkiaSharp;
namespace Jellyfin.Drawing.Skia
{
+ ///
+ /// Static helper class used to draw percentage-played indicators on images.
+ ///
public static class PercentPlayedDrawer
{
private const int IndicatorHeight = 8;
+ ///
+ /// Draw a percentage played indicator on a canvas.
+ ///
+ /// The canvas to draw the indicator on.
+ /// The size of the image being drawn on.
+ /// The percentage played to display with the indicator.
public static void Process(SKCanvas canvas, ImageDimensions imageSize, double percent)
{
using (var paint = new SKPaint())
diff --git a/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs b/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
index 7f3c18bb24..5084fd211c 100644
--- a/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
+++ b/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
@@ -3,10 +3,21 @@ using SkiaSharp;
namespace Jellyfin.Drawing.Skia
{
+ ///
+ /// Static helper class for drawing 'played' indicators.
+ ///
public static class PlayedIndicatorDrawer
{
private const int OffsetFromTopRightCorner = 38;
+ ///
+ /// Draw a 'played' indicator in the top right corner of a canvas.
+ ///
+ /// The canvas to draw the indicator on.
+ ///
+ /// The dimensions of the image to draw the indicator on. The width is used to determine the x-position of the
+ /// indicator.
+ ///
public static void DrawPlayedIndicator(SKCanvas canvas, ImageDimensions imageSize)
{
var x = imageSize.Width - OffsetFromTopRightCorner;
@@ -26,10 +37,10 @@ namespace Jellyfin.Drawing.Skia
paint.TextSize = 30;
paint.IsAntialias = true;
+ // or:
+ // var emojiChar = 0x1F680;
var text = "✔️";
var emojiChar = StringUtilities.GetUnicodeCharacterCode(text, SKTextEncoding.Utf32);
- // or:
- //var emojiChar = 0x1F680;
// ask the font manager for a font with that character
var fontManager = SKFontManager.Default;
diff --git a/Jellyfin.Drawing.Skia/SkiaCodecException.cs b/Jellyfin.Drawing.Skia/SkiaCodecException.cs
index f848636bcb..8158b846dd 100644
--- a/Jellyfin.Drawing.Skia/SkiaCodecException.cs
+++ b/Jellyfin.Drawing.Skia/SkiaCodecException.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using SkiaSharp;
@@ -8,16 +9,10 @@ namespace Jellyfin.Drawing.Skia
///
public class SkiaCodecException : SkiaException
{
- ///
- /// Returns the non-successfull codec result returned by Skia.
- ///
- /// The non-successfull codec result returned by Skia.
- public SKCodecResult CodecResult { get; }
-
///
/// Initializes a new instance of the class.
///
- /// The non-successfull codec result returned by Skia.
+ /// The non-successful codec result returned by Skia.
public SkiaCodecException(SKCodecResult result) : base()
{
CodecResult = result;
@@ -27,7 +22,7 @@ namespace Jellyfin.Drawing.Skia
/// Initializes a new instance of the class
/// with a specified error message.
///
- /// The non-successfull codec result returned by Skia.
+ /// The non-successful codec result returned by Skia.
/// The message that describes the error.
public SkiaCodecException(SKCodecResult result, string message)
: base(message)
@@ -35,6 +30,11 @@ namespace Jellyfin.Drawing.Skia
CodecResult = result;
}
+ ///
+ /// Gets the non-successful codec result returned by Skia.
+ ///
+ public SKCodecResult CodecResult { get; }
+
///
public override string ToString()
=> string.Format(
diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index 66b814f6eb..b080b3e6a5 100644
--- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs
+++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
@@ -13,6 +13,9 @@ using static Jellyfin.Drawing.Skia.SkiaHelper;
namespace Jellyfin.Drawing.Skia
{
+ ///
+ /// Image encoder that uses to manipulate images.
+ ///
public class SkiaEncoder : IImageEncoder
{
private readonly ILogger _logger;
@@ -22,6 +25,12 @@ namespace Jellyfin.Drawing.Skia
private static readonly HashSet _transparentImageTypes
= new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" };
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The application logger.
+ /// The application paths.
+ /// The application localization manager.
public SkiaEncoder(
ILogger logger,
IApplicationPaths appPaths,
@@ -32,12 +41,16 @@ namespace Jellyfin.Drawing.Skia
_localizationManager = localizationManager;
}
+ ///
public string Name => "Skia";
+ ///
public bool SupportsImageCollageCreation => true;
+ ///
public bool SupportsImageEncoding => true;
+ ///
public IReadOnlyCollection SupportedInputFormats =>
new HashSet(StringComparer.OrdinalIgnoreCase)
{
@@ -65,11 +78,12 @@ namespace Jellyfin.Drawing.Skia
"arw"
};
+ ///
public IReadOnlyCollection SupportedOutputFormats
=> new HashSet() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
///
- /// Test to determine if the native lib is available
+ /// Test to determine if the native lib is available.
///
public static void TestSkia()
{
@@ -80,6 +94,11 @@ namespace Jellyfin.Drawing.Skia
private static bool IsTransparent(SKColor color)
=> (color.Red == 255 && color.Green == 255 && color.Blue == 255) || color.Alpha == 0;
+ ///
+ /// Convert a to a .
+ ///
+ /// The format to convert.
+ /// The converted format.
public static SKEncodedImageFormat GetImageFormat(ImageFormat selectedFormat)
{
switch (selectedFormat)
@@ -186,6 +205,9 @@ namespace Jellyfin.Drawing.Skia
}
///
+ /// The path is null.
+ /// The path is not valid.
+ /// The file at the specified path could not be used to generate a codec.
public ImageDimensions GetImageSize(string path)
{
if (path == null)
@@ -269,6 +291,14 @@ namespace Jellyfin.Drawing.Skia
}
}
+ ///
+ /// Decode an image.
+ ///
+ /// The filepath of the image to decode.
+ /// Whether to force clean the bitmap.
+ /// The orientation of the image.
+ /// The detected origin of the image.
+ /// The resulting bitmap of the image.
internal SKBitmap Decode(string path, bool forceCleanBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin)
{
if (!File.Exists(path))
@@ -358,16 +388,6 @@ namespace Jellyfin.Drawing.Skia
private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin)
{
- //var transformations = {
- // 2: { rotate: 0, flip: true},
- // 3: { rotate: 180, flip: false},
- // 4: { rotate: 180, flip: true},
- // 5: { rotate: 90, flip: true},
- // 6: { rotate: 90, flip: false},
- // 7: { rotate: 270, flip: true},
- // 8: { rotate: 270, flip: false},
- //}
-
switch (origin)
{
case SKEncodedOrigin.TopRight:
@@ -497,6 +517,7 @@ namespace Jellyfin.Drawing.Skia
}
}
+ ///
public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
{
if (string.IsNullOrWhiteSpace(inputPath))
@@ -520,7 +541,7 @@ namespace Jellyfin.Drawing.Skia
{
if (bitmap == null)
{
- throw new ArgumentOutOfRangeException(string.Format("Skia unable to read image {0}", inputPath));
+ throw new ArgumentOutOfRangeException($"Skia unable to read image {inputPath}");
}
var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height);
@@ -556,7 +577,7 @@ namespace Jellyfin.Drawing.Skia
}
// create bitmap to use for canvas drawing used to draw into bitmap
- using (var saveBitmap = new SKBitmap(width, height))//, bitmap.ColorType, bitmap.AlphaType))
+ using (var saveBitmap = new SKBitmap(width, height)) // , bitmap.ColorType, bitmap.AlphaType))
using (var canvas = new SKCanvas(saveBitmap))
{
// set background color if present
@@ -609,9 +630,11 @@ namespace Jellyfin.Drawing.Skia
}
}
}
+
return outputPath;
}
+ ///
public void CreateImageCollage(ImageCollageOptions options)
{
double ratio = (double)options.Width / options.Height;
diff --git a/Jellyfin.Drawing.Skia/SkiaException.cs b/Jellyfin.Drawing.Skia/SkiaException.cs
index 7aeaf083e2..968d3a2448 100644
--- a/Jellyfin.Drawing.Skia/SkiaException.cs
+++ b/Jellyfin.Drawing.Skia/SkiaException.cs
@@ -7,17 +7,30 @@ namespace Jellyfin.Drawing.Skia
///
public class SkiaException : Exception
{
- ///
+ ///
+ /// Initializes a new instance of the class.
+ ///
public SkiaException() : base()
{
}
- ///
+ ///
+ /// Initializes a new instance of the class with a specified error message.
+ ///
+ /// The message that describes the error.
public SkiaException(string message) : base(message)
{
}
- ///
+ ///
+ /// Initializes a new instance of the class with a specified error message and a
+ /// reference to the inner exception that is the cause of this exception.
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if
+ /// no inner exception is specified.
+ ///
public SkiaException(string message, Exception innerException)
: base(message, innerException)
{
diff --git a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
index 1f2a6e81a4..0735ef194a 100644
--- a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
+++ b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
@@ -5,15 +5,27 @@ using SkiaSharp;
namespace Jellyfin.Drawing.Skia
{
+ ///
+ /// Used to build collages of multiple images arranged in vertical strips.
+ ///
public class StripCollageBuilder
{
private readonly SkiaEncoder _skiaEncoder;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The encoder to use for building collages.
public StripCollageBuilder(SkiaEncoder skiaEncoder)
{
_skiaEncoder = skiaEncoder;
}
+ ///
+ /// Check which format an image has been encoded with using its filename extension.
+ ///
+ /// The path to the image to get the format for.
+ /// The image format.
public static SKEncodedImageFormat GetEncodedFormat(string outputPath)
{
if (outputPath == null)
@@ -48,6 +60,13 @@ namespace Jellyfin.Drawing.Skia
return SKEncodedImageFormat.Png;
}
+ ///
+ /// Create a square collage.
+ ///
+ /// The paths of the images to use in the collage.
+ /// The path at which to place the resulting collage image.
+ /// The desired width of the collage.
+ /// The desired height of the collage.
public void BuildSquareCollage(string[] paths, string outputPath, int width, int height)
{
using (var bitmap = BuildSquareCollageBitmap(paths, width, height))
@@ -58,6 +77,13 @@ namespace Jellyfin.Drawing.Skia
}
}
+ ///
+ /// Create a thumb collage.
+ ///
+ /// The paths of the images to use in the collage.
+ /// The path at which to place the resulting image.
+ /// The desired width of the collage.
+ /// The desired height of the collage.
public void BuildThumbCollage(string[] paths, string outputPath, int width, int height)
{
using (var bitmap = BuildThumbCollageBitmap(paths, width, height))
@@ -98,6 +124,7 @@ namespace Jellyfin.Drawing.Skia
using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
{
currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
+
// crop image
int ix = (int)Math.Abs((iWidth - iSlice) / 2);
using (var image = SKImage.FromBitmap(resizeBitmap))
diff --git a/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs b/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs
index dbf935f4e7..a10fff9dfe 100644
--- a/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs
+++ b/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs
@@ -4,10 +4,25 @@ using SkiaSharp;
namespace Jellyfin.Drawing.Skia
{
+ ///
+ /// Static helper class for drawing unplayed count indicators.
+ ///
public static class UnplayedCountIndicator
{
+ ///
+ /// The x-offset used when drawing an unplayed count indicator.
+ ///
private const int OffsetFromTopRightCorner = 38;
+ ///
+ /// Draw an unplayed count indicator in the top right corner of a canvas.
+ ///
+ /// The canvas to draw the indicator on.
+ ///
+ /// The dimensions of the image to draw the indicator on. The width is used to determine the x-position of the
+ /// indicator.
+ ///
+ /// The number to draw in the indicator.
public static void DrawUnplayedCountIndicator(SKCanvas canvas, ImageDimensions imageSize, int count)
{
var x = imageSize.Width - OffsetFromTopRightCorner;
@@ -19,6 +34,7 @@ namespace Jellyfin.Drawing.Skia
paint.Style = SKPaintStyle.Fill;
canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
}
+
using (var paint = new SKPaint())
{
paint.Color = new SKColor(255, 255, 255, 255);
@@ -33,6 +49,7 @@ namespace Jellyfin.Drawing.Skia
{
x -= 7;
}
+
if (text.Length == 2)
{
x -= 13;
diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
index a0f9ae46e4..88e67b6486 100644
--- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs
+++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
@@ -11,6 +11,7 @@ namespace MediaBrowser.Controller.Drawing
///
/// The supported input formats.
IReadOnlyCollection SupportedInputFormats { get; }
+
///
/// Gets the supported output formats.
///
@@ -18,9 +19,9 @@ namespace MediaBrowser.Controller.Drawing
IReadOnlyCollection SupportedOutputFormats { get; }
///
- /// Gets the name.
+ /// Gets the display name for the encoder.
///
- /// The name.
+ /// The display name.
string Name { get; }
///
@@ -35,17 +36,22 @@ namespace MediaBrowser.Controller.Drawing
/// true if [supports image encoding]; otherwise, false.
bool SupportsImageEncoding { get; }
+ ///
+ /// Get the dimensions of an image from the filesystem.
+ ///
+ /// The filepath of the image.
+ /// The image dimensions.
ImageDimensions GetImageSize(string path);
///
- /// Encodes the image.
+ /// Encode an image.
///
string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
///
- /// Creates the image collage.
+ /// Create an image collage.
///
- /// The options.
+ /// The options to use when creating the collage.
void CreateImageCollage(ImageCollageOptions options);
}
}
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index 75b5573b67..27d8a7cd92 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -31,6 +31,8 @@
+
+