Browse Source

Merge pull request #1170 from SixLabors/js/faster-deflate

Faster Deflate
pull/1178/head
James Jackson-South 6 years ago
committed by GitHub
parent
commit
424d3886a8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      Directory.Build.props
  2. 10
      src/ImageSharp/Common/Helpers/InliningOptions.cs
  3. 7
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  4. 29
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  5. 2
      src/ImageSharp/Formats/Png/Zlib/Deflater.cs
  6. 5
      src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs
  7. 125
      src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
  8. 2
      src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs

24
Directory.Build.props

@ -31,21 +31,21 @@
<!--
https://apisof.net/
+===================+=======+==========+=====================+=============+=================+====================+==============+
| SUPPORTS | MATHF | HASHCODE | EXTENDED_INTRINSICS | SPAN_STREAM | ENCODING_STRING | RUNTIME_INTRINSICS | CODECOVERAGE |
+===================+=======+==========+=====================+=============+=================+====================+==============+
| netcoreapp3.1 | Y | Y | Y | Y | Y | Y | Y |
| netcoreapp2.1 | Y | Y | Y | Y | Y | N | Y |
| netcoreapp2.0 | Y | N | N | N | N | N | Y |
| netstandard2.1 | Y | Y | N | Y | Y | N | Y |
| netstandard2.0 | N | N | N | N | N | N | Y |
| netstandard1.3 | N | N | N | N | N | N | N |
| net472 | N | N | Y | N | N | N | Y |
+===================+=======+==========+=====================+=============+=================+====================+==============+
+===================+=======+==========+=====================+=============+=================+====================+==============+=========+
| SUPPORTS | MATHF | HASHCODE | EXTENDED_INTRINSICS | SPAN_STREAM | ENCODING_STRING | RUNTIME_INTRINSICS | CODECOVERAGE | HOTPATH |
+===================+=======+==========+=====================+=============+=================+====================+==============+=========|
| netcoreapp3.1 | Y | Y | Y | Y | Y | Y | Y | Y |
| netcoreapp2.1 | Y | Y | Y | Y | Y | N | Y | N |
| netcoreapp2.0 | Y | N | N | N | N | N | Y | N |
| netstandard2.1 | Y | Y | N | Y | Y | N | Y | N |
| netstandard2.0 | N | N | N | N | N | N | Y | N |
| netstandard1.3 | N | N | N | N | N | N | N | N |
| net472 | N | N | Y | N | N | N | Y | N |
+===================+=======+==========+=====================+=============+=================+====================+==============+=========|
-->
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<DefineConstants>$(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE</DefineConstants>
<DefineConstants>$(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE;SUPPORTS_HOTPATH</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<DefineConstants>$(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE</DefineConstants>

10
src/ImageSharp/Common/Helpers/InliningOptions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// Uncomment this for verbose profiler results. DO NOT PUSH TO MAIN!
@ -13,10 +13,16 @@ namespace SixLabors.ImageSharp
internal static class InliningOptions
{
#if PROFILING
public const MethodImplOptions HotPath = MethodImplOptions.NoInlining;
public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining;
#else
#if SUPPORTS_HOTPATH
public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization;
#else
public const MethodImplOptions HotPath = MethodImplOptions.AggressiveInlining;
#endif
public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining;
#endif
public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining;
}
}
}

7
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -380,7 +380,12 @@ namespace SixLabors.ImageSharp.Formats.Png
private void InitializeImage<TPixel>(ImageMetadata metadata, out Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
image = new Image<TPixel>(this.configuration, this.header.Width, this.header.Height, metadata);
image = Image.CreateUninitialized<TPixel>(
this.configuration,
this.header.Width,
this.header.Height,
metadata);
this.bytesPerPixel = this.CalculateBytesPerPixel();
this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1;
this.bytesPerSample = 1;

29
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -5,10 +5,8 @@ using System;
using System.Buffers;
using System.Buffers.Binary;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Png.Chunks;
using SixLabors.ImageSharp.Formats.Png.Filters;
@ -16,7 +14,6 @@ using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Png
{
@ -633,10 +630,21 @@ namespace SixLabors.ImageSharp.Formats.Png
private void WriteTextChunks(Stream stream, PngMetadata meta)
{
const int MaxLatinCode = 255;
foreach (PngTextData textData in meta.TextData)
for (int i = 0; i < meta.TextData.Count; i++)
{
bool hasUnicodeCharacters = textData.Value.Any(c => c > MaxLatinCode);
if (hasUnicodeCharacters || (!string.IsNullOrWhiteSpace(textData.LanguageTag) || !string.IsNullOrWhiteSpace(textData.TranslatedKeyword)))
PngTextData textData = meta.TextData[i];
bool hasUnicodeCharacters = false;
foreach (var c in textData.Value)
{
if (c > MaxLatinCode)
{
hasUnicodeCharacters = true;
break;
}
}
if (hasUnicodeCharacters || (!string.IsNullOrWhiteSpace(textData.LanguageTag) ||
!string.IsNullOrWhiteSpace(textData.TranslatedKeyword)))
{
// Write iTXt chunk.
byte[] keywordBytes = PngConstants.Encoding.GetBytes(textData.Keyword);
@ -647,7 +655,8 @@ namespace SixLabors.ImageSharp.Formats.Png
byte[] translatedKeyword = PngConstants.TranslatedEncoding.GetBytes(textData.TranslatedKeyword);
byte[] languageTag = PngConstants.LanguageEncoding.GetBytes(textData.LanguageTag);
Span<byte> outputBytes = new byte[keywordBytes.Length + textBytes.Length + translatedKeyword.Length + languageTag.Length + 5];
Span<byte> outputBytes = new byte[keywordBytes.Length + textBytes.Length +
translatedKeyword.Length + languageTag.Length + 5];
keywordBytes.CopyTo(outputBytes);
if (textData.Value.Length > this.options.TextCompressionThreshold)
{
@ -667,7 +676,8 @@ namespace SixLabors.ImageSharp.Formats.Png
if (textData.Value.Length > this.options.TextCompressionThreshold)
{
// Write zTXt chunk.
byte[] compressedData = this.GetCompressedTextBytes(PngConstants.Encoding.GetBytes(textData.Value));
byte[] compressedData =
this.GetCompressedTextBytes(PngConstants.Encoding.GetBytes(textData.Value));
Span<byte> outputBytes = new byte[textData.Keyword.Length + compressedData.Length + 2];
PngConstants.Encoding.GetBytes(textData.Keyword).CopyTo(outputBytes);
compressedData.CopyTo(outputBytes.Slice(textData.Keyword.Length + 2));
@ -678,7 +688,8 @@ namespace SixLabors.ImageSharp.Formats.Png
// Write tEXt chunk.
Span<byte> outputBytes = new byte[textData.Keyword.Length + textData.Value.Length + 1];
PngConstants.Encoding.GetBytes(textData.Keyword).CopyTo(outputBytes);
PngConstants.Encoding.GetBytes(textData.Value).CopyTo(outputBytes.Slice(textData.Keyword.Length + 1));
PngConstants.Encoding.GetBytes(textData.Value)
.CopyTo(outputBytes.Slice(textData.Keyword.Length + 1));
this.WriteChunk(stream, PngChunkType.Text, outputBytes.ToArray());
}
}

2
src/ImageSharp/Formats/Png/Zlib/Deflater.cs

@ -288,8 +288,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.engine = null;
this.isDisposed = true;
}
GC.SuppressFinalize(this);
}
}
}

5
src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs

@ -362,7 +362,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
more = this.inputEnd - this.inputOff;
}
Array.Copy(this.inputBuf, this.inputOff, this.window, this.strstart + this.lookahead, more);
Buffer.BlockCopy(this.inputBuf, this.inputOff, this.window, this.strstart + this.lookahead, more);
this.inputOff += more;
this.lookahead += more;
@ -397,8 +397,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.isDisposed = true;
}
GC.SuppressFinalize(this);
}
[MethodImpl(InliningOptions.ShortMethod)]
@ -464,6 +462,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// </summary>
/// <param name="curMatch">The current match.</param>
/// <returns>True if a match greater than the minimum length is found</returns>
[MethodImpl(InliningOptions.HotPath)]
private bool FindLongestMatch(int curMatch)
{
int match;

125
src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs

@ -36,11 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private const int EofSymbol = 256;
private static readonly short[] StaticLCodes;
private static readonly byte[] StaticLLength;
private static readonly short[] StaticDCodes;
private static readonly byte[] StaticDLength;
private Tree literalTree;
private Tree distTree;
private Tree blTree;
@ -58,49 +53,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private int extraBits;
private bool isDisposed;
// TODO: These should be pre-generated array/readonlyspans.
static DeflaterHuffman()
{
// See RFC 1951 3.2.6
// Literal codes
StaticLCodes = new short[LiteralNumber];
StaticLLength = new byte[LiteralNumber];
int i = 0;
while (i < 144)
{
StaticLCodes[i] = BitReverse((0x030 + i) << 8);
StaticLLength[i++] = 8;
}
while (i < 256)
{
StaticLCodes[i] = BitReverse((0x190 - 144 + i) << 7);
StaticLLength[i++] = 9;
}
while (i < 280)
{
StaticLCodes[i] = BitReverse((0x000 - 256 + i) << 9);
StaticLLength[i++] = 7;
}
while (i < LiteralNumber)
{
StaticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8);
StaticLLength[i++] = 8;
}
// Distance codes
StaticDCodes = new short[DistanceNumber];
StaticDLength = new byte[DistanceNumber];
for (i = 0; i < DistanceNumber; i++)
{
StaticDCodes[i] = BitReverse(i << 11);
StaticDLength[i] = 5;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="DeflaterHuffman"/> class.
/// </summary>
@ -122,12 +74,80 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.pinnedLiteralBuffer = (short*)this.literalBufferHandle.Pointer;
}
#pragma warning disable SA1201 // Elements should appear in the correct order
// See RFC 1951 3.2.6
// Literal codes
private static readonly short[] StaticLCodes = new short[]
{
12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 9,
137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5,
133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13,
141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 19,
275, 147, 403, 83, 339, 211, 467, 51, 307, 179, 435, 115, 371, 243, 499,
11, 267, 139, 395, 75, 331, 203, 459, 43, 299, 171, 427, 107, 363, 235, 491,
27, 283, 155, 411, 91, 347, 219, 475, 59, 315, 187, 443, 123, 379, 251, 507,
7, 263, 135, 391, 71, 327, 199, 455, 39, 295, 167, 423, 103, 359, 231, 487,
23, 279, 151, 407, 87, 343, 215, 471, 55, 311, 183, 439, 119, 375, 247, 503,
15, 271, 143, 399, 79, 335, 207, 463, 47, 303, 175, 431, 111, 367, 239, 495,
31, 287, 159, 415, 95, 351, 223, 479, 63, 319, 191, 447, 127, 383, 255, 511,
0, 64, 32, 96, 16, 80, 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, 4, 68, 36,
100, 20, 84, 52, 116, 3, 131, 67, 195, 35, 163
};
private static ReadOnlySpan<byte> StaticLLength => new byte[]
{
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8
};
// Distance codes and lengths.
private static readonly short[] StaticDCodes = new short[]
{
0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14,
30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23
};
private static ReadOnlySpan<byte> StaticDLength => new byte[]
{
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
};
#pragma warning restore SA1201 // Elements should appear in the correct order
/// <summary>
/// Gets the lengths of the bit length codes are sent in order of decreasing probability, to avoid transmitting the lengths for unused bit length codes.
/// </summary>
private static ReadOnlySpan<byte> BitLengthOrder => new byte[] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
private static ReadOnlySpan<byte> BitLengthOrder => new byte[]
{
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
private static ReadOnlySpan<byte> Bit4Reverse => new byte[] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
private static ReadOnlySpan<byte> Bit4Reverse => new byte[]
{
0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
};
/// <summary>
/// Gets the pending buffer to use.
@ -413,8 +433,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.distTree = null;
this.isDisposed = true;
}
GC.SuppressFinalize(this);
}
[MethodImpl(InliningOptions.ShortMethod)]
@ -553,6 +571,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
}
[MethodImpl(InliningOptions.HotPath)]
public void BuildTree()
{
int numSymbols = this.elementCount;
@ -964,8 +983,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.isDisposed = true;
}
GC.SuppressFinalize(this);
}
}
}

2
src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs

@ -172,8 +172,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.bufferMemoryOwner = null;
this.isDisposed = true;
}
GC.SuppressFinalize(this);
}
}
}

Loading…
Cancel
Save