Skip to content

Commit

Permalink
Added support for shading type 0, 4, 5, 6 and 7
Browse files Browse the repository at this point in the history
  • Loading branch information
dmester committed Feb 25, 2024
1 parent 705d59f commit 1fc2cb1
Show file tree
Hide file tree
Showing 50 changed files with 2,070 additions and 154 deletions.
3 changes: 3 additions & 0 deletions src/PdfToSvg/DocumentModel/Names.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ internal static class Names
public static PdfName PaintType { get; } = new PdfName("PaintType");
public static PdfName XStep { get; } = new PdfName("XStep");
public static PdfName YStep { get; } = new PdfName("YStep");
public static PdfName VerticesPerRow { get; } = new PdfName("VerticesPerRow");

public static PdfName Filter { get; } = new PdfName("Filter");
public static PdfName Crypt { get; } = new PdfName("Crypt");
Expand All @@ -169,6 +170,8 @@ internal static class Names
public static PdfName BlackIs1 { get; } = new PdfName("BlackIs1");
public static PdfName K { get; } = new PdfName("K");
public static PdfName BitsPerComponent { get; } = new PdfName("BitsPerComponent");
public static PdfName BitsPerCoordinate { get; } = new PdfName("BitsPerCoordinate");
public static PdfName BitsPerFlag { get; } = new PdfName("BitsPerFlag");
public static PdfName Colors { get; } = new PdfName("Colors");
public static PdfName Interpolate { get; } = new PdfName("Interpolate");

Expand Down
17 changes: 14 additions & 3 deletions src/PdfToSvg/Drawing/Matrix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,25 @@ public Matrix(

public static bool operator !=(Matrix a, Matrix b) => !(a == b);

public static Point operator *(Matrix a, Point b)
public static Point operator *(Matrix matrix, Point point)
{
return new Point(
a.A * b.X + a.C * b.Y + a.E,
a.B * b.X + a.D * b.Y + a.F
matrix.A * point.X + matrix.C * point.Y + matrix.E,
matrix.B * point.X + matrix.D * point.Y + matrix.F
);
}

public static Point[] operator *(Matrix matrix, Rectangle rect)
{
return new[]
{
matrix * rect.BottomLeft,
matrix * rect.TopLeft,
matrix * rect.TopRight,
matrix * rect.BottomRight,
};
}

public static Matrix Multiply(Matrix a, Matrix b)
{
return new Matrix(
Expand Down
23 changes: 23 additions & 0 deletions src/PdfToSvg/Drawing/Paths/PathData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,29 @@ public void CurveTo(double x1, double y1, double x2, double y2, double x3, doubl
commands.Add(new CurveToCommand(x1, y1, x2, y2, x3, y3));
}

public void Polygon(IEnumerable<Point> points)
{
using var enumerator = points.GetEnumerator();

if (enumerator.MoveNext())
{
var multiplePoints = false;

MoveTo(enumerator.Current.X, enumerator.Current.Y);

while (enumerator.MoveNext())
{
LineTo(enumerator.Current.X, enumerator.Current.Y);
multiplePoints = true;
}

if (multiplePoints)
{
ClosePath();
}
}
}

public PathData Transform(Matrix matrix)
{
var result = new PathData();
Expand Down
102 changes: 102 additions & 0 deletions src/PdfToSvg/Drawing/Rasterization/Canvas.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) PdfToSvg.NET contributors.
// https://github.com/dmester/pdftosvg.net
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PdfToSvg.Drawing.Rasterization
{
/// <summary>
/// For rasiterizing polygons. The rasterizer does not support anti-aliasing.
/// </summary>
internal class Canvas
{
private EdgeTable edges;
private int nextID = 1;

public Canvas(int width, int height)
{
if (width <= 0) throw new ArgumentOutOfRangeException(nameof(width));
if (height <= 0) throw new ArgumentOutOfRangeException(nameof(height));

this.edges = new EdgeTable(width, height);
Width = width;
Height = height;
}

public int Width { get; }
public int Height { get; }

public void FillPolygon(Rgba32Color color, Point[] points)
{
var id = nextID++;

for (var i = 1; i < points.Length; i++)
{
edges.Add(new Edge(id, points[i - 1], points[i], color));
}

// Close polygon
if (points[0].X != points[points.Length - 1].X ||
points[0].Y != points[points.Length - 1].Y)
{
edges.Add(new Edge(id, points[points.Length - 1], points[0], color));
}
}

public IEnumerable<byte[]> ReadRgbaRows()
{
this.edges.Sort();

var layers = new LayerManager();

var row = new byte[Width * 4];

for (var y = 0; y < Height; y++)
{
var intersections = this.edges[y];

layers.Clear();

var x = 0;
var rowOffset = 0;

var color = layers.CurrentColor;

for (var i = 0; i < intersections.Count; i++)
{
var intersection = intersections[i];

var intersectionX = (int)(intersection.X + 0.5);

while (x < intersectionX && x < Width)
{
row[rowOffset + 0] = (byte)color.Red;
row[rowOffset + 1] = (byte)color.Green;
row[rowOffset + 2] = (byte)color.Blue;
row[rowOffset + 3] = (byte)color.Alpha;
rowOffset += 4;
x++;
}

color = layers.Add(intersection.Edge);
}

while (x < Width)
{
row[rowOffset + 0] = (byte)color.Red;
row[rowOffset + 1] = (byte)color.Green;
row[rowOffset + 2] = (byte)color.Blue;
row[rowOffset + 3] = (byte)color.Alpha;
rowOffset += 4;
x++;
}

yield return row;
}
}
}
}
38 changes: 38 additions & 0 deletions src/PdfToSvg/Drawing/Rasterization/Edge.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) PdfToSvg.NET contributors.
// https://github.com/dmester/pdftosvg.net
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Text;

namespace PdfToSvg.Drawing.Rasterization
{
internal struct Edge
{
public Edge(int polygonId, Point from, Point to, Rgba32Color color)
{
PolygonID = polygonId;
From = from;
To = to;
Color = color;
}

public Point From { get; private set; }
public Point To { get; private set; }

public int PolygonID { get; private set; }
public Rgba32Color Color { get; private set; }

public override string ToString()
{
return PolygonID + ": " + From + "; " + To;
}

public double Intersection(double y)
{
var dx = (To.X - From.X) * (From.Y - y) / (From.Y - To.Y);
return From.X + dx;
}
}
}
18 changes: 18 additions & 0 deletions src/PdfToSvg/Drawing/Rasterization/EdgeIntersectionRange.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) PdfToSvg.NET contributors.
// https://github.com/dmester/pdftosvg.net
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace PdfToSvg.Drawing.Rasterization
{
[DebuggerDisplay("{FromX} {Width}")]
internal struct EdgeIntersectionRange
{
public double X;
public Edge Edge;
}
}
150 changes: 150 additions & 0 deletions src/PdfToSvg/Drawing/Rasterization/EdgeTable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright (c) PdfToSvg.NET contributors.
// https://github.com/dmester/pdftosvg.net
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Text;

namespace PdfToSvg.Drawing.Rasterization
{
internal class EdgeTable
{
private List<EdgeIntersectionRange>[] scanlines;
private int width;

public EdgeTable(int width, int height)
{
if (width <= 0) throw new ArgumentOutOfRangeException(nameof(width));
if (height <= 0) throw new ArgumentOutOfRangeException(nameof(height));

scanlines = new List<EdgeIntersectionRange>[height];
this.width = width;
}

private class EdgeComparer : IComparer<EdgeIntersectionRange>
{
public int Compare(EdgeIntersectionRange x, EdgeIntersectionRange y)
{
if (x.X < y.X)
{
return -1;
}
if (x.X > y.X)
{
return 1;
}
return 0;
}
}

public void Add(Edge edge)
{
int minY, maxY;

if (edge.From.Y == edge.To.Y)
{
// Skip horizontal lines
return;
}

if (edge.From.X >= width && edge.To.X >= width)
{
// Skip edges entirely to the right of the viewport
return;
}

// Edges crossing the right side of the viewport need to
// be clipped.
if ((edge.From.X > width) ^ (edge.To.X > width))
{
var intersectingY = edge.From.Y +
(edge.To.Y - edge.From.Y) * (width - edge.From.X) / (edge.To.X - edge.From.X);

if (edge.From.X > width)
{
// Keep To-point
edge = new Edge(edge.PolygonID,
new Point(width, intersectingY), edge.To,
edge.Color);
}
else
{
// Keep From-point
edge = new Edge(edge.PolygonID,
edge.From, new Point(width, intersectingY),
edge.Color);
}
}

if (edge.From.Y < 0 && edge.To.Y < 0)
{
// Skip edges entirely above the viewport
return;
}

if (edge.From.Y >= scanlines.Length && edge.To.Y >= scanlines.Length)
{
// Skip edges entirely below the viewport
return;
}

// Determine lower and upper vertical bounds
if (edge.From.Y < edge.To.Y)
{
minY = (int)(edge.From.Y + 0.5);
maxY = (int)(edge.To.Y + 0.5);
}
else
{
minY = (int)(edge.To.Y + 0.5);
maxY = (int)(edge.From.Y + 0.5);
}

// We only need to add the edge to the visible scanlines
if (minY < 0)
{
minY = 0;
}
if (maxY > scanlines.Length)
{
maxY = scanlines.Length;
}

var y = minY;

while (y < maxY)
{
var x = edge.Intersection(y + 0.5f);

var scanline = scanlines[y];
if (scanline == null)
{
scanlines[y] = scanline = new List<EdgeIntersectionRange>();
}
scanline.Add(new EdgeIntersectionRange
{
X = x,
Edge = edge
});

y++;
}
}

public void Sort()
{
var comparer = new EdgeComparer();

for (var i = 0; i < scanlines.Length; i++)
{
scanlines[i]?.Sort(comparer);
}
}

public IList<EdgeIntersectionRange> this[int y]
{
get { return scanlines[y] ?? (IList<EdgeIntersectionRange>)new EdgeIntersectionRange[0]; }
}
}
}
Loading

0 comments on commit 1fc2cb1

Please sign in to comment.