Sfoglia il codice sorgente

Merge pull request #2155 from mark-monteiro/2149-jellyfin-drawing-skia-warnings

Jellyfin.Drawing.Skia Warnings and Analyzers
Bond-009 5 anni fa
parent
commit
91562a4392

+ 13 - 0
Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj

@@ -4,6 +4,7 @@
     <TargetFramework>netstandard2.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>
 
   <ItemGroup>
@@ -22,4 +23,16 @@
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
   </ItemGroup>
 
+  <!-- Code analysers-->
+  <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" PrivateAssets="All" />
+    <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+    <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+    <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+  </ItemGroup>
+
+  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+    <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+
 </Project>

+ 9 - 0
Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs

@@ -4,10 +4,19 @@ using SkiaSharp;
 
 namespace Jellyfin.Drawing.Skia
 {
+    /// <summary>
+    /// Static helper class used to draw percentage-played indicators on images.
+    /// </summary>
     public static class PercentPlayedDrawer
     {
         private const int IndicatorHeight = 8;
 
+        /// <summary>
+        /// Draw a percentage played indicator on a canvas.
+        /// </summary>
+        /// <param name="canvas">The canvas to draw the indicator on.</param>
+        /// <param name="imageSize">The size of the image being drawn on.</param>
+        /// <param name="percent">The percentage played to display with the indicator.</param>
         public static void Process(SKCanvas canvas, ImageDimensions imageSize, double percent)
         {
             using (var paint = new SKPaint())

+ 13 - 2
Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs

@@ -3,10 +3,21 @@ using SkiaSharp;
 
 namespace Jellyfin.Drawing.Skia
 {
+    /// <summary>
+    /// Static helper class for drawing 'played' indicators.
+    /// </summary>
     public static class PlayedIndicatorDrawer
     {
         private const int OffsetFromTopRightCorner = 38;
 
+        /// <summary>
+        /// Draw a 'played' indicator in the top right corner of a canvas.
+        /// </summary>
+        /// <param name="canvas">The canvas to draw the indicator on.</param>
+        /// <param name="imageSize">
+        /// The dimensions of the image to draw the indicator on. The width is used to determine the x-position of the
+        /// indicator.
+        /// </param>
         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;

+ 8 - 8
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
     /// </summary>
     public class SkiaCodecException : SkiaException
     {
-        /// <summary>
-        /// Returns the non-successfull codec result returned by Skia.
-        /// </summary>
-        /// <value>The non-successfull codec result returned by Skia.</value>
-        public SKCodecResult CodecResult { get; }
-
         /// <summary>
         /// Initializes a new instance of the <see cref="SkiaCodecException" /> class.
         /// </summary>
-        /// <param name="result">The non-successfull codec result returned by Skia.</param>
+        /// <param name="result">The non-successful codec result returned by Skia.</param>
         public SkiaCodecException(SKCodecResult result) : base()
         {
             CodecResult = result;
@@ -27,7 +22,7 @@ namespace Jellyfin.Drawing.Skia
         /// Initializes a new instance of the <see cref="SkiaCodecException" /> class
         /// with a specified error message.
         /// </summary>
-        /// <param name="result">The non-successfull codec result returned by Skia.</param>
+        /// <param name="result">The non-successful codec result returned by Skia.</param>
         /// <param name="message">The message that describes the error.</param>
         public SkiaCodecException(SKCodecResult result, string message)
             : base(message)
@@ -35,6 +30,11 @@ namespace Jellyfin.Drawing.Skia
             CodecResult = result;
         }
 
+        /// <summary>
+        /// Gets the non-successful codec result returned by Skia.
+        /// </summary>
+        public SKCodecResult CodecResult { get; }
+
         /// <inheritdoc />
         public override string ToString()
             => string.Format(

+ 36 - 13
Jellyfin.Drawing.Skia/SkiaEncoder.cs

@@ -13,6 +13,9 @@ using static Jellyfin.Drawing.Skia.SkiaHelper;
 
 namespace Jellyfin.Drawing.Skia
 {
+    /// <summary>
+    /// Image encoder that uses <see cref="SkiaSharp"/> to manipulate images.
+    /// </summary>
     public class SkiaEncoder : IImageEncoder
     {
         private readonly ILogger _logger;
@@ -22,6 +25,12 @@ namespace Jellyfin.Drawing.Skia
         private static readonly HashSet<string> _transparentImageTypes
             = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" };
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SkiaEncoder"/> class.
+        /// </summary>
+        /// <param name="logger">The application logger.</param>
+        /// <param name="appPaths">The application paths.</param>
+        /// <param name="localizationManager">The application localization manager.</param>
         public SkiaEncoder(
             ILogger<SkiaEncoder> logger,
             IApplicationPaths appPaths,
@@ -32,12 +41,16 @@ namespace Jellyfin.Drawing.Skia
             _localizationManager = localizationManager;
         }
 
+        /// <inheritdoc/>
         public string Name => "Skia";
 
+        /// <inheritdoc/>
         public bool SupportsImageCollageCreation => true;
 
+        /// <inheritdoc/>
         public bool SupportsImageEncoding => true;
 
+        /// <inheritdoc/>
         public IReadOnlyCollection<string> SupportedInputFormats =>
             new HashSet<string>(StringComparer.OrdinalIgnoreCase)
             {
@@ -65,11 +78,12 @@ namespace Jellyfin.Drawing.Skia
                 "arw"
             };
 
+        /// <inheritdoc/>
         public IReadOnlyCollection<ImageFormat> SupportedOutputFormats
             => new HashSet<ImageFormat>() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
 
         /// <summary>
-        /// Test to determine if the native lib is available
+        /// Test to determine if the native lib is available.
         /// </summary>
         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;
 
+        /// <summary>
+        /// Convert a <see cref="ImageFormat"/> to a <see cref="SKEncodedImageFormat"/>.
+        /// </summary>
+        /// <param name="selectedFormat">The format to convert.</param>
+        /// <returns>The converted format.</returns>
         public static SKEncodedImageFormat GetImageFormat(ImageFormat selectedFormat)
         {
             switch (selectedFormat)
@@ -186,6 +205,9 @@ namespace Jellyfin.Drawing.Skia
         }
 
         /// <inheritdoc />
+        /// <exception cref="ArgumentNullException">The path is null.</exception>
+        /// <exception cref="FileNotFoundException">The path is not valid.</exception>
+        /// <exception cref="SkiaCodecException">The file at the specified path could not be used to generate a codec.</exception>
         public ImageDimensions GetImageSize(string path)
         {
             if (path == null)
@@ -269,6 +291,14 @@ namespace Jellyfin.Drawing.Skia
             }
         }
 
+        /// <summary>
+        /// Decode an image.
+        /// </summary>
+        /// <param name="path">The filepath of the image to decode.</param>
+        /// <param name="forceCleanBitmap">Whether to force clean the bitmap.</param>
+        /// <param name="orientation">The orientation of the image.</param>
+        /// <param name="origin">The detected origin of the image.</param>
+        /// <returns>The resulting bitmap of the image.</returns>
         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
             }
         }
 
+        /// <inheritdoc/>
         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;
         }
 
+        /// <inheritdoc/>
         public void CreateImageCollage(ImageCollageOptions options)
         {
             double ratio = (double)options.Width / options.Height;

+ 16 - 3
Jellyfin.Drawing.Skia/SkiaException.cs

@@ -7,17 +7,30 @@ namespace Jellyfin.Drawing.Skia
     /// </summary>
     public class SkiaException : Exception
     {
-        /// <inheritdoc />
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SkiaException"/> class.
+        /// </summary>
         public SkiaException() : base()
         {
         }
 
-        /// <inheritdoc />
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SkiaException"/> class with a specified error message.
+        /// </summary>
+        /// <param name="message">The message that describes the error.</param>
         public SkiaException(string message) : base(message)
         {
         }
 
-        /// <inheritdoc />
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SkiaException"/> class with a specified error message and a
+        /// reference to the inner exception that is the cause of this exception.
+        /// </summary>
+        /// <param name="message">The error message that explains the reason for the exception.</param>
+        /// <param name="innerException">
+        /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if
+        /// no inner exception is specified.
+        /// </param>
         public SkiaException(string message, Exception innerException)
             : base(message, innerException)
         {

+ 27 - 0
Jellyfin.Drawing.Skia/StripCollageBuilder.cs

@@ -5,15 +5,27 @@ using SkiaSharp;
 
 namespace Jellyfin.Drawing.Skia
 {
+    /// <summary>
+    /// Used to build collages of multiple images arranged in vertical strips.
+    /// </summary>
     public class StripCollageBuilder
     {
         private readonly SkiaEncoder _skiaEncoder;
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="StripCollageBuilder"/> class.
+        /// </summary>
+        /// <param name="skiaEncoder">The encoder to use for building collages.</param>
         public StripCollageBuilder(SkiaEncoder skiaEncoder)
         {
             _skiaEncoder = skiaEncoder;
         }
 
+        /// <summary>
+        /// Check which format an image has been encoded with using its filename extension.
+        /// </summary>
+        /// <param name="outputPath">The path to the image to get the format for.</param>
+        /// <returns>The image format.</returns>
         public static SKEncodedImageFormat GetEncodedFormat(string outputPath)
         {
             if (outputPath == null)
@@ -48,6 +60,13 @@ namespace Jellyfin.Drawing.Skia
             return SKEncodedImageFormat.Png;
         }
 
+        /// <summary>
+        /// Create a square collage.
+        /// </summary>
+        /// <param name="paths">The paths of the images to use in the collage.</param>
+        /// <param name="outputPath">The path at which to place the resulting collage image.</param>
+        /// <param name="width">The desired width of the collage.</param>
+        /// <param name="height">The desired height of the collage.</param>
         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
             }
         }
 
+        /// <summary>
+        /// Create a thumb collage.
+        /// </summary>
+        /// <param name="paths">The paths of the images to use in the collage.</param>
+        /// <param name="outputPath">The path at which to place the resulting image.</param>
+        /// <param name="width">The desired width of the collage.</param>
+        /// <param name="height">The desired height of the collage.</param>
         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))

+ 17 - 0
Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs

@@ -4,10 +4,25 @@ using SkiaSharp;
 
 namespace Jellyfin.Drawing.Skia
 {
+    /// <summary>
+    /// Static helper class for drawing unplayed count indicators.
+    /// </summary>
     public static class UnplayedCountIndicator
     {
+        /// <summary>
+        /// The x-offset used when drawing an unplayed count indicator.
+        /// </summary>
         private const int OffsetFromTopRightCorner = 38;
 
+        /// <summary>
+        /// Draw an unplayed count indicator in the top right corner of a canvas.
+        /// </summary>
+        /// <param name="canvas">The canvas to draw the indicator on.</param>
+        /// <param name="imageSize">
+        /// The dimensions of the image to draw the indicator on. The width is used to determine the x-position of the
+        /// indicator.
+        /// </param>
+        /// <param name="count">The number to draw in the indicator.</param>
         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;

+ 11 - 5
MediaBrowser.Controller/Drawing/IImageEncoder.cs

@@ -11,6 +11,7 @@ namespace MediaBrowser.Controller.Drawing
         /// </summary>
         /// <value>The supported input formats.</value>
         IReadOnlyCollection<string> SupportedInputFormats { get; }
+
         /// <summary>
         /// Gets the supported output formats.
         /// </summary>
@@ -18,9 +19,9 @@ namespace MediaBrowser.Controller.Drawing
         IReadOnlyCollection<ImageFormat> SupportedOutputFormats { get; }
 
         /// <summary>
-        /// Gets the name.
+        /// Gets the display name for the encoder.
         /// </summary>
-        /// <value>The name.</value>
+        /// <value>The display name.</value>
         string Name { get; }
 
         /// <summary>
@@ -35,17 +36,22 @@ namespace MediaBrowser.Controller.Drawing
         /// <value><c>true</c> if [supports image encoding]; otherwise, <c>false</c>.</value>
         bool SupportsImageEncoding { get; }
 
+        /// <summary>
+        /// Get the dimensions of an image from the filesystem.
+        /// </summary>
+        /// <param name="path">The filepath of the image.</param>
+        /// <returns>The image dimensions.</returns>
         ImageDimensions GetImageSize(string path);
 
         /// <summary>
-        /// Encodes the image.
+        /// Encode an image.
         /// </summary>
         string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
 
         /// <summary>
-        /// Creates the image collage.
+        /// Create an image collage.
         /// </summary>
-        /// <param name="options">The options.</param>
+        /// <param name="options">The options to use when creating the collage.</param>
         void CreateImageCollage(ImageCollageOptions options);
     }
 }

+ 2 - 0
jellyfin.ruleset

@@ -31,6 +31,8 @@
   <Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design">
     <!-- disable warning CA1031: Do not catch general exception types -->
     <Rule Id="CA1031" Action="Info" />
+    <!-- disable warning CA1032: Implement standard exception constructors -->
+    <Rule Id="CA1032" Action="Info" />
     <!-- disable warning CA1062: Validate arguments of public methods -->
     <Rule Id="CA1062" Action="Info" />
     <!-- disable warning CA1720: Identifiers should not contain type names -->