Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions UglyToad.PdfPig.Rendering.Skia.Tests/PdfToImageHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static class PdfToImageHelper
private static SKBitmap createEmptyDiffImage(int minWidth, int minHeight, int maxWidth,
int maxHeight)
{
using (SKBitmap bim3 = new SKBitmap(new SKImageInfo(maxWidth, maxHeight, SKColorType.Rgb888x)))
var bim3 = new SKBitmap(new SKImageInfo(maxWidth, maxHeight, SKColorType.Rgb888x));
using (SKCanvas canvas = new SKCanvas(bim3))
{
if (minWidth != maxWidth || minHeight != maxHeight)
Expand All @@ -45,14 +45,14 @@ private static SKBitmap createEmptyDiffImage(int minWidth, int minHeight, int ma

private const byte _threshold = 2;

private static SKBitmap diffImages(SKBitmap bim1, SKBitmap bim2)
private static SKBitmap? diffImages(SKBitmap bim1, SKBitmap bim2)
{
int minWidth = Math.Min(bim1.Width, bim2.Width);
int minHeight = Math.Min(bim1.Height, bim2.Height);
int maxWidth = Math.Max(bim1.Width, bim2.Width);
int maxHeight = Math.Max(bim1.Height, bim2.Height);

SKBitmap bim3 = null;
SKBitmap? bim3 = null;
if (minWidth != maxWidth || minHeight != maxHeight)
{
bim3 = createEmptyDiffImage(minWidth, minHeight, maxWidth, maxHeight);
Expand Down Expand Up @@ -121,7 +121,7 @@ public static bool TestResizeSinglePage(string pdfFile, int pageNumber, string e
using (var document = PdfDocument.Open(docPath, SkiaRenderingParsingOptions.Instance))
{
document.AddSkiaPageFactory();
using (var actual = document.GetPageAsSKBitmap(pageNumber, scale))
using (var actual = document.GetPageAsSKBitmap(pageNumber, scale, SKColors.White))
{
var skInfo = new SKImageInfo()
{
Expand Down Expand Up @@ -150,7 +150,7 @@ public static bool TestResizeSinglePage(string pdfFile, int pageNumber, string e
return true;
}

SKBitmap bim3 = diffImages(expectedResize, actualResize);
using var bim3 = diffImages(expectedResize, actualResize);
if (bim3 is null)
{
return true;
Expand Down Expand Up @@ -189,7 +189,7 @@ public static bool TestSinglePage(string pdfFile, int pageNumber, string expecte
using (var document = PdfDocument.Open(docPath, SkiaRenderingParsingOptions.Instance))
{
document.AddSkiaPageFactory();
using (var actual = document.GetPageAsSKBitmap(pageNumber, scale))
using (var actual = document.GetPageAsSKBitmap(pageNumber, scale, SKColors.White))
{
if (filesAreIdentical(expected, actual))
{
Expand Down
5 changes: 5 additions & 0 deletions UglyToad.PdfPig.Rendering.Skia.Tests/TestRendering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,11 @@ public class TestRendering
"GHOSTSCRIPT-698721-1-rotated_1.png",
"GHOSTSCRIPT-698721-1-rotated.pdf", 1, 2
},
new object[]
{
"blend_opacity_1.png",
"blend_opacity.pdf", 1, 2
},
};

[Theory(Skip = "for debugging purpose.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
<None Update="Documents\Apitron.PDF.Kit.Samples_patternFill-rotated.pdf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Documents\blend_opacity.pdf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Documents\caly-issues-56-1.pdf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down Expand Up @@ -365,6 +368,9 @@
<None Update="ExpectedImages\pdfpig_skia\Apitron.PDF.Kit.Samples_patternFill-rotated_1.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="ExpectedImages\pdfpig_skia\blend_opacity_1.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="ExpectedImages\pdfpig_skia\caly-issues-56-1_1.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Expand Down
47 changes: 27 additions & 20 deletions UglyToad.PdfPig.Rendering.Skia/Helpers/SKPaintCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ internal sealed class SKPaintCache : IDisposable
private readonly bool _isAntialias;

private readonly Dictionary<int, SKPaint> _cache = new();

private readonly SKPaint _antialiasingPaint;
private readonly SKPaint _noAntialiasingPaint;
private readonly Dictionary<(bool, BlendMode), SKPaint> _imagePaintCache = new();

#if DEBUG
private readonly SKPaint _imageDebugPaint;
Expand All @@ -38,9 +36,7 @@ public SKPaintCache(bool isAntialias, float minimumLineWidth)
{
_isAntialias = isAntialias;
// minimumLineWidth not in use
_antialiasingPaint = new SKPaint() { IsAntialias = true };

_noAntialiasingPaint = new SKPaint() { IsAntialias = false };
#if DEBUG
_imageDebugPaint = new SKPaint()
{
Expand All @@ -53,15 +49,15 @@ public SKPaintCache(bool isAntialias, float minimumLineWidth)
}

private static int GetPaintKey(IColor color, double alpha, bool stroke, float? strokeWidth, LineJoinStyle? joinStyle,
LineCapStyle? capStyle, LineDashPattern? dashPattern)
LineCapStyle? capStyle, LineDashPattern? dashPattern, BlendMode blendMode)
{
return HashCode.Combine(color, alpha, stroke, strokeWidth, joinStyle, capStyle, GetHash(dashPattern));
return HashCode.Combine(color, alpha, stroke, strokeWidth, joinStyle, capStyle, GetHash(dashPattern), blendMode);
}

public SKPaint GetPaint(IColor color, double alpha, bool stroke, float? strokeWidth, LineJoinStyle? joinStyle,
LineCapStyle? capStyle, LineDashPattern? dashPattern)
LineCapStyle? capStyle, LineDashPattern? dashPattern, BlendMode blendMode)
{
var key = GetPaintKey(color, alpha, stroke, strokeWidth, joinStyle, capStyle, dashPattern);
var key = GetPaintKey(color, alpha, stroke, strokeWidth, joinStyle, capStyle, dashPattern, blendMode);

if (_cache.TryGetValue(key, out var paint))
{
Expand All @@ -73,6 +69,7 @@ public SKPaint GetPaint(IColor color, double alpha, bool stroke, float? strokeWi
IsAntialias = _isAntialias,
Color = color.ToSKColor(alpha),
Style = stroke ? SKPaintStyle.Stroke : SKPaintStyle.Fill,
BlendMode = blendMode.ToSKBlendMode()
};

if (stroke)
Expand Down Expand Up @@ -104,18 +101,25 @@ private static int GetHash(LineDashPattern? dashPattern)
return key;
}

public SKPaint GetPaint(IPdfImage pdfImage)
public SKPaint GetPaint(IPdfImage pdfImage, BlendMode blendMode)
{
if (!pdfImage.Interpolate)
// For non-Normal blend modes, use general cache with ValueTuple key
var key = (pdfImage.Interpolate, blendMode);

if (_imagePaintCache.TryGetValue(key, out var paint))
{
return _noAntialiasingPaint;
return paint;
}
return _antialiasingPaint;
}

public SKPaint GetAntialiasing()
{
return _antialiasingPaint;
paint = new SKPaint
{
IsAntialias = pdfImage.Interpolate,
BlendMode = blendMode.ToSKBlendMode()
};

_imagePaintCache[key] = paint;

return paint;
}

#if DEBUG
Expand All @@ -130,15 +134,18 @@ public void Dispose()
#if DEBUG
_imageDebugPaint.Dispose();
#endif
_antialiasingPaint.Dispose();
_noAntialiasingPaint.Dispose();

foreach (var pair in _cache)
{
pair.Value.PathEffect?.Dispose();
pair.Value.Dispose();
}
_cache.Clear();

foreach (var pair in _imagePaintCache)
{
pair.Value.Dispose();
}
_imagePaintCache.Clear();
}
}
}
6 changes: 0 additions & 6 deletions UglyToad.PdfPig.Rendering.Skia/Helpers/SkiaExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,14 +275,8 @@ public static SKMatrix ToSkMatrix(this TransformationMatrix transformationMatrix
0, 0, 1);
}

private static readonly bool doBlending = false;

public static SKBlendMode ToSKBlendMode(this BlendMode blendMode)
{
if (!doBlending)
{
return SKBlendMode.SrcOver;
}

// https://pdfium.googlesource.com/pdfium/+/refs/heads/main/core/fxge/skia/fx_skia_device.cpp
switch (blendMode)
Expand Down
12 changes: 9 additions & 3 deletions UglyToad.PdfPig.Rendering.Skia/PdfPigExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,23 @@ public static void AddSkiaPageFactory(this PdfDocument document)
/// <param name="document">The pdf document.</param>
/// <param name="pageNumber">The number of the page to return, this starts from 1.</param>
/// <param name="scale">The scale factor to use when rendering the page.</param>
/// <param name="clearColor">Optional background color to clear the canvas with before rendering. If null, the canvas is not cleared.</param>
/// <returns>The <see cref="SKBitmap"/>.</returns>
public static SKBitmap GetPageAsSKBitmap(this PdfDocument document, int pageNumber, float scale = 1)
public static SKBitmap GetPageAsSKBitmap(this PdfDocument document, int pageNumber, float scale = 1, SKColor? clearColor = null)
{
using (var picture = document.GetPage<SKPicture>(pageNumber))
{
var size = new SKSizeI((int)Math.Ceiling(picture.CullRect.Width * scale), (int)Math.Ceiling(picture.CullRect.Height * scale));
var page = document.GetPage(pageNumber);
var size = new SKSizeI((int)Math.Ceiling(page.Width * scale), (int)Math.Ceiling(page.Height * scale));
Comment thread
BobLd marked this conversation as resolved.
var scaleMatrix = SKMatrix.CreateScale(scale, scale);

var bitmap = new SKBitmap(size.Width, size.Height);
using (var canvas = new SKCanvas(bitmap))
{
if (clearColor.HasValue)
{
canvas.Clear(clearColor.Value);
}
canvas.DrawPicture(picture, in scaleMatrix);
return bitmap;
}
Expand All @@ -67,7 +73,7 @@ public static SKBitmap GetPageAsSKBitmap(this PdfDocument document, int pageNumb
public static MemoryStream GetPageAsPng(this PdfDocument document, int pageNumber, float scale = 1, int quality = 100)
{
var ms = new MemoryStream();
using (var bitmap = document.GetPageAsSKBitmap(pageNumber, scale))
using (var bitmap = document.GetPageAsSKBitmap(pageNumber, scale, SKColors.White))
{
bitmap.Encode(ms, SKEncodedImageFormat.Png, quality);
ms.Position = 0;
Expand Down
17 changes: 2 additions & 15 deletions UglyToad.PdfPig.Rendering.Skia/SkiaStreamProcessor.Annotations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,6 @@ internal partial class SkiaStreamProcessor
/// </summary>
public static readonly RGBColor DefaultRequiredFieldsHighlightColor = new RGBColor(1, 0, 0);

private static bool IsAnnotationBelowText(Annotation annotation)
{
// TODO - Very hackish
switch (annotation.Type)
{
case AnnotationType.Highlight:
return true;

default:
return false;
}
}

private static bool ShouldRender(Annotation annotation)
{
// cf. ISO 32000-2:2020(E) - Table 167 — Annotation flags
Expand All @@ -70,11 +57,11 @@ private static bool ShouldRender(Annotation annotation)

private readonly Lazy<Annotation[]> _annotations;

private void DrawAnnotations(bool isBelowText)
private void DrawAnnotations()
{
// https://github.com/apache/pdfbox/blob/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java
// https://github.com/apache/pdfbox/blob/c4b212ecf42a1c0a55529873b132ea338a8ba901/pdfbox/src/main/java/org/apache/pdfbox/contentstream/PDFStreamEngine.java#L312
foreach (Annotation annotation in _annotations.Value.Where(a => IsAnnotationBelowText(a) == isBelowText))
foreach (Annotation annotation in _annotations.Value)
{
// Check if visible
if (!ShouldRender(annotation))
Expand Down
9 changes: 6 additions & 3 deletions UglyToad.PdfPig.Rendering.Skia/SkiaStreamProcessor.Glyph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private void ShowVectorFontGlyph(SKPath path, IColor strokingColor, IColor nonSt
else
{
var fillBrush = _paintCache.GetPaint(nonStrokingColor, currentState.AlphaConstantNonStroking, false,
null, null, null, null);
null, null, null, null, currentState.BlendMode);
_canvas.DrawPath(transformedPath, fillBrush);
}
}
Expand All @@ -136,7 +136,7 @@ private void ShowVectorFontGlyph(SKPath path, IColor strokingColor, IColor nonSt
// Then stroke
var strokePaint = _paintCache.GetPaint(strokingColor, currentState.AlphaConstantStroking, true,
(float)currentState.LineWidth, currentState.JoinStyle, currentState.CapStyle,
currentState.LineDashPattern);
currentState.LineDashPattern, currentState.BlendMode);
_canvas.DrawPath(transformedPath, strokePaint);
}
}
Expand Down Expand Up @@ -181,12 +181,15 @@ private void ShowNonVectorFontGlyph(IFont font, IColor strokingColor, IColor non

var color = style == SKPaintStyle.Stroke ? strokingColor : nonStrokingColor; // TODO - very not correct

var currentState = GetCurrentState();

using (var skFont = drawTypeface.Typeface.ToFont(1f))
using (var paint = new SKPaint())
{
paint.Style = style.Value;
paint.Color = color.ToSKColor(GetCurrentState().AlphaConstantNonStroking);
paint.Color = color.ToSKColor(currentState.AlphaConstantNonStroking);
paint.IsAntialias = _antiAliasing;
paint.BlendMode = currentState.BlendMode.ToSKBlendMode();

// TODO - Benchmark with SPARC - v9 Architecture Manual.pdf
// as _canvas.DrawShapedText(unicode, startBaseLine, fontPaint); as very slow without 'Shaper' caching
Expand Down
Loading