mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Merge pull request #2155 from mark-monteiro/2149-jellyfin-drawing-skia-warnings
Jellyfin.Drawing.Skia Warnings and Analyzers
This commit is contained in:
commit
91562a4392
@ -4,6 +4,7 @@
|
|||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -22,4 +23,16 @@
|
|||||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
|
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
|
||||||
</ItemGroup>
|
</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>
|
</Project>
|
||||||
|
@ -4,10 +4,19 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace Jellyfin.Drawing.Skia
|
namespace Jellyfin.Drawing.Skia
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Static helper class used to draw percentage-played indicators on images.
|
||||||
|
/// </summary>
|
||||||
public static class PercentPlayedDrawer
|
public static class PercentPlayedDrawer
|
||||||
{
|
{
|
||||||
private const int IndicatorHeight = 8;
|
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)
|
public static void Process(SKCanvas canvas, ImageDimensions imageSize, double percent)
|
||||||
{
|
{
|
||||||
using (var paint = new SKPaint())
|
using (var paint = new SKPaint())
|
||||||
|
@ -3,10 +3,21 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace Jellyfin.Drawing.Skia
|
namespace Jellyfin.Drawing.Skia
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Static helper class for drawing 'played' indicators.
|
||||||
|
/// </summary>
|
||||||
public static class PlayedIndicatorDrawer
|
public static class PlayedIndicatorDrawer
|
||||||
{
|
{
|
||||||
private const int OffsetFromTopRightCorner = 38;
|
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)
|
public static void DrawPlayedIndicator(SKCanvas canvas, ImageDimensions imageSize)
|
||||||
{
|
{
|
||||||
var x = imageSize.Width - OffsetFromTopRightCorner;
|
var x = imageSize.Width - OffsetFromTopRightCorner;
|
||||||
@ -26,10 +37,10 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
paint.TextSize = 30;
|
paint.TextSize = 30;
|
||||||
paint.IsAntialias = true;
|
paint.IsAntialias = true;
|
||||||
|
|
||||||
|
// or:
|
||||||
|
// var emojiChar = 0x1F680;
|
||||||
var text = "✔️";
|
var text = "✔️";
|
||||||
var emojiChar = StringUtilities.GetUnicodeCharacterCode(text, SKTextEncoding.Utf32);
|
var emojiChar = StringUtilities.GetUnicodeCharacterCode(text, SKTextEncoding.Utf32);
|
||||||
// or:
|
|
||||||
//var emojiChar = 0x1F680;
|
|
||||||
|
|
||||||
// ask the font manager for a font with that character
|
// ask the font manager for a font with that character
|
||||||
var fontManager = SKFontManager.Default;
|
var fontManager = SKFontManager.Default;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
@ -8,16 +9,10 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class SkiaCodecException : SkiaException
|
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>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="SkiaCodecException" /> class.
|
/// Initializes a new instance of the <see cref="SkiaCodecException" /> class.
|
||||||
/// </summary>
|
/// </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()
|
public SkiaCodecException(SKCodecResult result) : base()
|
||||||
{
|
{
|
||||||
CodecResult = result;
|
CodecResult = result;
|
||||||
@ -27,7 +22,7 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
/// Initializes a new instance of the <see cref="SkiaCodecException" /> class
|
/// Initializes a new instance of the <see cref="SkiaCodecException" /> class
|
||||||
/// with a specified error message.
|
/// with a specified error message.
|
||||||
/// </summary>
|
/// </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>
|
/// <param name="message">The message that describes the error.</param>
|
||||||
public SkiaCodecException(SKCodecResult result, string message)
|
public SkiaCodecException(SKCodecResult result, string message)
|
||||||
: base(message)
|
: base(message)
|
||||||
@ -35,6 +30,11 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
CodecResult = result;
|
CodecResult = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the non-successful codec result returned by Skia.
|
||||||
|
/// </summary>
|
||||||
|
public SKCodecResult CodecResult { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
=> string.Format(
|
=> string.Format(
|
||||||
|
@ -13,6 +13,9 @@ using static Jellyfin.Drawing.Skia.SkiaHelper;
|
|||||||
|
|
||||||
namespace Jellyfin.Drawing.Skia
|
namespace Jellyfin.Drawing.Skia
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Image encoder that uses <see cref="SkiaSharp"/> to manipulate images.
|
||||||
|
/// </summary>
|
||||||
public class SkiaEncoder : IImageEncoder
|
public class SkiaEncoder : IImageEncoder
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
@ -22,6 +25,12 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
private static readonly HashSet<string> _transparentImageTypes
|
private static readonly HashSet<string> _transparentImageTypes
|
||||||
= new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" };
|
= 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(
|
public SkiaEncoder(
|
||||||
ILogger<SkiaEncoder> logger,
|
ILogger<SkiaEncoder> logger,
|
||||||
IApplicationPaths appPaths,
|
IApplicationPaths appPaths,
|
||||||
@ -32,12 +41,16 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
_localizationManager = localizationManager;
|
_localizationManager = localizationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public string Name => "Skia";
|
public string Name => "Skia";
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public bool SupportsImageCollageCreation => true;
|
public bool SupportsImageCollageCreation => true;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public bool SupportsImageEncoding => true;
|
public bool SupportsImageEncoding => true;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public IReadOnlyCollection<string> SupportedInputFormats =>
|
public IReadOnlyCollection<string> SupportedInputFormats =>
|
||||||
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
@ -65,11 +78,12 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
"arw"
|
"arw"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public IReadOnlyCollection<ImageFormat> SupportedOutputFormats
|
public IReadOnlyCollection<ImageFormat> SupportedOutputFormats
|
||||||
=> new HashSet<ImageFormat>() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
|
=> new HashSet<ImageFormat>() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test to determine if the native lib is available
|
/// Test to determine if the native lib is available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void TestSkia()
|
public static void TestSkia()
|
||||||
{
|
{
|
||||||
@ -80,6 +94,11 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
private static bool IsTransparent(SKColor color)
|
private static bool IsTransparent(SKColor color)
|
||||||
=> (color.Red == 255 && color.Green == 255 && color.Blue == 255) || color.Alpha == 0;
|
=> (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)
|
public static SKEncodedImageFormat GetImageFormat(ImageFormat selectedFormat)
|
||||||
{
|
{
|
||||||
switch (selectedFormat)
|
switch (selectedFormat)
|
||||||
@ -186,6 +205,9 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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)
|
public ImageDimensions GetImageSize(string path)
|
||||||
{
|
{
|
||||||
if (path == null)
|
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)
|
internal SKBitmap Decode(string path, bool forceCleanBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin)
|
||||||
{
|
{
|
||||||
if (!File.Exists(path))
|
if (!File.Exists(path))
|
||||||
@ -358,16 +388,6 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
|
|
||||||
private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin)
|
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)
|
switch (origin)
|
||||||
{
|
{
|
||||||
case SKEncodedOrigin.TopRight:
|
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)
|
public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(inputPath))
|
if (string.IsNullOrWhiteSpace(inputPath))
|
||||||
@ -520,7 +541,7 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
{
|
{
|
||||||
if (bitmap == null)
|
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);
|
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
|
// 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))
|
using (var canvas = new SKCanvas(saveBitmap))
|
||||||
{
|
{
|
||||||
// set background color if present
|
// set background color if present
|
||||||
@ -609,9 +630,11 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return outputPath;
|
return outputPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void CreateImageCollage(ImageCollageOptions options)
|
public void CreateImageCollage(ImageCollageOptions options)
|
||||||
{
|
{
|
||||||
double ratio = (double)options.Width / options.Height;
|
double ratio = (double)options.Width / options.Height;
|
||||||
|
@ -7,17 +7,30 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class SkiaException : Exception
|
public class SkiaException : Exception
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="SkiaException"/> class.
|
||||||
|
/// </summary>
|
||||||
public SkiaException() : base()
|
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)
|
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)
|
public SkiaException(string message, Exception innerException)
|
||||||
: base(message, innerException)
|
: base(message, innerException)
|
||||||
{
|
{
|
||||||
|
@ -5,15 +5,27 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace Jellyfin.Drawing.Skia
|
namespace Jellyfin.Drawing.Skia
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Used to build collages of multiple images arranged in vertical strips.
|
||||||
|
/// </summary>
|
||||||
public class StripCollageBuilder
|
public class StripCollageBuilder
|
||||||
{
|
{
|
||||||
private readonly SkiaEncoder _skiaEncoder;
|
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)
|
public StripCollageBuilder(SkiaEncoder skiaEncoder)
|
||||||
{
|
{
|
||||||
_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)
|
public static SKEncodedImageFormat GetEncodedFormat(string outputPath)
|
||||||
{
|
{
|
||||||
if (outputPath == null)
|
if (outputPath == null)
|
||||||
@ -48,6 +60,13 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
return SKEncodedImageFormat.Png;
|
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)
|
public void BuildSquareCollage(string[] paths, string outputPath, int width, int height)
|
||||||
{
|
{
|
||||||
using (var bitmap = BuildSquareCollageBitmap(paths, width, 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)
|
public void BuildThumbCollage(string[] paths, string outputPath, int width, int height)
|
||||||
{
|
{
|
||||||
using (var bitmap = BuildThumbCollageBitmap(paths, width, 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))
|
using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
|
||||||
{
|
{
|
||||||
currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
|
currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
|
||||||
|
|
||||||
// crop image
|
// crop image
|
||||||
int ix = (int)Math.Abs((iWidth - iSlice) / 2);
|
int ix = (int)Math.Abs((iWidth - iSlice) / 2);
|
||||||
using (var image = SKImage.FromBitmap(resizeBitmap))
|
using (var image = SKImage.FromBitmap(resizeBitmap))
|
||||||
|
@ -4,10 +4,25 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace Jellyfin.Drawing.Skia
|
namespace Jellyfin.Drawing.Skia
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Static helper class for drawing unplayed count indicators.
|
||||||
|
/// </summary>
|
||||||
public static class UnplayedCountIndicator
|
public static class UnplayedCountIndicator
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The x-offset used when drawing an unplayed count indicator.
|
||||||
|
/// </summary>
|
||||||
private const int OffsetFromTopRightCorner = 38;
|
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)
|
public static void DrawUnplayedCountIndicator(SKCanvas canvas, ImageDimensions imageSize, int count)
|
||||||
{
|
{
|
||||||
var x = imageSize.Width - OffsetFromTopRightCorner;
|
var x = imageSize.Width - OffsetFromTopRightCorner;
|
||||||
@ -19,6 +34,7 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
paint.Style = SKPaintStyle.Fill;
|
paint.Style = SKPaintStyle.Fill;
|
||||||
canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
|
canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var paint = new SKPaint())
|
using (var paint = new SKPaint())
|
||||||
{
|
{
|
||||||
paint.Color = new SKColor(255, 255, 255, 255);
|
paint.Color = new SKColor(255, 255, 255, 255);
|
||||||
@ -33,6 +49,7 @@ namespace Jellyfin.Drawing.Skia
|
|||||||
{
|
{
|
||||||
x -= 7;
|
x -= 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.Length == 2)
|
if (text.Length == 2)
|
||||||
{
|
{
|
||||||
x -= 13;
|
x -= 13;
|
||||||
|
@ -11,6 +11,7 @@ namespace MediaBrowser.Controller.Drawing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The supported input formats.</value>
|
/// <value>The supported input formats.</value>
|
||||||
IReadOnlyCollection<string> SupportedInputFormats { get; }
|
IReadOnlyCollection<string> SupportedInputFormats { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the supported output formats.
|
/// Gets the supported output formats.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -18,9 +19,9 @@ namespace MediaBrowser.Controller.Drawing
|
|||||||
IReadOnlyCollection<ImageFormat> SupportedOutputFormats { get; }
|
IReadOnlyCollection<ImageFormat> SupportedOutputFormats { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name.
|
/// Gets the display name for the encoder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The name.</value>
|
/// <value>The display name.</value>
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -35,17 +36,22 @@ namespace MediaBrowser.Controller.Drawing
|
|||||||
/// <value><c>true</c> if [supports image encoding]; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if [supports image encoding]; otherwise, <c>false</c>.</value>
|
||||||
bool SupportsImageEncoding { get; }
|
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);
|
ImageDimensions GetImageSize(string path);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Encodes the image.
|
/// Encode an image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
|
string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the image collage.
|
/// Create an image collage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options to use when creating the collage.</param>
|
||||||
void CreateImageCollage(ImageCollageOptions options);
|
void CreateImageCollage(ImageCollageOptions options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
<Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design">
|
<Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design">
|
||||||
<!-- disable warning CA1031: Do not catch general exception types -->
|
<!-- disable warning CA1031: Do not catch general exception types -->
|
||||||
<Rule Id="CA1031" Action="Info" />
|
<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 -->
|
<!-- disable warning CA1062: Validate arguments of public methods -->
|
||||||
<Rule Id="CA1062" Action="Info" />
|
<Rule Id="CA1062" Action="Info" />
|
||||||
<!-- disable warning CA1720: Identifiers should not contain type names -->
|
<!-- disable warning CA1720: Identifiers should not contain type names -->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user