From 28facaf98dfe5059a4ee140b84c13c8ba07968d2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 10 Apr 2018 21:42:58 +1000 Subject: [PATCH] Faster scan decoder --- .../Components/FixedByteBuffer256.cs | 24 +++ .../Components/FixedInt16Buffer18.cs | 24 +++ .../Components/FixedInt16Buffer256.cs | 24 +++ .../Components/FixedInt64Buffer18.cs | 24 +++ .../PdfJsPort/Components/PdfJsHuffmanTable.cs | 176 +++++++----------- .../Components/PdfJsHuffmanTables.cs | 14 +- .../PdfJsPort/Components/PdfJsScanDecoder.cs | 132 +++++++------ .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 14 +- 8 files changed, 245 insertions(+), 187 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs create mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer18.cs create mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer256.cs create mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt64Buffer18.cs diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs new file mode 100644 index 000000000..5870e3da8 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct FixedByteBuffer256 + { + public fixed byte Data[256]; + + public byte this[int idx] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ref byte self = ref Unsafe.As(ref this); + return Unsafe.Add(ref self, idx); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer18.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer18.cs new file mode 100644 index 000000000..20d4b7733 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer18.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct FixedInt16Buffer18 + { + public fixed short Data[18]; + + public short this[int idx] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ref short self = ref Unsafe.As(ref this); + return Unsafe.Add(ref self, idx); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer256.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer256.cs new file mode 100644 index 000000000..2c16a918f --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer256.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct FixedInt16Buffer256 + { + public fixed short Data[256]; + + public short this[int idx] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ref short self = ref Unsafe.As(ref this); + return Unsafe.Add(ref self, idx); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt64Buffer18.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt64Buffer18.cs new file mode 100644 index 000000000..51381cb27 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt64Buffer18.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct FixedInt64Buffer18 + { + public fixed long Data[18]; + + public long this[int idx] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ref long self = ref Unsafe.As(ref this); + return Unsafe.Add(ref self, idx); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs index 3c43ba244..1958de7c6 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components @@ -10,92 +9,52 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// Represents a Huffman Table /// - internal struct PdfJsHuffmanTable : IDisposable + internal unsafe struct PdfJsHuffmanTable { - private BasicArrayBuffer lookahead; - private BasicArrayBuffer valOffset; - private BasicArrayBuffer maxcode; - private IManagedByteBuffer huffval; - - /// - /// Initializes a new instance of the struct. - /// - /// The to use for buffer allocations. - /// The code lengths - /// The huffman values - public PdfJsHuffmanTable(MemoryManager memoryManager, byte[] lengths, byte[] values) - { - // TODO: Replace FakeBuffer usages with standard or array orfixed-sized arrays - this.lookahead = memoryManager.AllocateFake(256); - this.valOffset = memoryManager.AllocateFake(18); - this.maxcode = memoryManager.AllocateFake(18); - - using (IBuffer huffsize = memoryManager.Allocate(257)) - using (IBuffer huffcode = memoryManager.Allocate(257)) - { - GenerateSizeTable(lengths, huffsize.Span); - GenerateCodeTable(huffsize.Span, huffcode.Span); - GenerateDecoderTables(lengths, huffcode.Span, this.valOffset.Span, this.maxcode.Span); - GenerateLookaheadTables(lengths, values, this.lookahead.Span); - } - - this.huffval = memoryManager.AllocateManagedByteBuffer(values.Length, true); - Buffer.BlockCopy(values, 0, this.huffval.Array, 0, values.Length); - - this.MaxCode = this.maxcode.Array; - this.ValOffset = this.valOffset.Array; - this.HuffVal = this.huffval.Array; - this.Lookahead = this.lookahead.Array; - } - /// /// Gets the max code array /// - public long[] MaxCode - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - } + public FixedInt64Buffer18 MaxCode; /// /// Gets the value offset array /// - public short[] ValOffset - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - } + public FixedInt16Buffer18 ValOffset; /// /// Gets the huffman value array /// - public byte[] HuffVal - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - } + public FixedByteBuffer256 HuffVal; /// /// Gets the lookahead array /// - public short[] Lookahead - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - } + public FixedInt16Buffer256 Lookahead; - /// - public void Dispose() + /// + /// Initializes a new instance of the struct. + /// + /// The to use for buffer allocations. + /// The code lengths + /// The huffman values + public PdfJsHuffmanTable(MemoryManager memoryManager, byte[] lengths, byte[] values) { - this.lookahead?.Dispose(); - this.valOffset?.Dispose(); - this.maxcode?.Dispose(); - this.huffval?.Dispose(); - - this.lookahead = null; - this.valOffset = null; - this.maxcode = null; - this.huffval = null; + using (IBuffer huffsize = memoryManager.Allocate(257)) + using (IBuffer huffcode = memoryManager.Allocate(257)) + { + GenerateSizeTable(lengths, huffsize.Span); + GenerateCodeTable(huffsize.Span, huffcode.Span); + this.GenerateDecoderTables(lengths, huffcode.Span); + this.GenerateLookaheadTables(lengths, values); + } + + fixed (byte* huffValRef = this.HuffVal.Data) + { + for (int i = 0; i < values.Length; i++) + { + huffValRef[i] = values[i]; + } + } } /// @@ -148,29 +107,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// The code lengths /// The huffman code span - /// The value offset span - /// The max code span - private static void GenerateDecoderTables(byte[] lengths, Span huffcode, Span valOffset, Span maxcode) + private void GenerateDecoderTables(byte[] lengths, Span huffcode) { - short bitcount = 0; - for (int i = 1; i <= 16; i++) + fixed (short* valOffsetRef = this.ValOffset.Data) + fixed (long* maxcodeRef = this.MaxCode.Data) { - if (lengths[i] != 0) - { - // valoffset[l] = huffval[] index of 1st symbol of code length i, - // minus the minimum code of length i - valOffset[i] = (short)(bitcount - huffcode[bitcount]); - bitcount += lengths[i]; - maxcode[i] = huffcode[bitcount - 1]; // maximum code of length i - } - else + short bitcount = 0; + for (int i = 1; i <= 16; i++) { - maxcode[i] = -1; // -1 if no codes of this length + if (lengths[i] != 0) + { + // valOffsetRef[l] = huffval[] index of 1st symbol of code length i, minus the minimum code of length i + valOffsetRef[i] = (short)(bitcount - huffcode[bitcount]); + bitcount += lengths[i]; + maxcodeRef[i] = huffcode[bitcount - 1]; // maximum code of length i + } + else + { + maxcodeRef[i] = -1; // -1 if no codes of this length + } } - } - valOffset[17] = 0; - maxcode[17] = 0xFFFFFL; + valOffsetRef[17] = 0; + maxcodeRef[17] = 0xFFFFFL; + } } /// @@ -178,32 +138,34 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// The code lengths /// The huffman value array - /// The lookahead span - private static void GenerateLookaheadTables(byte[] lengths, byte[] huffval, Span lookahead) + private void GenerateLookaheadTables(byte[] lengths, byte[] huffval) { - int x = 0, code = 0; - - for (int i = 0; i < 8; i++) + fixed (short* lookaheadRef = this.Lookahead.Data) { - code <<= 1; + int x = 0, code = 0; - for (int j = 0; j < lengths[i + 1]; j++) + for (int i = 0; i < 8; i++) { - // The codeLength is 1+i, so shift code by 8-(1+i) to - // calculate the high bits for every 8-bit sequence - // whose codeLength's high bits matches code. - // The high 8 bits of lutValue are the encoded value. - // The low 8 bits are 1 plus the codeLength. - byte base2 = (byte)(code << (7 - i)); - short lutValue = (short)((short)(huffval[x] << 8) | (short)(2 + i)); - - for (int k = 0; k < 1 << (7 - i); k++) + code <<= 1; + + for (int j = 0; j < lengths[i + 1]; j++) { - lookahead[base2 | k] = lutValue; + // The codeLength is 1+i, so shift code by 8-(1+i) to + // calculate the high bits for every 8-bit sequence + // whose codeLength's high bits matches code. + // The high 8 bits of lutValue are the encoded value. + // The low 8 bits are 1 plus the codeLength. + byte base2 = (byte)(code << (7 - i)); + short lutValue = (short)((short)(huffval[x] << 8) | (short)(2 + i)); + + for (int k = 0; k < 1 << (7 - i); k++) + { + lookaheadRef[base2 | k] = lutValue; + } + + code++; + x++; } - - code++; - x++; } } } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs index 5d59809cc..0fd6d76b3 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs @@ -1,16 +1,15 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components { /// - /// Defines a pair of huffman tables + /// Defines a 2 pairs of huffman tables /// - internal sealed class PdfJsHuffmanTables : IDisposable + internal sealed class PdfJsHuffmanTables { private readonly PdfJsHuffmanTable[] tables = new PdfJsHuffmanTable[4]; @@ -27,14 +26,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components return ref this.tables[index]; } } - - /// - public void Dispose() - { - for (int i = 0; i < this.tables.Length; i++) - { - this.tables[i].Dispose(); - } - } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs index c6f6ac270..43e3ef435 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components { @@ -172,7 +173,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream); // Some images include more Scan blocks than expected, skip past those and - // attempt to find the next valid marker (fixes issue8182.pdf) in original code. + // attempt to find the next valid marker (fixes issue8182.pdf) ref original code. if (fileMarker.Invalid) { #if DEBUG @@ -201,6 +202,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components if (componentsLength == 1) { PdfJsFrameComponent component = components[this.compIndex]; + ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span); ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; @@ -211,7 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; } - this.DecodeBlockBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, mcu, stream); + this.DecodeBlockBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, ref blockDataRef, mcu, stream); mcu++; } } @@ -222,6 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components for (int i = 0; i < componentsLength; i++) { PdfJsFrameComponent component = components[i]; + ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span); ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; int h = component.HorizontalSamplingFactor; @@ -236,7 +239,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; } - this.DecodeMcuBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + this.DecodeMcuBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, ref blockDataRef, mcusPerLine, mcu, j, k, stream); } } } @@ -259,6 +262,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components if (componentsLength == 1) { PdfJsFrameComponent component = components[this.compIndex]; + ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span); ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; for (int n = 0; n < mcuToRead; n++) @@ -268,7 +272,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; } - this.DecodeBlockDCFirst(ref dcHuffmanTable, component, mcu, stream); + this.DecodeBlockDCFirst(ref dcHuffmanTable, component, ref blockDataRef, mcu, stream); mcu++; } } @@ -279,6 +283,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components for (int i = 0; i < componentsLength; i++) { PdfJsFrameComponent component = components[i]; + ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span); ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -292,7 +297,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; } - this.DecodeMcuDCFirst(ref dcHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + this.DecodeMcuDCFirst(ref dcHuffmanTable, component, ref blockDataRef, mcusPerLine, mcu, j, k, stream); } } } @@ -314,6 +319,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components if (componentsLength == 1) { PdfJsFrameComponent component = components[this.compIndex]; + ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span); + for (int n = 0; n < mcuToRead; n++) { if (this.endOfStreamReached || this.unexpectedMarkerReached) @@ -321,7 +328,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; } - this.DecodeBlockDCSuccessive(component, mcu, stream); + this.DecodeBlockDCSuccessive(component, ref blockDataRef, mcu, stream); mcu++; } } @@ -334,6 +341,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components PdfJsFrameComponent component = components[i]; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; + ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span); + for (int j = 0; j < v; j++) { for (int k = 0; k < h; k++) @@ -343,7 +352,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; } - this.DecodeMcuDCSuccessive(component, mcusPerLine, mcu, j, k, stream); + this.DecodeMcuDCSuccessive(component, ref blockDataRef, mcusPerLine, mcu, j, k, stream); } } } @@ -366,6 +375,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components if (componentsLength == 1) { PdfJsFrameComponent component = components[this.compIndex]; + ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span); ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; for (int n = 0; n < mcuToRead; n++) @@ -375,7 +385,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; } - this.DecodeBlockACFirst(ref acHuffmanTable, component, mcu, stream); + this.DecodeBlockACFirst(ref acHuffmanTable, component, ref blockDataRef, mcu, stream); mcu++; } } @@ -386,6 +396,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components for (int i = 0; i < componentsLength; i++) { PdfJsFrameComponent component = components[i]; + ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span); ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -399,7 +410,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; } - this.DecodeMcuACFirst(ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + this.DecodeMcuACFirst(ref acHuffmanTable, component, ref blockDataRef, mcusPerLine, mcu, j, k, stream); } } } @@ -422,6 +433,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components if (componentsLength == 1) { PdfJsFrameComponent component = components[this.compIndex]; + ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span); ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; for (int n = 0; n < mcuToRead; n++) @@ -431,7 +443,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; } - this.DecodeBlockACSuccessive(ref acHuffmanTable, component, mcu, stream); + this.DecodeBlockACSuccessive(ref acHuffmanTable, component, ref blockDataRef, mcu, stream); mcu++; } } @@ -442,6 +454,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components for (int i = 0; i < componentsLength; i++) { PdfJsFrameComponent component = components[i]; + ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span); ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -455,7 +468,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; } - this.DecodeMcuACSuccessive(ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + this.DecodeMcuACSuccessive(ref acHuffmanTable, component, ref blockDataRef, mcusPerLine, mcu, j, k, stream); } } } @@ -466,103 +479,103 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBlockBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + private void DecodeBlockBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcu, Stream stream) { int blockRow = mcu / component.WidthInBlocks; int blockCol = mcu % component.WidthInBlocks; int offset = component.GetBlockBufferOffset(blockRow, blockCol); - this.DecodeBaseline(component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream); + this.DecodeBaseline(component, ref blockDataRef, offset, ref dcHuffmanTable, ref acHuffmanTable, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeMcuBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + private void DecodeMcuBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcusPerLine, int mcu, int row, int col, Stream stream) { int mcuRow = mcu / mcusPerLine; int mcuCol = mcu % mcusPerLine; int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; int offset = component.GetBlockBufferOffset(blockRow, blockCol); - this.DecodeBaseline(component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream); + this.DecodeBaseline(component, ref blockDataRef, offset, ref dcHuffmanTable, ref acHuffmanTable, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBlockDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + private void DecodeBlockDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcu, Stream stream) { int blockRow = mcu / component.WidthInBlocks; int blockCol = mcu % component.WidthInBlocks; int offset = component.GetBlockBufferOffset(blockRow, blockCol); - this.DecodeDCFirst(component, offset, ref dcHuffmanTable, stream); + this.DecodeDCFirst(component, ref blockDataRef, offset, ref dcHuffmanTable, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeMcuDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + private void DecodeMcuDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcusPerLine, int mcu, int row, int col, Stream stream) { int mcuRow = mcu / mcusPerLine; int mcuCol = mcu % mcusPerLine; int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; int offset = component.GetBlockBufferOffset(blockRow, blockCol); - this.DecodeDCFirst(component, offset, ref dcHuffmanTable, stream); + this.DecodeDCFirst(component, ref blockDataRef, offset, ref dcHuffmanTable, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBlockDCSuccessive(PdfJsFrameComponent component, int mcu, Stream stream) + private void DecodeBlockDCSuccessive(PdfJsFrameComponent component, ref short blockDataRef, int mcu, Stream stream) { int blockRow = mcu / component.WidthInBlocks; int blockCol = mcu % component.WidthInBlocks; int offset = component.GetBlockBufferOffset(blockRow, blockCol); - this.DecodeDCSuccessive(component, offset, stream); + this.DecodeDCSuccessive(component, ref blockDataRef, offset, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeMcuDCSuccessive(PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + private void DecodeMcuDCSuccessive(PdfJsFrameComponent component, ref short blockDataRef, int mcusPerLine, int mcu, int row, int col, Stream stream) { int mcuRow = mcu / mcusPerLine; int mcuCol = mcu % mcusPerLine; int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; int offset = component.GetBlockBufferOffset(blockRow, blockCol); - this.DecodeDCSuccessive(component, offset, stream); + this.DecodeDCSuccessive(component, ref blockDataRef, offset, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBlockACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + private void DecodeBlockACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcu, Stream stream) { int blockRow = mcu / component.WidthInBlocks; int blockCol = mcu % component.WidthInBlocks; int offset = component.GetBlockBufferOffset(blockRow, blockCol); - this.DecodeACFirst(component, offset, ref acHuffmanTable, stream); + this.DecodeACFirst(component, ref blockDataRef, offset, ref acHuffmanTable, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeMcuACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + private void DecodeMcuACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcusPerLine, int mcu, int row, int col, Stream stream) { int mcuRow = mcu / mcusPerLine; int mcuCol = mcu % mcusPerLine; int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; int offset = component.GetBlockBufferOffset(blockRow, blockCol); - this.DecodeACFirst(component, offset, ref acHuffmanTable, stream); + this.DecodeACFirst(component, ref blockDataRef, offset, ref acHuffmanTable, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBlockACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + private void DecodeBlockACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcu, Stream stream) { int blockRow = mcu / component.WidthInBlocks; int blockCol = mcu % component.WidthInBlocks; int offset = component.GetBlockBufferOffset(blockRow, blockCol); - this.DecodeACSuccessive(component, offset, ref acHuffmanTable, stream); + this.DecodeACSuccessive(component, ref blockDataRef, offset, ref acHuffmanTable, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeMcuACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + private void DecodeMcuACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, ref short blockDataRef, int mcusPerLine, int mcu, int row, int col, Stream stream) { int mcuRow = mcu / mcusPerLine; int mcuCol = mcu % mcusPerLine; int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; int offset = component.GetBlockBufferOffset(blockRow, blockCol); - this.DecodeACSuccessive(component, offset, ref acHuffmanTable, stream); + this.DecodeACSuccessive(component, ref blockDataRef, offset, ref acHuffmanTable, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -579,7 +592,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components if (this.bitsData == -0x1) { - // We've encountered the end of the file stream which means there's no EOI marker in the image + // We've encountered the end of the file stream which means there's no EOI marker ref the image this.endOfStreamReached = true; } @@ -705,23 +718,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBaseline(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) + private void DecodeBaseline(PdfJsFrameComponent component, ref short blockDataRef, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) { - Span blockDataSpan = component.BlockData.Span; - - int t = this.DecodeHuffman(ref dcHuffmanTable, stream); + short t = this.DecodeHuffman(ref dcHuffmanTable, stream); if (this.endOfStreamReached || this.unexpectedMarkerReached) { return; } int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream); - blockDataSpan[offset] = (short)(component.Pred += diff); + Unsafe.Add(ref blockDataRef, offset) = (short)(component.Pred += diff); int k = 1; while (k < 64) { - int rs = this.DecodeHuffman(ref acHuffmanTable, stream); + short rs = this.DecodeHuffman(ref acHuffmanTable, stream); if (this.endOfStreamReached || this.unexpectedMarkerReached) { return; @@ -750,42 +761,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components byte z = PdfJsQuantizationTables.DctZigZag[k]; short re = (short)this.ReceiveAndExtend(s, stream); - blockDataSpan[offset + z] = re; + Unsafe.Add(ref blockDataRef, offset + z) = re; k++; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeDCFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream) + private void DecodeDCFirst(PdfJsFrameComponent component, ref short blockDataRef, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream) { - Span blockDataSpan = component.BlockData.Span; - - int t = this.DecodeHuffman(ref dcHuffmanTable, stream); + short t = this.DecodeHuffman(ref dcHuffmanTable, stream); if (this.endOfStreamReached || this.unexpectedMarkerReached) { return; } int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream) << this.successiveState; - blockDataSpan[offset] = (short)(component.Pred += diff); + Unsafe.Add(ref blockDataRef, offset) = (short)(component.Pred += diff); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeDCSuccessive(PdfJsFrameComponent component, int offset, Stream stream) + private void DecodeDCSuccessive(PdfJsFrameComponent component, ref short blockDataRef, int offset, Stream stream) { - Span blockDataSpan = component.BlockData.Span; - int bit = this.ReadBit(stream); if (this.endOfStreamReached || this.unexpectedMarkerReached) { return; } - blockDataSpan[offset] |= (short)(bit << this.successiveState); + Unsafe.Add(ref blockDataRef, offset) |= (short)(bit << this.successiveState); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeACFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) + private void DecodeACFirst(PdfJsFrameComponent component, ref short blockDataRef, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) { if (this.eobrun > 0) { @@ -793,7 +800,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components return; } - Span componentBlockDataSpan = component.BlockData.Span; int k = this.specStart; int e = this.specEnd; while (k <= e) @@ -820,19 +826,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } k += r; - byte z = PdfJsQuantizationTables.DctZigZag[k]; - componentBlockDataSpan[offset + z] = (short)(this.ReceiveAndExtend(s, stream) * (1 << this.successiveState)); + + ref byte z = ref PdfJsQuantizationTables.DctZigZag[k]; + Unsafe.Add(ref blockDataRef, offset + z) = (short)(this.ReceiveAndExtend(s, stream) * (1 << this.successiveState)); k++; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeACSuccessive(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) + private void DecodeACSuccessive(PdfJsFrameComponent component, ref short blockDataRef, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) { int k = this.specStart; int e = this.specEnd; int r = 0; - Span componentBlockDataSpan = component.BlockData.Span; + while (k <= e) { byte z = PdfJsQuantizationTables.DctZigZag[k]; @@ -874,7 +881,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components continue; case 1: // Skipping r zero items case 2: - if (componentBlockDataSpan[offset + z] != 0) + ref short blockRef = ref Unsafe.Add(ref blockDataRef, offset + z); + if (blockRef != 0) { int bit = this.ReadBit(stream); if (this.endOfStreamReached || this.unexpectedMarkerReached) @@ -882,7 +890,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components return; } - componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); + blockRef += (short)(bit << this.successiveState); } else { @@ -895,7 +903,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components break; case 3: // Set value for a zero item - if (componentBlockDataSpan[offset + z] != 0) + ref short blockRef2 = ref Unsafe.Add(ref blockDataRef, offset + z); + if (blockRef2 != 0) { int bit = this.ReadBit(stream); if (this.endOfStreamReached || this.unexpectedMarkerReached) @@ -903,17 +912,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components return; } - componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); + blockRef2 += (short)(bit << this.successiveState); } else { - componentBlockDataSpan[offset + z] = (short)(this.successiveACNextValue << this.successiveState); + blockRef2 = (short)(this.successiveACNextValue << this.successiveState); this.successiveACState = 0; } break; case 4: // Eob - if (componentBlockDataSpan[offset + z] != 0) + ref short blockRef3 = ref Unsafe.Add(ref blockDataRef, offset + z); + if (blockRef3 != 0) { int bit = this.ReadBit(stream); if (this.endOfStreamReached || this.unexpectedMarkerReached) @@ -921,7 +931,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components return; } - componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); + blockRef3 += (short)(bit << this.successiveState); } break; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index 30b8158e7..aa9a9a6b0 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers.Binary; using System.IO; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; @@ -123,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort if (value == 0) { - return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, (int)stream.Length - 2); + return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, stream.Length - 2); } if (marker[0] == PdfJsJpegConstants.Markers.Prefix) @@ -135,16 +136,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort int suffix = stream.ReadByte(); if (suffix == -1) { - return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, (int)stream.Length - 2); + return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, stream.Length - 2); } marker[1] = (byte)suffix; } - return new PdfJsFileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2)); + return new PdfJsFileMarker(BinaryPrimitives.ReadUInt16BigEndian(marker), stream.Position - 2); } - return new PdfJsFileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2), true); + return new PdfJsFileMarker(BinaryPrimitives.ReadUInt16BigEndian(marker), stream.Position - 2, true); } /// @@ -172,8 +173,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort this.Frame?.Dispose(); this.components?.Dispose(); this.quantizationTables?.Dispose(); - this.dcHuffmanTables?.Dispose(); - this.acHuffmanTables?.Dispose(); this.pixelArea.Dispose(); // Set large fields to null. @@ -827,6 +826,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// The table index /// The codelengths /// The values + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void BuildHuffmanTable(PdfJsHuffmanTables tables, int index, byte[] codeLengths, byte[] values) { tables[index] = new PdfJsHuffmanTable(this.configuration.MemoryManager, codeLengths, values); @@ -938,7 +938,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort private ushort ReadUint16() { this.InputStream.Read(this.markerBuffer, 0, 2); - return (ushort)((this.markerBuffer[0] << 8) | this.markerBuffer[1]); + return BinaryPrimitives.ReadUInt16BigEndian(this.markerBuffer); } } } \ No newline at end of file