Replaced System.Drawing.Common with SkiaSharp

This commit is contained in:
Dimitri Vranken 2023-02-13 11:25:45 +01:00
parent c96fdc490a
commit f464be430b
5 changed files with 34 additions and 57 deletions

View file

@ -3,6 +3,7 @@
using FFMpegCore.Enums; using FFMpegCore.Enums;
using FFMpegCore.Extensions.System.Drawing.Common; using FFMpegCore.Extensions.System.Drawing.Common;
using FFMpegCore.Pipes; using FFMpegCore.Pipes;
using SkiaSharp;
var inputPath = "/path/to/input"; var inputPath = "/path/to/input";
var outputPath = "/path/to/output"; var outputPath = "/path/to/output";
@ -79,7 +80,7 @@ await FFMpegArguments
FFMpeg.PosterWithAudio(inputPath, inputAudioPath, outputPath); FFMpeg.PosterWithAudio(inputPath, inputAudioPath, outputPath);
// or // or
#pragma warning disable CA1416 #pragma warning disable CA1416
using var image = Image.FromFile(inputImagePath); using var image = SKBitmap.Decode(inputImagePath);
image.AddAudio(inputAudioPath, outputPath); image.AddAudio(inputAudioPath, outputPath);
#pragma warning restore CA1416 #pragma warning restore CA1416
} }

View file

@ -1,13 +1,17 @@
using System.Drawing; using SkiaSharp;
namespace FFMpegCore.Extensions.System.Drawing.Common namespace FFMpegCore.Extensions.System.Drawing.Common
{ {
public static class BitmapExtensions public static class BitmapExtensions
{ {
public static bool AddAudio(this Image poster, string audio, string output) public static bool AddAudio(this SKBitmap poster, string audio, string output)
{ {
var destination = $"{Environment.TickCount}.png"; var destination = $"{Environment.TickCount}.png";
poster.Save(destination); using (var fileStream = File.OpenWrite(destination))
{
poster.Encode(fileStream, SKEncodedImageFormat.Png, default); // PNG does not respect the quality parameter
}
try try
{ {
return FFMpeg.PosterWithAudio(destination, audio, output); return FFMpeg.PosterWithAudio(destination, audio, output);

View file

@ -1,7 +1,5 @@
using System.Drawing; using FFMpegCore.Pipes;
using System.Drawing.Imaging; using SkiaSharp;
using System.Runtime.InteropServices;
using FFMpegCore.Pipes;
namespace FFMpegCore.Extensions.System.Drawing.Common namespace FFMpegCore.Extensions.System.Drawing.Common
{ {
@ -13,44 +11,24 @@ public class BitmapVideoFrameWrapper : IVideoFrame, IDisposable
public string Format { get; private set; } public string Format { get; private set; }
public Bitmap Source { get; private set; } public SKBitmap Source { get; private set; }
public BitmapVideoFrameWrapper(Bitmap bitmap) public BitmapVideoFrameWrapper(SKBitmap bitmap)
{ {
Source = bitmap ?? throw new ArgumentNullException(nameof(bitmap)); Source = bitmap ?? throw new ArgumentNullException(nameof(bitmap));
Format = ConvertStreamFormat(bitmap.PixelFormat); Format = ConvertStreamFormat(bitmap.ColorType);
} }
public void Serialize(Stream stream) public void Serialize(Stream stream)
{ {
var data = Source.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, Source.PixelFormat); var data = Source.Bytes;
stream.Write(data, 0, data.Length);
try
{
var buffer = new byte[data.Stride * data.Height];
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
stream.Write(buffer, 0, buffer.Length);
}
finally
{
Source.UnlockBits(data);
}
} }
public async Task SerializeAsync(Stream stream, CancellationToken token) public async Task SerializeAsync(Stream stream, CancellationToken token)
{ {
var data = Source.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, Source.PixelFormat); var data = Source.Bytes;
await stream.WriteAsync(data, 0, data.Length, token).ConfigureAwait(false);
try
{
var buffer = new byte[data.Stride * data.Height];
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
await stream.WriteAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);
}
finally
{
Source.UnlockBits(data);
}
} }
public void Dispose() public void Dispose()
@ -58,27 +36,20 @@ public void Dispose()
Source.Dispose(); Source.Dispose();
} }
private static string ConvertStreamFormat(PixelFormat fmt) private static string ConvertStreamFormat(SKColorType fmt)
{ {
switch (fmt) switch (fmt)
{ {
case PixelFormat.Format16bppGrayScale: case SKColorType.Gray8:
return "gray16le"; return "gray8";
case PixelFormat.Format16bppRgb555: case SKColorType.Bgra8888:
return "bgr555le";
case PixelFormat.Format16bppRgb565:
return "bgr565le";
case PixelFormat.Format24bppRgb:
return "bgr24";
case PixelFormat.Format32bppArgb:
return "bgra"; return "bgra";
case PixelFormat.Format32bppPArgb: case SKColorType.Rgb888x:
//This is not really same as argb32 return "rgb";
return "argb"; case SKColorType.Rgba8888:
case PixelFormat.Format32bppRgb:
return "rgba"; return "rgba";
case PixelFormat.Format48bppRgb: case SKColorType.Rgb565:
return "rgb48le"; return "rgb565";
default: default:
throw new NotSupportedException($"Not supported pixel format {fmt}"); throw new NotSupportedException($"Not supported pixel format {fmt}");
} }

View file

@ -11,7 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="7.0.0" /> <PackageReference Include="SkiaSharp" Version="2.88.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,5 +1,6 @@
using System.Drawing; using System.Drawing;
using FFMpegCore.Pipes; using FFMpegCore.Pipes;
using SkiaSharp;
namespace FFMpegCore.Extensions.System.Drawing.Common namespace FFMpegCore.Extensions.System.Drawing.Common
{ {
@ -14,7 +15,7 @@ public static class FFMpegImage
/// <param name="streamIndex">Selected video stream index.</param> /// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param> /// <param name="inputFileIndex">Input file index</param>
/// <returns>Bitmap with the requested snapshot.</returns> /// <returns>Bitmap with the requested snapshot.</returns>
public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0) public static SKBitmap Snapshot(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{ {
var source = FFProbe.Analyse(input); var source = FFProbe.Analyse(input);
var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
@ -26,8 +27,8 @@ public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? capture
.ProcessSynchronously(); .ProcessSynchronously();
ms.Position = 0; ms.Position = 0;
using var bitmap = new Bitmap(ms); using var bitmap = SKBitmap.Decode(ms);
return bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), bitmap.PixelFormat); return bitmap.Copy();
} }
/// <summary> /// <summary>
/// Saves a 'png' thumbnail to an in-memory bitmap /// Saves a 'png' thumbnail to an in-memory bitmap
@ -38,7 +39,7 @@ public static Bitmap Snapshot(string input, Size? size = null, TimeSpan? capture
/// <param name="streamIndex">Selected video stream index.</param> /// <param name="streamIndex">Selected video stream index.</param>
/// <param name="inputFileIndex">Input file index</param> /// <param name="inputFileIndex">Input file index</param>
/// <returns>Bitmap with the requested snapshot.</returns> /// <returns>Bitmap with the requested snapshot.</returns>
public static async Task<Bitmap> SnapshotAsync(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0) public static async Task<SKBitmap> SnapshotAsync(string input, Size? size = null, TimeSpan? captureTime = null, int? streamIndex = null, int inputFileIndex = 0)
{ {
var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false); var source = await FFProbe.AnalyseAsync(input).ConfigureAwait(false);
var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex); var (arguments, outputOptions) = SnapshotArgumentBuilder.BuildSnapshotArguments(input, source, size, captureTime, streamIndex, inputFileIndex);
@ -50,7 +51,7 @@ await arguments
.ProcessAsynchronously(); .ProcessAsynchronously();
ms.Position = 0; ms.Position = 0;
return new Bitmap(ms); return SKBitmap.Decode(ms);
} }
} }
} }