mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-15 19:54:52 +08:00
#47 improve flate filter performance by streaming all data in single operation
also improves page constructor performance by removing linq and invoking stringbuilder directly. removes page rotation overhead by skipping multiplication for non-rotated pages and using cached transformation matrices for rotations. removes linq from filter provider and shares instances of filter types.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Annotations;
|
||||
using Graphics.Operations;
|
||||
using Tokens;
|
||||
@@ -104,7 +104,13 @@
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return string.Join(string.Empty, content.Letters.Select(x => x.Value));
|
||||
var builder = new StringBuilder();
|
||||
for (var i = 0; i < content.Letters.Count; i++)
|
||||
{
|
||||
builder.Append(content.Letters[i].Value);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -9,6 +9,10 @@
|
||||
/// </summary>
|
||||
public struct PageRotationDegrees : IEquatable<PageRotationDegrees>
|
||||
{
|
||||
private static readonly TransformationMatrix Rotate90 = TransformationMatrix.FromArray(new[] {0m, -1, 1, 0});
|
||||
private static readonly TransformationMatrix Rotate180 = TransformationMatrix.FromArray(new[] { -1m, 0, 0, -1 });
|
||||
private static readonly TransformationMatrix Rotate270 = TransformationMatrix.FromArray(new[] { 0m, 1, -1, 0 });
|
||||
|
||||
/// <summary>
|
||||
/// The rotation of the page in degrees clockwise.
|
||||
/// </summary>
|
||||
@@ -54,26 +58,19 @@
|
||||
[Pure]
|
||||
internal TransformationMatrix Rotate(TransformationMatrix matrix)
|
||||
{
|
||||
TransformationMatrix thisMatrix;
|
||||
switch (Value)
|
||||
{
|
||||
case 0:
|
||||
thisMatrix = TransformationMatrix.FromArray(new[]{ 1m, 0, 0, 1 });
|
||||
break;
|
||||
return matrix;
|
||||
case 90:
|
||||
thisMatrix = TransformationMatrix.FromArray(new[] {0m, -1, 1, 0});
|
||||
break;
|
||||
return Rotate90.Multiply(matrix);
|
||||
case 180:
|
||||
thisMatrix = TransformationMatrix.FromArray(new[] {-1m, 0, 0, -1});
|
||||
break;
|
||||
return Rotate180.Multiply(matrix);
|
||||
case 270:
|
||||
thisMatrix = TransformationMatrix.FromArray(new[] {0m, 1, -1, 0});
|
||||
break;
|
||||
return Rotate270.Multiply(matrix);
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid value for rotation: {Value}.");
|
||||
}
|
||||
|
||||
return thisMatrix.Multiply(matrix);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@@ -76,6 +76,7 @@
|
||||
try
|
||||
{
|
||||
using (var memoryStream = new MemoryStream(input))
|
||||
using (var output = new MemoryStream())
|
||||
{
|
||||
// The first 2 bytes are the header which DeflateStream does not support.
|
||||
memoryStream.ReadByte();
|
||||
@@ -83,23 +84,8 @@
|
||||
|
||||
using (var deflate = new DeflateStream(memoryStream, CompressionMode.Decompress))
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
|
||||
var x = deflate.ReadByte();
|
||||
while (x != -1)
|
||||
{
|
||||
bytes.Add((byte)x);
|
||||
x = deflate.ReadByte();
|
||||
}
|
||||
|
||||
var result = new byte[bytes.Count];
|
||||
|
||||
for (var i = 0; i < bytes.Count; i++)
|
||||
{
|
||||
result[i] = bytes[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
deflate.CopyTo(output);
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,35 +2,35 @@
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Exceptions;
|
||||
using Logging;
|
||||
using Tokens;
|
||||
using Util;
|
||||
|
||||
internal class MemoryFilterProvider : IFilterProvider
|
||||
{
|
||||
private readonly IReadOnlyDictionary<string, Func<IFilter>> filterFactories;
|
||||
private readonly IReadOnlyDictionary<string, IFilter> filterInstances;
|
||||
|
||||
public MemoryFilterProvider(IDecodeParameterResolver decodeParameterResolver, IPngPredictor pngPredictor, ILog log)
|
||||
{
|
||||
IFilter Ascii85Func() => new Ascii85Filter();
|
||||
IFilter AsciiHexFunc() => new AsciiHexDecodeFilter();
|
||||
IFilter FlateFunc() => new FlateFilter(decodeParameterResolver, pngPredictor, log);
|
||||
IFilter RunLengthFunc() => new RunLengthFilter();
|
||||
IFilter LzwFunc() => new LzwFilter(decodeParameterResolver, pngPredictor);
|
||||
var ascii85 = new Ascii85Filter();
|
||||
var asciiHex = new AsciiHexDecodeFilter();
|
||||
var flate = new FlateFilter(decodeParameterResolver, pngPredictor, log);
|
||||
var runLength = new RunLengthFilter();
|
||||
var lzw = new LzwFilter(decodeParameterResolver, pngPredictor);
|
||||
|
||||
filterFactories = new Dictionary<string, Func<IFilter>>
|
||||
filterInstances = new Dictionary<string, IFilter>
|
||||
{
|
||||
{NameToken.Ascii85Decode.Data, Ascii85Func},
|
||||
{NameToken.Ascii85DecodeAbbreviation.Data, Ascii85Func},
|
||||
{NameToken.AsciiHexDecode.Data, AsciiHexFunc},
|
||||
{NameToken.AsciiHexDecodeAbbreviation.Data, AsciiHexFunc},
|
||||
{NameToken.FlateDecode.Data, FlateFunc},
|
||||
{NameToken.FlateDecodeAbbreviation.Data, FlateFunc},
|
||||
{NameToken.RunLengthDecode.Data, RunLengthFunc},
|
||||
{NameToken.RunLengthDecodeAbbreviation.Data, RunLengthFunc},
|
||||
{NameToken.LzwDecode, LzwFunc},
|
||||
{NameToken.LzwDecodeAbbreviation, LzwFunc}
|
||||
{NameToken.Ascii85Decode.Data, ascii85},
|
||||
{NameToken.Ascii85DecodeAbbreviation.Data, ascii85},
|
||||
{NameToken.AsciiHexDecode.Data, asciiHex},
|
||||
{NameToken.AsciiHexDecodeAbbreviation.Data, asciiHex},
|
||||
{NameToken.FlateDecode.Data, flate},
|
||||
{NameToken.FlateDecodeAbbreviation.Data, flate},
|
||||
{NameToken.RunLengthDecode.Data, runLength},
|
||||
{NameToken.RunLengthDecodeAbbreviation.Data, runLength},
|
||||
{NameToken.LzwDecode, lzw},
|
||||
{NameToken.LzwDecodeAbbreviation, lzw}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,14 +43,21 @@
|
||||
|
||||
if (!dictionary.TryGet(NameToken.Filter, out var token))
|
||||
{
|
||||
return new IFilter[0];
|
||||
return EmptyArray<IFilter>.Instance;
|
||||
}
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case ArrayToken filters:
|
||||
// TODO: presumably this may be invalid...
|
||||
return filters.Data.Select(x => GetFilterStrict(((NameToken)x).Data)).ToList();
|
||||
var result = new IFilter[filters.Data.Count];
|
||||
for (var i = 0; i < filters.Data.Count; i++)
|
||||
{
|
||||
var filterToken = filters.Data[i];
|
||||
var filterName = ((NameToken) filterToken).Data;
|
||||
result[i] = GetFilterStrict(filterName);
|
||||
}
|
||||
|
||||
return result;
|
||||
case NameToken name:
|
||||
return new[] { GetFilterStrict(name.Data) };
|
||||
default:
|
||||
@@ -60,12 +67,12 @@
|
||||
|
||||
private IFilter GetFilterStrict(string name)
|
||||
{
|
||||
if (!filterFactories.TryGetValue(name, out var factory))
|
||||
if (!filterInstances.TryGetValue(name, out var factory))
|
||||
{
|
||||
throw new NotSupportedException($"The filter with the name {name} is not supported yet. Please raise an issue.");
|
||||
}
|
||||
|
||||
return factory();
|
||||
return factory;
|
||||
}
|
||||
|
||||
public IReadOnlyList<IFilter> GetAllFilters()
|
||||
|
Reference in New Issue
Block a user