diff --git a/NAPS2.Images.Gdi/GdiImageContext.cs b/NAPS2.Images.Gdi/GdiImageContext.cs index 7dc8234cf..e8585f3a0 100644 --- a/NAPS2.Images.Gdi/GdiImageContext.cs +++ b/NAPS2.Images.Gdi/GdiImageContext.cs @@ -11,11 +11,7 @@ public class GdiImageContext : ImageContext { private readonly GdiImageTransformer _imageTransformer; - public GdiImageContext() : this(null) - { - } - - public GdiImageContext(IPdfRenderer? pdfRenderer) : base(typeof(GdiImage), pdfRenderer) + public GdiImageContext() : base(typeof(GdiImage)) { _imageTransformer = new GdiImageTransformer(this); } diff --git a/NAPS2.Images.Gtk/GtkImageContext.cs b/NAPS2.Images.Gtk/GtkImageContext.cs index f2c8f3a85..71b1f0368 100644 --- a/NAPS2.Images.Gtk/GtkImageContext.cs +++ b/NAPS2.Images.Gtk/GtkImageContext.cs @@ -9,7 +9,7 @@ public class GtkImageContext : ImageContext private readonly GtkImageTransformer _imageTransformer; private readonly LibTiffIo _tiffIo; - public GtkImageContext(IPdfRenderer? pdfRenderer = null) : base(typeof(GtkImage), pdfRenderer) + public GtkImageContext() : base(typeof(GtkImage)) { _imageTransformer = new GtkImageTransformer(this); _tiffIo = new LibTiffIo(this); diff --git a/NAPS2.Images.ImageSharp/ImageSharpImageContext.cs b/NAPS2.Images.ImageSharp/ImageSharpImageContext.cs index e1ccbb667..0bd6fead5 100644 --- a/NAPS2.Images.ImageSharp/ImageSharpImageContext.cs +++ b/NAPS2.Images.ImageSharp/ImageSharpImageContext.cs @@ -21,7 +21,7 @@ public class ImageSharpImageContext : ImageContext Configuration = GetConfiguration() }; - public ImageSharpImageContext(IPdfRenderer? pdfRenderer = null) : base(typeof(ImageSharpImage), pdfRenderer) + public ImageSharpImageContext() : base(typeof(ImageSharpImage)) { _imageTransformer = new ImageSharpImageTransformer(this); } diff --git a/NAPS2.Images.Mac/MacImageContext.cs b/NAPS2.Images.Mac/MacImageContext.cs index e480fa2a0..46b1d1e68 100644 --- a/NAPS2.Images.Mac/MacImageContext.cs +++ b/NAPS2.Images.Mac/MacImageContext.cs @@ -9,7 +9,7 @@ public class MacImageContext : ImageContext private readonly MacImageTransformer _imageTransformer; - public MacImageContext(IPdfRenderer? pdfRenderer = null) : base(typeof(MacImage), pdfRenderer) + public MacImageContext() : base(typeof(MacImage)) { NSApplication.CheckForIllegalCrossThreadCalls = false; _imageTransformer = new MacImageTransformer(this); diff --git a/NAPS2.Images.Wpf/WpfImageContext.cs b/NAPS2.Images.Wpf/WpfImageContext.cs index 818166b54..22a0335ae 100644 --- a/NAPS2.Images.Wpf/WpfImageContext.cs +++ b/NAPS2.Images.Wpf/WpfImageContext.cs @@ -9,7 +9,7 @@ public class WpfImageContext : ImageContext { private readonly WpfImageTransformer _imageTransformer; - public WpfImageContext(IPdfRenderer? pdfRenderer = null) : base(typeof(WpfImage), pdfRenderer) + public WpfImageContext() : base(typeof(WpfImage)) { _imageTransformer = new WpfImageTransformer(this); } diff --git a/NAPS2.Images/IMemoryImage.cs b/NAPS2.Images/IMemoryImage.cs index 4b4ea01a0..2ebf25798 100644 --- a/NAPS2.Images/IMemoryImage.cs +++ b/NAPS2.Images/IMemoryImage.cs @@ -8,6 +8,8 @@ namespace NAPS2.Images; /// 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. /// /// Gets the image context used to create this image. /// diff --git a/NAPS2.Images/IPdfRendererProvider.cs b/NAPS2.Images/IPdfRendererProvider.cs new file mode 100644 index 000000000..4defe199c --- /dev/null +++ b/NAPS2.Images/IPdfRendererProvider.cs @@ -0,0 +1,6 @@ +namespace NAPS2.Images; + +public interface IPdfRendererProvider +{ + IPdfRenderer PdfRenderer { get; } +} \ No newline at end of file diff --git a/NAPS2.Images/ImageContext.cs b/NAPS2.Images/ImageContext.cs index 7b2fdc327..78f19fb6b 100644 --- a/NAPS2.Images/ImageContext.cs +++ b/NAPS2.Images/ImageContext.cs @@ -5,8 +5,6 @@ namespace NAPS2.Images; public abstract class ImageContext { - private readonly IPdfRenderer? _pdfRenderer; - public static ImageFileFormat GetFileFormatFromExtension(string path) { 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; - _pdfRenderer = pdfRenderer; } // 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 (_pdfRenderer == null) + if (pdfRenderer == null) { 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; } renderedPdf = null; 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 (_pdfRenderer == null) + if (pdfRenderer == null) { 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; - renderedPdf = _pdfRenderer.Render(this, stream.GetBuffer(), (int) stream.Length, PdfRenderSize.Default) + renderedPdf = pdfRenderer.Render(this, stream.GetBuffer(), (int) stream.Length, PdfRenderSize.Default) .Single(); return true; } @@ -239,22 +236,27 @@ public abstract class ImageContext public IMemoryImage Render(IRenderableImage image) { - var bitmap = RenderFromStorage(image.Storage); + var bitmap = RenderWithoutTransforms(image); 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) { case ImageFileStorage fileStorage: - if (MaybeRenderPdf(fileStorage, out var renderedPdf)) + if (MaybeRenderPdf(fileStorage, pdfRenderer, out var renderedPdf)) { return renderedPdf!; } return Load(fileStorage.FullPath); case ImageMemoryStorage memoryStorage: - if (MaybeRenderPdf(memoryStorage, out var renderedMemoryPdf)) + if (MaybeRenderPdf(memoryStorage, pdfRenderer, out var renderedMemoryPdf)) { return renderedMemoryPdf!; } diff --git a/NAPS2.Sdk.Tests/ContextualTests.cs b/NAPS2.Sdk.Tests/ContextualTests.cs index 77f03af6f..3e876cbf4 100644 --- a/NAPS2.Sdk.Tests/ContextualTests.cs +++ b/NAPS2.Sdk.Tests/ContextualTests.cs @@ -18,7 +18,7 @@ public class ContextualTests : IDisposable FolderPath = Path.GetFullPath(Path.Combine("naps2_test_temp", Path.GetRandomFileName())); Folder = Directory.CreateDirectory(FolderPath); - ImageContext = TestImageContextFactory.Get(new PdfiumPdfRenderer()); + ImageContext = TestImageContextFactory.Get(); ScanningContext = new ScanningContext(ImageContext); ScanningContext.TempFolderPath = Path.Combine(FolderPath, "temp"); Directory.CreateDirectory(ScanningContext.TempFolderPath); diff --git a/NAPS2.Sdk.Tests/Images/ImageSerializerTests.cs b/NAPS2.Sdk.Tests/Images/ImageSerializerTests.cs index 63e575fa9..7b52b98f3 100644 --- a/NAPS2.Sdk.Tests/Images/ImageSerializerTests.cs +++ b/NAPS2.Sdk.Tests/Images/ImageSerializerTests.cs @@ -278,7 +278,7 @@ public class ImageSerializerTests : ContextualTests public async Task PdfDeserializeToMemoryStorage(StorageConfig config) { 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"); File.WriteAllBytes(importPath, PdfResources.word_generated_pdf); @@ -340,7 +340,7 @@ public class ImageSerializerTests : ContextualTests private ScanningContext CreateDestContextWithFileStorage() { - return new ScanningContext(TestImageContextFactory.Get(new PdfiumPdfRenderer())) + return new ScanningContext(TestImageContextFactory.Get()) { FileStorageManager = FileStorageManager.CreateFolder(Path.Combine(FolderPath, "dest")) }; diff --git a/NAPS2.Sdk.Tests/TestImageContextFactory.cs b/NAPS2.Sdk.Tests/TestImageContextFactory.cs index c2dec6802..9fb9505fd 100644 --- a/NAPS2.Sdk.Tests/TestImageContextFactory.cs +++ b/NAPS2.Sdk.Tests/TestImageContextFactory.cs @@ -2,18 +2,18 @@ namespace NAPS2.Sdk.Tests; 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 // more comprehensive, i.e. set up some IMAGESHARP/SKIA compiler variables and have special test commands. #if MAC - return new NAPS2.Images.Mac.MacImageContext(pdfRenderer); + return new NAPS2.Images.Mac.MacImageContext(); #elif LINUX - return new NAPS2.Images.Gtk.GtkImageContext(pdfRenderer); + return new NAPS2.Images.Gtk.GtkImageContext(); #elif NET6_0_OR_GREATER - return new NAPS2.Images.ImageSharp.ImageSharpImageContext(pdfRenderer); + return new NAPS2.Images.ImageSharp.ImageSharpImageContext(); #else - return new NAPS2.Images.Gdi.GdiImageContext(pdfRenderer); + return new NAPS2.Images.Gdi.GdiImageContext(); #endif } } \ No newline at end of file diff --git a/NAPS2.Sdk/Images/ProcessedImage.cs b/NAPS2.Sdk/Images/ProcessedImage.cs index 86e137f98..05b42e395 100644 --- a/NAPS2.Sdk/Images/ProcessedImage.cs +++ b/NAPS2.Sdk/Images/ProcessedImage.cs @@ -1,3 +1,5 @@ +using NAPS2.Pdf; + namespace NAPS2.Images; /// @@ -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 /// all related instances are disposed (or the parent ScanningContext is disposed). /// -public class ProcessedImage : IRenderableImage, IDisposable, IEquatable +public class ProcessedImage : IRenderableImage, IPdfRendererProvider, IDisposable, IEquatable { private readonly RefCount.Token _token; private bool _disposed; @@ -189,4 +191,6 @@ public class ProcessedImage : IRenderableImage, IDisposable, IEquatable public record WeakReference(ProcessedImage ProcessedImage); + + IPdfRenderer IPdfRendererProvider.PdfRenderer => new PdfiumPdfRenderer(); } \ No newline at end of file diff --git a/NAPS2.Sdk/Images/ThumbnailRenderer.cs b/NAPS2.Sdk/Images/ThumbnailRenderer.cs index 6ac359e8e..957a5341f 100644 --- a/NAPS2.Sdk/Images/ThumbnailRenderer.cs +++ b/NAPS2.Sdk/Images/ThumbnailRenderer.cs @@ -17,7 +17,7 @@ public class ThumbnailRenderer public IMemoryImage Render(ProcessedImage processedImage, int outputSize) { - var image = _imageContext.RenderFromStorage(processedImage.Storage); + var image = _imageContext.RenderWithoutTransforms(processedImage); var transformList = processedImage.TransformState.Transforms; if (!processedImage.TransformState.IsEmpty) {