Improve Code Quality (#831)

* 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
This commit is contained in:
Jason Nelson 2024-05-05 23:38:06 -07:00 committed by GitHub
parent b6e0305a1c
commit c6a7a2d0a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 104 additions and 117 deletions

View File

@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;

View File

@ -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>
@ -138,53 +135,27 @@
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.")
};
}
}
@ -201,7 +172,7 @@
/// 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])
{
}
@ -373,7 +344,7 @@
/// </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)
{
@ -394,7 +365,7 @@
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>
@ -403,7 +374,7 @@
/// <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);
@ -524,17 +495,19 @@
/// <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 />

View File

@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;

View File

@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;

View File

@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;

View File

@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;

View File

@ -1,7 +1,5 @@
namespace UglyToad.PdfPig.Annotations
{
using System;
using System.Collections.Generic;
using Core;
/// <summary>
@ -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"/>.
@ -33,7 +33,7 @@
throw new ArgumentException($"Quadpoints quadrilateral should only contain 4 points, instead got {points.Length} points.");
}
Points = points;
this.points = points;
}
/// <inheritdoc />

View File

@ -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;
@ -83,7 +81,7 @@
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;
@ -169,7 +167,7 @@
if (A < Amin)
{
Amin = A;
MBR = [R0X, R0Y, R1X, R1Y, R2X, R2Y, R3X, R3Y];
mrb = [R0X, R0Y, R1X, R1Y, R2X, R2Y, R3X, R3Y];
}
}
@ -180,10 +178,10 @@
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>
@ -471,10 +469,19 @@
/// 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>

View File

@ -0,0 +1,2 @@
global using System;
global using System.Collections.Generic;

View File

@ -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 int[][] PassToScanlineGridIndex = [
[0],
[0],
[4],
[0, 4],
[2, 6],
[0, 2, 4, 6],
[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[][] 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:
@ -50,7 +46,7 @@
public static int GetNumberOfScanlinesInPass(ImageHeader header, int pass)
{
var indices = PassToScanlineGridIndex[pass + 1];
var indices = PassToScanlineGridIndex[pass];
var mod = header.Height % 8;
@ -75,7 +71,7 @@
public static int GetPixelsPerScanlineInPass(ImageHeader header, int pass)
{
var indices = PassToScanlineColumnIndex[pass + 1];
var indices = PassToScanlineColumnIndex[pass];
var mod = header.Width % 8;
@ -100,8 +96,8 @@
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;

View File

@ -1,6 +1,5 @@
namespace UglyToad.PdfPig.Parser.Parts.CrossReference
{
using System.Collections.Generic;
using Core;
using Filters;
using PdfPig.CrossReference;
@ -45,7 +44,10 @@
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)
@ -84,7 +86,7 @@
}
private static void ReadNextStreamObject(int type, long objectNumber, CrossReferenceStreamFieldSize fieldSizes,
CrossReferenceTablePartBuilder builder, byte[] lineBuffer)
CrossReferenceTablePartBuilder builder, ReadOnlySpan<byte> lineBuffer)
{
switch (type)
{

View File

@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using UglyToad.PdfPig.Core;
using UglyToad.PdfPig.Filters;
using UglyToad.PdfPig.Graphics.Operations.TextShowing;
using UglyToad.PdfPig.Graphics.Operations;
using UglyToad.PdfPig.Graphics;
using UglyToad.PdfPig.Graphics.Operations;
using UglyToad.PdfPig.Graphics.Operations.TextShowing;
using UglyToad.PdfPig.Logging;
using UglyToad.PdfPig.Parser;
using UglyToad.PdfPig.Tokens;
@ -17,7 +14,7 @@ namespace UglyToad.PdfPig.Writer
/// <summary>
/// Derived class of <see cref="TokenWriter"/> that does not write <see cref="ShowText"/> or <see cref="ShowTextsWithPositioning"/> operations in streams
/// </summary>
internal class NoTextTokenWriter : TokenWriter
internal sealed class NoTextTokenWriter : TokenWriter
{
/// <summary>
/// Set this value prior to processing page to get the right page number in log messages