diff --git a/Directory.Build.props b/Directory.Build.props index 12a4a5c2a..50c09fbb3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -31,21 +31,21 @@ - $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE;SUPPORTS_HOTPATH $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE diff --git a/src/ImageSharp/Common/Helpers/InliningOptions.cs b/src/ImageSharp/Common/Helpers/InliningOptions.cs index 069a426d7..895b6250f 100644 --- a/src/ImageSharp/Common/Helpers/InliningOptions.cs +++ b/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; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 4d7de4161..bc7b9d815 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -380,7 +380,12 @@ namespace SixLabors.ImageSharp.Formats.Png private void InitializeImage(ImageMetadata metadata, out Image image) where TPixel : unmanaged, IPixel { - image = new Image(this.configuration, this.header.Width, this.header.Height, metadata); + image = Image.CreateUninitialized( + this.configuration, + this.header.Width, + this.header.Height, + metadata); + this.bytesPerPixel = this.CalculateBytesPerPixel(); this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; this.bytesPerSample = 1; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 45e1ffd2d..fcbbc6697 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/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 outputBytes = new byte[keywordBytes.Length + textBytes.Length + translatedKeyword.Length + languageTag.Length + 5]; + Span 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 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 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()); } } diff --git a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs b/src/ImageSharp/Formats/Png/Zlib/Deflater.cs index 7398b089b..2083edab1 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs +++ b/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); } } } diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs index 1a8bb4ab0..c1c86a98b 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs +++ b/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 /// /// The current match. /// True if a match greater than the minimum length is found + [MethodImpl(InliningOptions.HotPath)] private bool FindLongestMatch(int curMatch) { int match; diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs index 543a1fe30..022d3d4d0 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs +++ b/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; - } - } - /// /// Initializes a new instance of the class. /// @@ -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 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 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 + /// /// 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. /// - private static ReadOnlySpan 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 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 Bit4Reverse => new byte[] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; + private static ReadOnlySpan Bit4Reverse => new byte[] + { + 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 + }; /// /// 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); } } } diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs index 731c9e80f..0414ca2f8 100644 --- a/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs +++ b/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); } } }