Skip to content

Commit

Permalink
Improve Code Quality (#831)
Browse files Browse the repository at this point in the history
* Introduce globals

* Spanify TransformationMatrix.FromArray

* Eliminate allocation in GeometryExtensions.ParametricPerpendicularProjection

* Eliminate allocation in CrossReferenceTablePart.Parse

* Optimize Adam7 (eliminate virtual calls)

* Spanify QuadPointsQuadrilateral.Points to eliminate virtual calls

* Eliminate allocation in PdfRectangle.Normalize

* Format TransformMatrix

* Pass TransformationMatrix  by reference in TransformationMatrix.Multiply

* Seal NoTextTokenWriter
  • Loading branch information
iamcarbon authored May 6, 2024
1 parent b6e0305 commit c6a7a2d
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 118 deletions.
2 changes: 2 additions & 0 deletions src/UglyToad.PdfPig.Core/Globals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;
103 changes: 38 additions & 65 deletions src/UglyToad.PdfPig.Core/TransformationMatrix.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
namespace UglyToad.PdfPig.Core
{
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using static UglyToad.PdfPig.Core.PdfSubpath;

/// <summary>
Expand Down Expand Up @@ -138,53 +135,27 @@ public static TransformationMatrix GetRotationMatrix(double degreesCounterclockw
throw new ArgumentOutOfRangeException(nameof(col), "Cannot access negative columns in a matrix.");
}

switch (row)
{
case 0:
{
switch (col)
{
case 0:
return A;
case 1:
return B;
case 2:
return row1;
default:
throw new ArgumentOutOfRangeException($"Trying to access {row}, {col} which was not in the value array.");
}
}
case 1:
{
switch (col)
{
case 0:
return C;
case 1:
return D;
case 2:
return row2;
default:
throw new ArgumentOutOfRangeException($"Trying to access {row}, {col} which was not in the value array.");
}
}
case 2:
{
switch (col)
{
case 0:
return E;
case 1:
return F;
case 2:
return row3;
default:
throw new ArgumentOutOfRangeException($"Trying to access {row}, {col} which was not in the value array.");
}
}
default:
throw new ArgumentOutOfRangeException($"Trying to access {row}, {col} which was not in the value array.");
}
return row switch {
0 => col switch {
0 => A,
1 => B,
2 => row1,
_ => throw new ArgumentOutOfRangeException($"Trying to access {row}, {col} which was not in the value array.")
},
1 => col switch {
0 => C,
1 => D,
2 => row2,
_ => throw new ArgumentOutOfRangeException($"Trying to access {row}, {col} which was not in the value array.")
},
2 => col switch {
0 => E,
1 => F,
2 => row3,
_ => throw new ArgumentOutOfRangeException($"Trying to access {row}, {col} which was not in the value array.")
},
_ => throw new ArgumentOutOfRangeException($"Trying to access {row}, {col} which was not in the value array.")
};
}
}

Expand All @@ -201,7 +172,7 @@ public static TransformationMatrix GetRotationMatrix(double degreesCounterclockw
/// Create a new <see cref="TransformationMatrix"/>.
/// </summary>
/// <param name="value">The 9 values of the matrix.</param>
public TransformationMatrix(double[] value) : this(value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], value[8])
public TransformationMatrix(ReadOnlySpan<double> value) : this(value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], value[8])
{
}

Expand Down Expand Up @@ -373,7 +344,7 @@ public static TransformationMatrix FromValues(double a, double b, double c, doub
/// </summary>
/// <param name="values">Either all 9 values of the matrix, 6 values in the default PDF order or the 4 values of the top left square.</param>
/// <returns></returns>
public static TransformationMatrix FromArray(double[] values)
public static TransformationMatrix FromArray(ReadOnlySpan<double> values)
{
if (values.Length == 9)
{
Expand All @@ -394,7 +365,7 @@ public static TransformationMatrix FromArray(double[] values)
0, 0, 1);
}

throw new ArgumentException("The array must either define all 9 elements of the matrix or all 6 key elements. Instead array was: " + values);
throw new ArgumentException("The array must either define all 9 elements of the matrix or all 6 key elements. Instead array was: " + string.Join(", ", values.ToArray()));
}

/// <summary>
Expand All @@ -403,7 +374,7 @@ public static TransformationMatrix FromArray(double[] values)
/// <param name="matrix">The matrix to multiply</param>
/// <returns>The resulting matrix.</returns>
[Pure]
public TransformationMatrix Multiply(TransformationMatrix matrix)
public TransformationMatrix Multiply(in TransformationMatrix matrix)
{
var a = (A * matrix.A) + (B * matrix.C) + (row1 * matrix.E);
var b = (A * matrix.B) + (B * matrix.D) + (row1 * matrix.F);
Expand Down Expand Up @@ -524,17 +495,19 @@ public static bool Equals(TransformationMatrix a, TransformationMatrix b)
/// <inheritdoc />
public override int GetHashCode()
{
var hashCode = 472622392;
hashCode = hashCode * -1521134295 + row1.GetHashCode();
hashCode = hashCode * -1521134295 + row2.GetHashCode();
hashCode = hashCode * -1521134295 + row3.GetHashCode();
hashCode = hashCode * -1521134295 + A.GetHashCode();
hashCode = hashCode * -1521134295 + B.GetHashCode();
hashCode = hashCode * -1521134295 + C.GetHashCode();
hashCode = hashCode * -1521134295 + D.GetHashCode();
hashCode = hashCode * -1521134295 + E.GetHashCode();
hashCode = hashCode * -1521134295 + F.GetHashCode();
return hashCode;
var hashCode = new HashCode();

hashCode.Add(row1);
hashCode.Add(row2);
hashCode.Add(row3);
hashCode.Add(A);
hashCode.Add(B);
hashCode.Add(C);
hashCode.Add(D);
hashCode.Add(E);
hashCode.Add(F);

return hashCode.ToHashCode();
}

/// <inheritdoc />
Expand Down
2 changes: 2 additions & 0 deletions src/UglyToad.PdfPig.DocumentLayoutAnalysis/Globals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;
2 changes: 2 additions & 0 deletions src/UglyToad.PdfPig.Fonts/Globals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;
2 changes: 2 additions & 0 deletions src/UglyToad.PdfPig.Tokenization/Globals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;
2 changes: 2 additions & 0 deletions src/UglyToad.PdfPig.Tokens/Globals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;
8 changes: 4 additions & 4 deletions src/UglyToad.PdfPig/Annotations/QuadPointsQuadrilateral.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
namespace UglyToad.PdfPig.Annotations
{
using System;
using System.Collections.Generic;
using Core;

/// <summary>
Expand All @@ -10,13 +8,15 @@
/// </summary>
public readonly struct QuadPointsQuadrilateral
{
private readonly PdfPoint[] points;

/// <summary>
/// The 4 points defining this quadrilateral.
/// The PDF specification defines these as being in anti-clockwise order starting from the lower-left corner, however
/// Adobe's implementation doesn't obey the specification and points seem to go in the order: top-left, top-right,
/// bottom-left, bottom-right. See: https://stackoverflow.com/questions/9855814/pdf-spec-vs-acrobat-creation-quadpoints.
/// </summary>
public IReadOnlyList<PdfPoint> Points { get; }
public ReadOnlySpan<PdfPoint> Points => points;

/// <summary>
/// Create a new <see cref="QuadPointsQuadrilateral"/>.
Expand All @@ -33,7 +33,7 @@ public QuadPointsQuadrilateral(PdfPoint[] points)
throw new ArgumentException($"Quadpoints quadrilateral should only contain 4 points, instead got {points.Length} points.");
}

Points = points;
this.points = points;
}

/// <inheritdoc />
Expand Down
31 changes: 19 additions & 12 deletions src/UglyToad.PdfPig/Geometry/GeometryExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
namespace UglyToad.PdfPig.Geometry
{
using Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Core;
using UglyToad.PdfPig.Geometry.ClipperLibrary;
using UglyToad.PdfPig.Graphics;
using static UglyToad.PdfPig.Core.PdfSubpath;
Expand Down Expand Up @@ -83,7 +81,7 @@ private static PdfRectangle ParametricPerpendicularProjection(IReadOnlyList<PdfP
return new PdfRectangle(polygon[0], polygon[1]);
}

double[] MBR = new double[8];
Span<double> mrb = stackalloc double[8];

double Amin = double.PositiveInfinity;
int j = 1;
Expand Down Expand Up @@ -169,7 +167,7 @@ private static PdfRectangle ParametricPerpendicularProjection(IReadOnlyList<PdfP
if (A < Amin)
{
Amin = A;
MBR = [R0X, R0Y, R1X, R1Y, R2X, R2Y, R3X, R3Y];
mrb = [R0X, R0Y, R1X, R1Y, R2X, R2Y, R3X, R3Y];
}
}

Expand All @@ -180,10 +178,10 @@ private static PdfRectangle ParametricPerpendicularProjection(IReadOnlyList<PdfP
if (k == polygon.Count) break;
}

return new PdfRectangle(new PdfPoint(MBR[4], MBR[5]),
new PdfPoint(MBR[6], MBR[7]),
new PdfPoint(MBR[2], MBR[3]),
new PdfPoint(MBR[0], MBR[1]));
return new PdfRectangle(new PdfPoint(mrb[4], mrb[5]),
new PdfPoint(mrb[6], mrb[7]),
new PdfPoint(mrb[2], mrb[3]),
new PdfPoint(mrb[0], mrb[1]));
}

/// <summary>
Expand Down Expand Up @@ -471,10 +469,19 @@ public static bool IntersectsWith(this PdfRectangle rectangle, PdfRectangle othe
/// Gets the axis-aligned rectangle that completely containing the original rectangle, with no rotation.
/// </summary>
/// <param name="rectangle"></param>
public static PdfRectangle Normalise(this PdfRectangle rectangle)
public static PdfRectangle Normalise(this in PdfRectangle rectangle)
{
var points = new[] { rectangle.BottomLeft, rectangle.BottomRight, rectangle.TopLeft, rectangle.TopRight };
return new PdfRectangle(points.Min(p => p.X), points.Min(p => p.Y), points.Max(p => p.X), points.Max(p => p.Y));
var bottomLeft = rectangle.BottomLeft;
var bottomRight = rectangle.BottomRight;
var topLeft = rectangle.TopLeft;
var topRight = rectangle.TopRight;

var minX = Math.Min(Math.Min(bottomLeft.X, bottomRight.X), Math.Min(topLeft.X, topRight.X));
var minY = Math.Min(Math.Min(bottomLeft.Y, bottomRight.Y), Math.Min(topLeft.Y, topRight.Y));
var maxX = Math.Max(Math.Max(bottomLeft.X, bottomRight.X), Math.Max(topLeft.X, topRight.X));
var maxY = Math.Max(Math.Max(bottomLeft.Y, bottomRight.Y), Math.Max(topLeft.Y, topRight.Y));

return new PdfRectangle(minX, minY, maxX, maxY);
}

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions src/UglyToad.PdfPig/Globals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;
50 changes: 23 additions & 27 deletions src/UglyToad.PdfPig/Images/Png/Adam7.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
namespace UglyToad.PdfPig.Images.Png
{
using System.Collections.Generic;

internal static class Adam7
{
/// <summary>
/// For a given pass number (1 indexed) the scanline indexes of the lines included in that pass in the 8x8 grid.
/// </summary>
private static readonly IReadOnlyDictionary<int, int[]> PassToScanlineGridIndex = new Dictionary<int, int[]>
{
{ 1, [0] },
{ 2, [0] },
{ 3, [4] },
{ 4, [0, 4] },
{ 5, [2, 6] },
{ 6, [0, 2, 4, 6] },
{ 7, [1, 3, 5, 7] }
};

private static readonly IReadOnlyDictionary<int, int[]> PassToScanlineColumnIndex = new Dictionary<int, int[]>
{
{ 1, [0] },
{ 2, [4] },
{ 3, [0, 4] },
{ 4, [2, 6] },
{ 5, [0, 2, 4, 6] },
{ 6, [1, 3, 5, 7] },
{ 7, [0, 1, 2, 3, 4, 5, 6, 7] }
};
private static readonly int[][] PassToScanlineGridIndex = [
[0],
[0],
[4],
[0, 4],
[2, 6],
[0, 2, 4, 6],
[1, 3, 5, 7]
];

private static readonly int[][] PassToScanlineColumnIndex = [
[0],
[4],
[0, 4],
[2, 6],
[0, 2, 4, 6],
[1, 3, 5, 7],
[0, 1, 2, 3, 4, 5, 6, 7]
];

/*
* To go from raw image data to interlaced:
Expand All @@ -50,7 +46,7 @@ internal static class Adam7

public static int GetNumberOfScanlinesInPass(ImageHeader header, int pass)
{
var indices = PassToScanlineGridIndex[pass + 1];
var indices = PassToScanlineGridIndex[pass];

var mod = header.Height % 8;

Expand All @@ -75,7 +71,7 @@ public static int GetNumberOfScanlinesInPass(ImageHeader header, int pass)

public static int GetPixelsPerScanlineInPass(ImageHeader header, int pass)
{
var indices = PassToScanlineColumnIndex[pass + 1];
var indices = PassToScanlineColumnIndex[pass];

var mod = header.Width % 8;

Expand All @@ -100,8 +96,8 @@ public static int GetPixelsPerScanlineInPass(ImageHeader header, int pass)

public static (int x, int y) GetPixelIndexForScanlineInPass(ImageHeader header, int pass, int scanlineIndex, int indexInScanline)
{
var columnIndices = PassToScanlineColumnIndex[pass + 1];
var rows = PassToScanlineGridIndex[pass + 1];
var columnIndices = PassToScanlineColumnIndex[pass];
var rows = PassToScanlineGridIndex[pass];

var actualRow = scanlineIndex % rows.Length;
var actualCol = indexInScanline % columnIndices.Length;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
namespace UglyToad.PdfPig.Parser.Parts.CrossReference
{
using System.Collections.Generic;
using Core;
using Filters;
using PdfPig.CrossReference;
Expand Down Expand Up @@ -45,7 +44,10 @@ public CrossReferenceTablePart Parse(long streamOffset, long? fromTableAtOffset,
var objectNumbers = GetObjectNumbers(stream.StreamDictionary);

var lineNumber = 0;
var lineBuffer = new byte[fieldSizes.LineLength];
Span<byte> lineBuffer = fieldSizes.LineLength <= 64
? stackalloc byte[fieldSizes.LineLength]
: new byte[fieldSizes.LineLength];

foreach (var objectNumber in objectNumbers)
{
if (lineNumber >= lineCount)
Expand Down Expand Up @@ -84,7 +86,7 @@ public CrossReferenceTablePart Parse(long streamOffset, long? fromTableAtOffset,
}

private static void ReadNextStreamObject(int type, long objectNumber, CrossReferenceStreamFieldSize fieldSizes,
CrossReferenceTablePartBuilder builder, byte[] lineBuffer)
CrossReferenceTablePartBuilder builder, ReadOnlySpan<byte> lineBuffer)
{
switch (type)
{
Expand Down
Loading

0 comments on commit c6a7a2d

Please sign in to comment.