Migrated to SixLabors.ImageSharp from System.Drawing.
Updated for PDFiumCore v100.0.4849.1.
This commit is contained in:
DJGosnell 2022-01-26 17:46:25 -05:00
Родитель d3536b47b6
Коммит 918b4c8857
18 изменённых файлов: 180 добавлений и 158 удалений

Просмотреть файл

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30413.136
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DtronixPdf", "DtronixPdf\DtronixPdf.csproj", "{23F0BD01-C9B9-4DC8-A444-978A4A91F5EE}"
EndProject

Просмотреть файл

@ -1,10 +1,9 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using DtronixPdf.Dispatcher;
using DtronixPdf.Renderer.Dispatcher;
using PDFiumCore;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace DtronixPdf.Actions
{
@ -39,16 +38,16 @@ namespace DtronixPdf.Actions
_dispatcher = dispatcher;
}
protected override PdfBitmap OnExecute()
protected override unsafe PdfBitmap OnExecute()
{
try
{
_cancellationToken.ThrowIfCancellationRequested();
_bitmap = fpdfview.FPDFBitmapCreateEx(
(int) _viewport.Size.Width,
(int) _viewport.Size.Height,
(int) (_includeAlpha ? FPDFBitmapFormat.BGRA : FPDFBitmapFormat.BGR),
(int)_viewport.Size.Width,
(int)_viewport.Size.Height,
(int)(_includeAlpha ? FPDFBitmapFormat.BGRA : FPDFBitmapFormat.BGR),
IntPtr.Zero,
0);
@ -56,11 +55,15 @@ namespace DtronixPdf.Actions
throw new Exception("failed to create a bitmap object");
_cancellationToken.ThrowIfCancellationRequested();
if (_backgroundColor.HasValue)
{
fpdfview.FPDFBitmapFillRect(
_bitmap, 0, 0, (int) _viewport.Size.Width, (int) _viewport.Size.Height, (uint) _backgroundColor.Value.ToArgb());
_bitmap,
0,
0,
(int)_viewport.Size.Width,
(int)_viewport.Size.Height,
_backgroundColor.Value.ToPixel<Argb32>().Argb);
_cancellationToken.ThrowIfCancellationRequested();
}
@ -83,19 +86,23 @@ namespace DtronixPdf.Actions
clipping.Bottom = 0;
clipping.Top = _viewport.Size.Height;
fpdfview.FPDF_RenderPageBitmapWithMatrix(_bitmap, _pageInstance, matrix, clipping, (int) _flags);
fpdfview.FPDF_RenderPageBitmapWithMatrix(_bitmap, _pageInstance, matrix, clipping, (int)_flags);
// Cancellation check;
_cancellationToken.ThrowIfCancellationRequested();
var scan0 = fpdfview.FPDFBitmapGetBuffer(_bitmap);
return new PdfBitmap(
_bitmap,
(int) _viewport.Size.Width,
(int) _viewport.Size.Height,
_dispatcher,
_includeAlpha ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb,
_scale,
_viewport);
var image = _includeAlpha
? (Image)Image.WrapMemory<Bgra32>(
scan0.ToPointer(),
(int)_viewport.Size.Width,
(int)_viewport.Size.Height)
: Image.WrapMemory<Bgr24>(
scan0.ToPointer(),
(int)_viewport.Size.Width,
(int)_viewport.Size.Height);
return new PdfBitmap(_bitmap, image, _dispatcher, _scale, _viewport);
}
catch (OperationCanceledException)
{

Просмотреть файл

@ -1,6 +1,6 @@
using System;
namespace DtronixPdf.Renderer.Dispatcher
namespace DtronixPdf.Dispatcher
{
public class ActionThreadDispatcherAction : ThreadMessagePumpAction<bool>
{

Просмотреть файл

@ -1,6 +1,6 @@
using System;
namespace DtronixPdf.Renderer.Dispatcher
namespace DtronixPdf.Dispatcher
{
public class FuncMessagePumpAction<TResult> : ThreadMessagePumpAction<TResult>
{

Просмотреть файл

@ -2,7 +2,6 @@
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using DtronixPdf.Renderer.Dispatcher;
namespace DtronixPdf.Dispatcher
{

Просмотреть файл

@ -1,7 +1,7 @@
using System;
using System.Threading.Tasks;
namespace DtronixPdf.Renderer.Dispatcher
namespace DtronixPdf.Dispatcher
{
public abstract class ThreadDispatcherAction
{
@ -11,14 +11,13 @@ namespace DtronixPdf.Renderer.Dispatcher
public abstract class ThreadMessagePumpAction<TResult> : ThreadDispatcherAction
{
private readonly TaskCompletionSource<TResult> _completionSource
= new TaskCompletionSource<TResult>();
private readonly TaskCompletionSource<TResult> _completionSource = new();
public Task<TResult> Result => _completionSource.Task;
internal override void SetFailed(Exception e)
{
var trys = _completionSource.TrySetException(e);
_completionSource.TrySetException(e);
}
public override void Execute()

Просмотреть файл

@ -1,6 +1,6 @@
using System;
namespace DtronixPdf.Renderer.Dispatcher
namespace DtronixPdf.Dispatcher
{
public class ThreadDispatcherExceptionEventArgs : EventArgs
{

Просмотреть файл

@ -1,6 +1,6 @@
using System;
namespace DtronixPdf.Renderer.Dispatcher
namespace DtronixPdf.Dispatcher
{
public abstract class ThreadDispatcherVoidAction : ThreadDispatcherAction
{

Просмотреть файл

@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="PDFiumCore" Version="4270.0.0" />
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
<PackageReference Include="PDFiumCore" Version="100.0.4849.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DtronixPdf.Dispatcher;
using PDFiumCore;
namespace DtronixPdf
{
public class PDFiumManager
{
internal readonly ThreadDispatcher Dispatcher;
private static int Instances = 0;
private static PDFiumManager _managerDefaultInstance;
private static bool FPDF_InitLibrary;
public static PDFiumManager Default => _managerDefaultInstance ??= new PDFiumManager();
public PDFiumManager()
{
Dispatcher = new ThreadDispatcher();
Dispatcher.Start();
if (!FPDF_InitLibrary)
Initialize();
}
private async Task Initialize()
{
FPDF_InitLibrary = true;
// Initialize the library.
await Dispatcher.QueueForCompletion(fpdfview.FPDF_InitLibrary);
}
}
}

Просмотреть файл

@ -1,10 +1,8 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading.Tasks;
using DtronixPdf.Dispatcher;
using DtronixPdf.Renderer.Dispatcher;
using PDFiumCore;
using SixLabors.ImageSharp;
namespace DtronixPdf
{
@ -14,55 +12,34 @@ namespace DtronixPdf
private readonly ThreadDispatcher _dispatcher;
public int Width { get; }
public int Height { get; }
public PixelFormat Format { get; }
public int Stride { get; }
public IntPtr Scan0 { get; }
public float Scale { get; }
public RectangleF Viewport { get; }
public Image Image { get; }
public bool IsDisposed { get; private set; }
/// <summary>
/// Only call within the dispatcher since dll calls are made.
/// </summary>
/// <param name="pdfBitmap"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="image"></param>
/// <param name="dispatcher"></param>
/// <param name="format"></param>
/// <param name="scale"></param>
/// <param name="viewport"></param>
internal PdfBitmap(
FpdfBitmapT pdfBitmap,
int width,
int height,
ThreadDispatcher dispatcher,
PixelFormat format,
Image image,
ThreadDispatcher dispatcher,
float scale,
RectangleF viewport)
{
_pdfBitmap = pdfBitmap;
_dispatcher = dispatcher;
Scan0 = fpdfview.FPDFBitmapGetBuffer(pdfBitmap);
Stride = fpdfview.FPDFBitmapGetStride(pdfBitmap);
Height = height;
Format = format;
Scale = scale;
Viewport = viewport;
Width = width;
}
public Bitmap ToBitmap()
{
return new Bitmap(Width, Height, Stride, Format, Scan0);
Image = image;
}
public async ValueTask DisposeAsync()

Просмотреть файл

@ -5,8 +5,6 @@ using System.Reflection;
using System.Threading.Tasks;
using DtronixPdf.Actions;
using DtronixPdf.Dispatcher;
using DtronixPdf.Renderer;
using DtronixPdf.Renderer.Dispatcher;
using PDFiumCore;
@ -16,28 +14,30 @@ namespace DtronixPdf
{
private readonly FpdfDocumentT _documentInstance;
private static readonly ThreadDispatcher Dispatcher;
private readonly PDFiumManager _manager;
public int Pages { get; private set; }
static PdfDocument()
{
Dispatcher = new ThreadDispatcher();
Dispatcher.Start();
// Initialize the library.
Dispatcher.QueueForCompletion(fpdfview.FPDF_InitLibrary);
}
private PdfDocument(FpdfDocumentT documentInstance)
private PdfDocument(PDFiumManager manager, FpdfDocumentT documentInstance)
{
_manager = manager;
_documentInstance = documentInstance;
}
public static async Task<PdfDocument> Load(string path, string password)
public static Task<PdfDocument> Load(string path, string password)
{
return Load(path, password, PDFiumManager.Default);
}
public static async Task<PdfDocument> Load(
string path,
string password,
PDFiumManager manager)
{
int pages = -1;
var result = await Dispatcher.QueueWithResult(() =>
var result = await manager.Dispatcher.QueueWithResult(() =>
{
var document = fpdfview.FPDF_LoadDocument(path, password);
pages = fpdfview.FPDF_GetPageCount(document);
@ -47,27 +47,32 @@ namespace DtronixPdf
if (result == null)
return null;
var pdfDocument = new PdfDocument(result)
var pdfDocument = new PdfDocument(manager, result)
{
Pages = pages
Pages = pages,
};
return pdfDocument;
}
public static async Task<PdfDocument> Create()
public static Task<PdfDocument> Create()
{
var result = await Dispatcher.QueueWithResult(fpdf_edit.FPDF_CreateNewDocument);
return Create(PDFiumManager.Default);
}
public static async Task<PdfDocument> Create(PDFiumManager manager)
{
var result = await manager.Dispatcher.QueueWithResult(fpdf_edit.FPDF_CreateNewDocument);
if (result == null)
return null;
return new PdfDocument(result);
return new PdfDocument(manager, result);
}
public Task<PdfPage> GetPage(int pageIndex)
{
return PdfPage.Create(Dispatcher, _documentInstance, pageIndex);
return PdfPage.Create(_manager.Dispatcher, _documentInstance, pageIndex);
}
/// <summary>
@ -81,7 +86,7 @@ namespace DtronixPdf
/// <returns>True on success, false on failure.</returns>
public async Task<bool> ImportPages(PdfDocument document, string pageRange, int insertIndex)
{
return await Dispatcher.QueueForCompletion(() =>
return await _manager.Dispatcher.QueueForCompletion(() =>
fpdf_ppo.FPDF_ImportPages(_documentInstance, document._documentInstance, pageRange, insertIndex));
}
@ -105,7 +110,7 @@ namespace DtronixPdf
/// <returns>True on success, false on failure.</returns>
public Task<bool> DeletePage(int pageIndex)
{
return Dispatcher.QueueForCompletion(() => fpdf_edit.FPDFPageDelete(_documentInstance, pageIndex));
return _manager.Dispatcher.QueueForCompletion(() => fpdf_edit.FPDFPageDelete(_documentInstance, pageIndex));
}
@ -136,7 +141,7 @@ namespace DtronixPdf
#define FPDF_REMOVE_SECURITY 3
*/
var result = await Dispatcher.QueueForCompletion(() =>
var result = await _manager.Dispatcher.QueueForCompletion(() =>
fpdf_save.FPDF_SaveAsCopy(_documentInstance, writer, 1));
return result;
@ -144,10 +149,10 @@ namespace DtronixPdf
public async ValueTask DisposeAsync()
{
await Dispatcher.QueueForCompletion(() =>
fpdfview.FPDF_CloseDocument(_documentInstance));
Dispatcher.Stop();
await _manager.Dispatcher.QueueForCompletion(() =>
{
fpdfview.FPDF_CloseDocument(_documentInstance);
});
}
}
}

Просмотреть файл

@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DtronixPdf.Actions;
using DtronixPdf.Dispatcher;
using DtronixPdf.Renderer.Dispatcher;
using PDFiumCore;
using SixLabors.ImageSharp;
namespace DtronixPdf
{

Просмотреть файл

@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace DtronixPdf
{
[Flags]
public enum FPDFBitmapFormat
{
/// <summary>
/// Unknown or unsupported format.
/// </summary>
Unknown = 0,
/// <summary>
/// Gray scale bitmap, one byte per pixel.
/// </summary>
Gray = 1,
/// <summary>
/// 3 bytes per pixel, byte order: blue, green, red.
/// </summary>
BGR = 2,
/// <summary>
/// 4 bytes per pixel, byte order: blue, green, red, unused.
/// </summary>
BGRx = 3,
/// <summary>
/// 4 bytes per pixel, byte order: blue, green, red, alpha.
/// </summary>
BGRA = 4
}
}

Просмотреть файл

@ -0,0 +1,6 @@
namespace DtronixPdf
{
public class PdfiumConfig {
public bool AutoUnload { get; init; }
}
}

Просмотреть файл

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DtronixPdf\DtronixPdf.csproj" />

Просмотреть файл

@ -1,60 +1,87 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using DtronixPdf;
using DtronixPdf.Dispatcher;
using PDFiumCore;
using SixLabors.ImageSharp;
namespace DtronixPdfBenchmark
{
class Program
{
static Stopwatch sw = new Stopwatch();
private const string TestPdf = "TestPdf.pdf";
static async Task Main(string[] args)
{
await RenderViewport();
if(!Directory.Exists("output"))
Directory.CreateDirectory("output");
//await RenderViewportScaling();
await OpenAndCloseBenchmark();
Console.ReadLine();
}
static async Task RenderViewport()
static async Task RenderViewportScaling()
{
var drawing = await PdfDocument.Load("TestPdf.pdf", null);
await using var page = await drawing.GetPage(0);
Console.WriteLine($"RenderViewport Benchmark {TestPdf}");
var document = await PdfDocument.Load(TestPdf, null);
sw.Start();
var iterations = 2;
//await using var page = await drawing.GetPage(0);
var viewport = new Rectangle(500, 50, 500, 500);
sw.Restart();
var iterations = 100;
for (int i = 1; i < iterations; i++)
{
/*
* new BitmapClip(
viewport.Left * scale + viewport.Width / 2 * (scale - 1),
viewport.Top * scale + viewport.Height / 2 * (scale - 1),
(page.Size.Width - viewport.Width - viewport.Left) * scale + viewport.Width / 2 * (scale - 1),
((page.Size.Height - viewport.Height - viewport.Top) * scale) + viewport.Height / 2 * (scale - 1)),
*/
float scale = i;
await using var page = await document.GetPage(0);
float scale = i * 0.25f;
Point center = new Point(0, 0);
Size size = new Size(1920, 1080);
await using var result = await page.Render(RenderFlags.RenderAnnotations, scale,
new Rectangle((int) ((page.Size.Width / 2 - size.Width / 2 + center.X) * scale + size.Width / 2 * (scale - 1)),
(int) ((page.Size.Height / 2 - size.Height / 2 - center.Y) * scale + size.Height / 2 * (scale - 1)),
size.Width,
size.Height),
var viewport = new RectangleF(
(int)((page.Size.Width / 2 - size.Width / 2 + center.X) * scale + size.Width / 2 * (scale - 1)),
(int)((page.Size.Height / 2 - size.Height / 2 - center.Y) * scale + size.Height / 2 * (scale - 1)),
size.Width,
size.Height);
await using var result = await page.Render(
RenderFlags.RenderAnnotations,
scale,
viewport,
false, Color.White, default, DispatcherPriority.Normal);
Console.WriteLine($"{sw.ElapsedMilliseconds:##,###}");
result.ToBitmap().Save($"test{i}.png");
await result.Image.SaveAsPngAsync($"output/{TestPdf}-{i}.png");
Console.WriteLine($"{sw.ElapsedMilliseconds:##,###} Milliseconds");
sw.Restart();
}
sw.Stop();
await drawing.DisposeAsync();
await document.DisposeAsync();
Console.WriteLine($"Rendering {TestPdf} Complete");
}
static async Task OpenAndCloseBenchmark()
{
Console.WriteLine($"Open and Close {TestPdf}");
sw.Restart();
var iterations = 100;
for (int i = 1; i < iterations; i++)
{
await using var document = await PdfDocument.Load(TestPdf, null);
await using var page = await document.GetPage(0);
Console.WriteLine($"{sw.ElapsedMilliseconds:##,###} Milliseconds");
sw.Restart();
}
sw.Stop();
Console.WriteLine($"Open and Close {TestPdf} Complete");
}
static async Task RenderTests()

Просмотреть файл

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>