Provide IPdfRenderer from ProcessedImage

This commit is contained in:
Ben Olden-Cooligan 2024-02-06 20:22:48 -08:00
parent 80dc5c0635
commit 8906f5b76e
13 changed files with 45 additions and 35 deletions

View File

@ -11,11 +11,7 @@ public class GdiImageContext : ImageContext
{ {
private readonly GdiImageTransformer _imageTransformer; private readonly GdiImageTransformer _imageTransformer;
public GdiImageContext() : this(null) public GdiImageContext() : base(typeof(GdiImage))
{
}
public GdiImageContext(IPdfRenderer? pdfRenderer) : base(typeof(GdiImage), pdfRenderer)
{ {
_imageTransformer = new GdiImageTransformer(this); _imageTransformer = new GdiImageTransformer(this);
} }

View File

@ -9,7 +9,7 @@ public class GtkImageContext : ImageContext
private readonly GtkImageTransformer _imageTransformer; private readonly GtkImageTransformer _imageTransformer;
private readonly LibTiffIo _tiffIo; private readonly LibTiffIo _tiffIo;
public GtkImageContext(IPdfRenderer? pdfRenderer = null) : base(typeof(GtkImage), pdfRenderer) public GtkImageContext() : base(typeof(GtkImage))
{ {
_imageTransformer = new GtkImageTransformer(this); _imageTransformer = new GtkImageTransformer(this);
_tiffIo = new LibTiffIo(this); _tiffIo = new LibTiffIo(this);

View File

@ -21,7 +21,7 @@ public class ImageSharpImageContext : ImageContext
Configuration = GetConfiguration() Configuration = GetConfiguration()
}; };
public ImageSharpImageContext(IPdfRenderer? pdfRenderer = null) : base(typeof(ImageSharpImage), pdfRenderer) public ImageSharpImageContext() : base(typeof(ImageSharpImage))
{ {
_imageTransformer = new ImageSharpImageTransformer(this); _imageTransformer = new ImageSharpImageTransformer(this);
} }

View File

@ -9,7 +9,7 @@ public class MacImageContext : ImageContext
private readonly MacImageTransformer _imageTransformer; private readonly MacImageTransformer _imageTransformer;
public MacImageContext(IPdfRenderer? pdfRenderer = null) : base(typeof(MacImage), pdfRenderer) public MacImageContext() : base(typeof(MacImage))
{ {
NSApplication.CheckForIllegalCrossThreadCalls = false; NSApplication.CheckForIllegalCrossThreadCalls = false;
_imageTransformer = new MacImageTransformer(this); _imageTransformer = new MacImageTransformer(this);

View File

@ -9,7 +9,7 @@ public class WpfImageContext : ImageContext
{ {
private readonly WpfImageTransformer _imageTransformer; private readonly WpfImageTransformer _imageTransformer;
public WpfImageContext(IPdfRenderer? pdfRenderer = null) : base(typeof(WpfImage), pdfRenderer) public WpfImageContext() : base(typeof(WpfImage))
{ {
_imageTransformer = new WpfImageTransformer(this); _imageTransformer = new WpfImageTransformer(this);
} }

View File

@ -8,6 +8,8 @@ namespace NAPS2.Images;
/// </summary> /// </summary>
public interface IMemoryImage : IImageStorage public interface IMemoryImage : IImageStorage
{ {
// TODO: Now that ImageContext objects are fully stateless, we can maybe eliminate ImageContext as a parameter
// in IMemoryImage constructors and just create the appropriate ImageContext objects automatically.
/// <summary> /// <summary>
/// Gets the image context used to create this image. /// Gets the image context used to create this image.
/// </summary> /// </summary>

View File

@ -0,0 +1,6 @@
namespace NAPS2.Images;
public interface IPdfRendererProvider
{
IPdfRenderer PdfRenderer { get; }
}

View File

@ -5,8 +5,6 @@ namespace NAPS2.Images;
public abstract class ImageContext public abstract class ImageContext
{ {
private readonly IPdfRenderer? _pdfRenderer;
public static ImageFileFormat GetFileFormatFromExtension(string path) public static ImageFileFormat GetFileFormatFromExtension(string path)
{ {
return Path.GetExtension(path).ToLowerInvariant() switch return Path.GetExtension(path).ToLowerInvariant() switch
@ -43,40 +41,39 @@ public abstract class ImageContext
}; };
} }
protected ImageContext(Type imageType, IPdfRenderer? pdfRenderer = null) protected ImageContext(Type imageType)
{ {
ImageType = imageType; ImageType = imageType;
_pdfRenderer = pdfRenderer;
} }
// TODO: Add NotNullWhen attribute? // TODO: Add NotNullWhen attribute?
private bool MaybeRenderPdf(ImageFileStorage fileStorage, out IMemoryImage? renderedPdf) private bool MaybeRenderPdf(ImageFileStorage fileStorage, IPdfRenderer? pdfRenderer, out IMemoryImage? renderedPdf)
{ {
if (Path.GetExtension(fileStorage.FullPath).ToLowerInvariant() == ".pdf") if (Path.GetExtension(fileStorage.FullPath).ToLowerInvariant() == ".pdf")
{ {
if (_pdfRenderer == null) if (pdfRenderer == null)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
"Unable to render pdf page as the ImageContext wasn't created with an IPdfRenderer."); "Unable to render pdf page as the IRenderableImage didn't implement IPdfRendererProvider.");
} }
renderedPdf = _pdfRenderer.Render(this, fileStorage.FullPath, PdfRenderSize.Default).Single(); renderedPdf = pdfRenderer.Render(this, fileStorage.FullPath, PdfRenderSize.Default).Single();
return true; return true;
} }
renderedPdf = null; renderedPdf = null;
return false; return false;
} }
private bool MaybeRenderPdf(ImageMemoryStorage memoryStorage, out IMemoryImage? renderedPdf) private bool MaybeRenderPdf(ImageMemoryStorage memoryStorage, IPdfRenderer? pdfRenderer, out IMemoryImage? renderedPdf)
{ {
if (memoryStorage.TypeHint == ".pdf") if (memoryStorage.TypeHint == ".pdf")
{ {
if (_pdfRenderer == null) if (pdfRenderer == null)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
"Unable to render pdf page as the ImageContext wasn't created with an IPdfRenderer."); "Unable to render pdf page as the IRenderableImage didn't implement IPdfRendererProvider.");
} }
var stream = memoryStorage.Stream; var stream = memoryStorage.Stream;
renderedPdf = _pdfRenderer.Render(this, stream.GetBuffer(), (int) stream.Length, PdfRenderSize.Default) renderedPdf = pdfRenderer.Render(this, stream.GetBuffer(), (int) stream.Length, PdfRenderSize.Default)
.Single(); .Single();
return true; return true;
} }
@ -239,22 +236,27 @@ public abstract class ImageContext
public IMemoryImage Render(IRenderableImage image) public IMemoryImage Render(IRenderableImage image)
{ {
var bitmap = RenderFromStorage(image.Storage); var bitmap = RenderWithoutTransforms(image);
return PerformAllTransforms(bitmap, image.TransformState.Transforms); return PerformAllTransforms(bitmap, image.TransformState.Transforms);
} }
public IMemoryImage RenderFromStorage(IImageStorage storage) public IMemoryImage RenderWithoutTransforms(IRenderableImage image)
{
return RenderFromStorage(image.Storage, (image as IPdfRendererProvider)?.PdfRenderer);
}
private IMemoryImage RenderFromStorage(IImageStorage storage, IPdfRenderer pdfRenderer)
{ {
switch (storage) switch (storage)
{ {
case ImageFileStorage fileStorage: case ImageFileStorage fileStorage:
if (MaybeRenderPdf(fileStorage, out var renderedPdf)) if (MaybeRenderPdf(fileStorage, pdfRenderer, out var renderedPdf))
{ {
return renderedPdf!; return renderedPdf!;
} }
return Load(fileStorage.FullPath); return Load(fileStorage.FullPath);
case ImageMemoryStorage memoryStorage: case ImageMemoryStorage memoryStorage:
if (MaybeRenderPdf(memoryStorage, out var renderedMemoryPdf)) if (MaybeRenderPdf(memoryStorage, pdfRenderer, out var renderedMemoryPdf))
{ {
return renderedMemoryPdf!; return renderedMemoryPdf!;
} }

View File

@ -18,7 +18,7 @@ public class ContextualTests : IDisposable
FolderPath = Path.GetFullPath(Path.Combine("naps2_test_temp", Path.GetRandomFileName())); FolderPath = Path.GetFullPath(Path.Combine("naps2_test_temp", Path.GetRandomFileName()));
Folder = Directory.CreateDirectory(FolderPath); Folder = Directory.CreateDirectory(FolderPath);
ImageContext = TestImageContextFactory.Get(new PdfiumPdfRenderer()); ImageContext = TestImageContextFactory.Get();
ScanningContext = new ScanningContext(ImageContext); ScanningContext = new ScanningContext(ImageContext);
ScanningContext.TempFolderPath = Path.Combine(FolderPath, "temp"); ScanningContext.TempFolderPath = Path.Combine(FolderPath, "temp");
Directory.CreateDirectory(ScanningContext.TempFolderPath); Directory.CreateDirectory(ScanningContext.TempFolderPath);

View File

@ -278,7 +278,7 @@ public class ImageSerializerTests : ContextualTests
public async Task PdfDeserializeToMemoryStorage(StorageConfig config) public async Task PdfDeserializeToMemoryStorage(StorageConfig config)
{ {
config.Apply(this); config.Apply(this);
using var destContext = new ScanningContext(TestImageContextFactory.Get(new PdfiumPdfRenderer())); using var destContext = new ScanningContext(TestImageContextFactory.Get());
var importPath = Path.Combine(FolderPath, "import.pdf"); var importPath = Path.Combine(FolderPath, "import.pdf");
File.WriteAllBytes(importPath, PdfResources.word_generated_pdf); File.WriteAllBytes(importPath, PdfResources.word_generated_pdf);
@ -340,7 +340,7 @@ public class ImageSerializerTests : ContextualTests
private ScanningContext CreateDestContextWithFileStorage() private ScanningContext CreateDestContextWithFileStorage()
{ {
return new ScanningContext(TestImageContextFactory.Get(new PdfiumPdfRenderer())) return new ScanningContext(TestImageContextFactory.Get())
{ {
FileStorageManager = FileStorageManager.CreateFolder(Path.Combine(FolderPath, "dest")) FileStorageManager = FileStorageManager.CreateFolder(Path.Combine(FolderPath, "dest"))
}; };

View File

@ -2,18 +2,18 @@ namespace NAPS2.Sdk.Tests;
public static class TestImageContextFactory public static class TestImageContextFactory
{ {
public static ImageContext Get(IPdfRenderer pdfRenderer = null) public static ImageContext Get()
{ {
// TODO: For now we use ImageSharp on net6-windows for coverage. But eventually we'll need to do something // TODO: For now we use ImageSharp on net6-windows for coverage. But eventually we'll need to do something
// more comprehensive, i.e. set up some IMAGESHARP/SKIA compiler variables and have special test commands. // more comprehensive, i.e. set up some IMAGESHARP/SKIA compiler variables and have special test commands.
#if MAC #if MAC
return new NAPS2.Images.Mac.MacImageContext(pdfRenderer); return new NAPS2.Images.Mac.MacImageContext();
#elif LINUX #elif LINUX
return new NAPS2.Images.Gtk.GtkImageContext(pdfRenderer); return new NAPS2.Images.Gtk.GtkImageContext();
#elif NET6_0_OR_GREATER #elif NET6_0_OR_GREATER
return new NAPS2.Images.ImageSharp.ImageSharpImageContext(pdfRenderer); return new NAPS2.Images.ImageSharp.ImageSharpImageContext();
#else #else
return new NAPS2.Images.Gdi.GdiImageContext(pdfRenderer); return new NAPS2.Images.Gdi.GdiImageContext();
#endif #endif
} }
} }

View File

@ -1,3 +1,5 @@
using NAPS2.Pdf;
namespace NAPS2.Images; namespace NAPS2.Images;
/// <summary> /// <summary>
@ -9,7 +11,7 @@ namespace NAPS2.Images;
/// reference with Clone() that will need to be disposed, and the underlying image storage will only be disposed once /// reference with Clone() that will need to be disposed, and the underlying image storage will only be disposed once
/// all related instances are disposed (or the parent ScanningContext is disposed). /// all related instances are disposed (or the parent ScanningContext is disposed).
/// </summary> /// </summary>
public class ProcessedImage : IRenderableImage, IDisposable, IEquatable<ProcessedImage> public class ProcessedImage : IRenderableImage, IPdfRendererProvider, IDisposable, IEquatable<ProcessedImage>
{ {
private readonly RefCount.Token _token; private readonly RefCount.Token _token;
private bool _disposed; private bool _disposed;
@ -189,4 +191,6 @@ public class ProcessedImage : IRenderableImage, IDisposable, IEquatable<Processe
/// at any moment. /// at any moment.
/// </param> /// </param>
public record WeakReference(ProcessedImage ProcessedImage); public record WeakReference(ProcessedImage ProcessedImage);
IPdfRenderer IPdfRendererProvider.PdfRenderer => new PdfiumPdfRenderer();
} }

View File

@ -17,7 +17,7 @@ public class ThumbnailRenderer
public IMemoryImage Render(ProcessedImage processedImage, int outputSize) public IMemoryImage Render(ProcessedImage processedImage, int outputSize)
{ {
var image = _imageContext.RenderFromStorage(processedImage.Storage); var image = _imageContext.RenderWithoutTransforms(processedImage);
var transformList = processedImage.TransformState.Transforms; var transformList = processedImage.TransformState.Transforms;
if (!processedImage.TransformState.IsEmpty) if (!processedImage.TransformState.IsEmpty)
{ {