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;
public GdiImageContext() : this(null)
{
}
public GdiImageContext(IPdfRenderer? pdfRenderer) : base(typeof(GdiImage), pdfRenderer)
public GdiImageContext() : base(typeof(GdiImage))
{
_imageTransformer = new GdiImageTransformer(this);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -8,6 +8,8 @@ namespace NAPS2.Images;
/// </summary>
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>
/// Gets the image context used to create this image.
/// </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
{
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!;
}

View File

@ -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);

View File

@ -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"))
};

View File

@ -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
}
}

View File

@ -1,3 +1,5 @@
using NAPS2.Pdf;
namespace NAPS2.Images;
/// <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
/// all related instances are disposed (or the parent ScanningContext is disposed).
/// </summary>
public class ProcessedImage : IRenderableImage, IDisposable, IEquatable<ProcessedImage>
public class ProcessedImage : IRenderableImage, IPdfRendererProvider, IDisposable, IEquatable<ProcessedImage>
{
private readonly RefCount.Token _token;
private bool _disposed;
@ -189,4 +191,6 @@ public class ProcessedImage : IRenderableImage, IDisposable, IEquatable<Processe
/// at any moment.
/// </param>
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)
{
var image = _imageContext.RenderFromStorage(processedImage.Storage);
var image = _imageContext.RenderWithoutTransforms(processedImage);
var transformList = processedImage.TransformState.Transforms;
if (!processedImage.TransformState.IsEmpty)
{