From b717bee06e7a9740e35cc82149153f7d7ea3d98b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 18 Aug 2017 20:59:09 +0200 Subject: [PATCH] let's merge jpeg-port to have the changelog! --- .../Jpeg/PdfJsPort/Components/PdfJsAdobe.cs | 74 -- .../PdfJsPort/Components/PdfJsComponent.cs | 44 - .../Components/PdfJsComponentBlocks.cs | 34 - .../PdfJsPort/Components/PdfJsFileMarker.cs | 59 -- .../Jpeg/PdfJsPort/Components/PdfJsFrame.cs | 89 -- .../Components/PdfJsFrameComponent.cs | 74 -- .../PdfJsPort/Components/PdfJsHuffmanTable.cs | 212 ---- .../Components/PdfJsHuffmanTables.cs | 42 - .../Jpeg/PdfJsPort/Components/PdfJsIDCT.cs | 511 --------- .../Jpeg/PdfJsPort/Components/PdfJsJFif.cs | 79 -- .../Components/PdfJsJpegPixelArea.cs | 147 --- .../Components/PdfJsQuantizationTables.cs | 67 -- .../PdfJsPort/Components/PdfJsScanDecoder.cs | 947 ----------------- .../Components/PdfJsYCbCrToRgbTables.cs | 128 --- .../Jpeg/PdfJsPort/PdfJsJpegConstants.cs | 359 ------- .../Jpeg/PdfJsPort/PdfJsJpegDecoder.cs | 21 - .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 985 ------------------ .../Formats/Jpg/JpegDecoderTests.cs | 18 +- 18 files changed, 11 insertions(+), 3879 deletions(-) delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsAdobe.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJFif.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsAdobe.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsAdobe.cs deleted file mode 100644 index 542272044..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsAdobe.cs +++ /dev/null @@ -1,74 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -// ReSharper disable InconsistentNaming -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - - /// - /// Provides information about the Adobe marker segment - /// - internal struct PdfJsAdobe : IEquatable - { - /// - /// The DCT Encode Version - /// - public short DCTEncodeVersion; - - /// - /// 0x0 : (none) - /// Bit 15 : Encoded with Blend=1 downsampling - /// - public short APP14Flags0; - - /// - /// 0x0 : (none) - /// - public short APP14Flags1; - - /// - /// Determines the colorspace transform - /// 00 : Unknown (RGB or CMYK) - /// 01 : YCbCr - /// 02 : YCCK - /// - public byte ColorTransform; - - /// - public bool Equals(PdfJsAdobe other) - { - return this.DCTEncodeVersion == other.DCTEncodeVersion - && this.APP14Flags0 == other.APP14Flags0 - && this.APP14Flags1 == other.APP14Flags1 - && this.ColorTransform == other.ColorTransform; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - return obj is PdfJsAdobe && this.Equals((PdfJsAdobe)obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - // TODO: Merge and use HashCodeHelpers - int hashCode = this.DCTEncodeVersion.GetHashCode(); - hashCode = (hashCode * 397) ^ this.APP14Flags0.GetHashCode(); - hashCode = (hashCode * 397) ^ this.APP14Flags1.GetHashCode(); - hashCode = (hashCode * 397) ^ this.ColorTransform.GetHashCode(); - return hashCode; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs deleted file mode 100644 index 12e11a86e..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - using System.Numerics; - using ImageSharp.Memory; - - /// - /// Represents a component block - /// - internal struct PdfJsComponent : IDisposable - { - /// - /// Gets or sets the output - /// - public Buffer Output; - - /// - /// Gets or sets the scaling factors - /// - public Vector2 Scale; - - /// - /// Gets or sets the number of blocks per line - /// - public int BlocksPerLine; - - /// - /// Gets or sets the number of blocks per column - /// - public int BlocksPerColumn; - - /// - public void Dispose() - { - this.Output?.Dispose(); - this.Output = null; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs deleted file mode 100644 index 6a879b4e5..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - - /// - /// Contains all the decoded component blocks - /// - internal sealed class PdfJsComponentBlocks : IDisposable - { - /// - /// Gets or sets the component blocks - /// - public PdfJsComponent[] Components { get; set; } - - /// - public void Dispose() - { - if (this.Components != null) - { - for (int i = 0; i < this.Components.Length; i++) - { - this.Components[i].Dispose(); - } - - this.Components = null; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs deleted file mode 100644 index 5e2555a9b..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - /// - /// Represents a jpeg file marker - /// - internal struct PdfJsFileMarker - { - /// - /// Initializes a new instance of the struct. - /// - /// The marker - /// The position within the stream - public PdfJsFileMarker(ushort marker, long position) - { - this.Marker = marker; - this.Position = position; - this.Invalid = false; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The marker - /// The position within the stream - /// Whether the current marker is invalid - public PdfJsFileMarker(ushort marker, long position, bool invalid) - { - this.Marker = marker; - this.Position = position; - this.Invalid = invalid; - } - - /// - /// Gets or sets a value indicating whether the current marker is invalid - /// - public bool Invalid { get; set; } - - /// - /// Gets the position of the marker within a stream - /// - public ushort Marker { get; } - - /// - /// Gets the position of the marker within a stream - /// - public long Position { get; } - - /// - public override string ToString() - { - return this.Marker.ToString("X"); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs deleted file mode 100644 index ff25ee154..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs +++ /dev/null @@ -1,89 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - - /// - /// Represent a single jpeg frame - /// - internal sealed class PdfJsFrame : IDisposable - { - /// - /// Gets or sets a value indicating whether the frame uses the extended specification - /// - public bool Extended { get; set; } - - /// - /// Gets or sets a value indicating whether the frame uses the progressive specification - /// - public bool Progressive { get; set; } - - /// - /// Gets or sets the precision - /// - public byte Precision { get; set; } - - /// - /// Gets or sets the number of scanlines within the frame - /// - public short Scanlines { get; set; } - - /// - /// Gets or sets the number of samples per scanline - /// - public short SamplesPerLine { get; set; } - - /// - /// Gets or sets the number of components within a frame. In progressive frames this value can range from only 1 to 4 - /// - public byte ComponentCount { get; set; } - - /// - /// Gets or sets the component id collection - /// - public byte[] ComponentIds { get; set; } - - /// - /// Gets or sets the frame component collection - /// - public PdfJsFrameComponent[] Components { get; set; } - - /// - /// Gets or sets the maximum horizontal sampling factor - /// - public int MaxHorizontalFactor { get; set; } - - /// - /// Gets or sets the maximum vertical sampling factor - /// - public int MaxVerticalFactor { get; set; } - - /// - /// Gets or sets the number of MCU's per line - /// - public int McusPerLine { get; set; } - - /// - /// Gets or sets the number of MCU's per column - /// - public int McusPerColumn { get; set; } - - /// - public void Dispose() - { - if (this.Components != null) - { - for (int i = 0; i < this.Components.Length; i++) - { - this.Components[i].Dispose(); - } - - this.Components = null; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs deleted file mode 100644 index b999d86f1..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs +++ /dev/null @@ -1,74 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - - using ImageSharp.Memory; - - /// - /// Represents a single frame component - /// - internal struct PdfJsFrameComponent : IDisposable - { - /// - /// Gets or sets the component Id - /// - public byte Id; - - /// - /// TODO: What does pred stand for? - /// - public int Pred; - - /// - /// Gets or sets the horizontal sampling factor. - /// - public int HorizontalFactor; - - /// - /// Gets or sets the vertical sampling factor. - /// - public int VerticalFactor; - - /// - /// Gets or sets the identifier - /// - public byte QuantizationIdentifier; - - /// - /// Gets or sets the block data - /// - public Buffer BlockData; - - /// - /// Gets or sets the number of blocks per line - /// - public int BlocksPerLine; - - /// - /// Gets or sets the number of blocks per column - /// - public int BlocksPerColumn; - - /// - /// Gets the index for the DC Huffman table - /// - public int DCHuffmanTableId; - - /// - /// Gets the index for the AC Huffman table - /// - public int ACHuffmanTableId; - - /// - public void Dispose() - { - this.BlockData?.Dispose(); - this.BlockData = null; - } - } -} \ 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 deleted file mode 100644 index 745dc6361..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ /dev/null @@ -1,212 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - - /// - /// Represents a Huffman Table - /// - internal struct PdfJsHuffmanTable : IDisposable - { - private Buffer lookahead; - private Buffer valOffset; - private Buffer maxcode; - private Buffer huffval; - - /// - /// Initializes a new instance of the struct. - /// - /// The code lengths - /// The huffman values - public PdfJsHuffmanTable(byte[] lengths, byte[] values) - { - this.lookahead = Buffer.CreateClean(256); - this.valOffset = Buffer.CreateClean(18); - this.maxcode = Buffer.CreateClean(18); - - using (var huffsize = Buffer.CreateClean(257)) - using (var huffcode = Buffer.CreateClean(257)) - { - GenerateSizeTable(lengths, huffsize); - GenerateCodeTable(huffsize, huffcode); - GenerateDecoderTables(lengths, huffcode, this.valOffset, this.maxcode); - GenerateLookaheadTables(lengths, values, this.lookahead); - } - - this.huffval = Buffer.CreateClean(values.Length); - 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; - } - - /// - /// Gets the value offset array - /// - public short[] ValOffset - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - } - - /// - /// Gets the huffman value array - /// - public byte[] HuffVal - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - } - - /// - /// Gets the lookahead array - /// - public short[] Lookahead - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - } - - /// - public void Dispose() - { - this.lookahead?.Dispose(); - this.valOffset?.Dispose(); - this.maxcode?.Dispose(); - this.huffval?.Dispose(); - - this.lookahead = null; - this.valOffset = null; - this.maxcode = null; - this.huffval = null; - } - - /// - /// Figure C.1: make table of Huffman code length for each symbol - /// - /// The code lengths - /// The huffman size span - private static void GenerateSizeTable(byte[] lengths, Span huffsize) - { - short index = 0; - for (short l = 1; l <= 16; l++) - { - byte i = lengths[l]; - for (short j = 0; j < i; j++) - { - huffsize[index] = l; - index++; - } - } - - huffsize[index] = 0; - } - - /// - /// Figure C.2: generate the codes themselves - /// - /// The huffman size span - /// The huffman code span - private static void GenerateCodeTable(Span huffsize, Span huffcode) - { - short k = 0; - short si = huffsize[0]; - short code = 0; - for (short i = 0; i < huffsize.Length; i++) - { - while (huffsize[k] == si) - { - huffcode[k] = code; - code++; - k++; - } - - code <<= 1; - si++; - } - } - - /// - /// Figure F.15: generate decoding tables for bit-sequential decoding - /// - /// 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) - { - short bitcount = 0; - for (int i = 1; i <= 16; i++) - { - 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 - { - maxcode[i] = -1; // -1 if no codes of this length - } - } - - valOffset[17] = 0; - maxcode[17] = 0xFFFFFL; - } - - /// - /// Generates lookup tables to speed up decoding - /// - /// The code lengths - /// The huffman value array - /// The lookahead span - private static void GenerateLookaheadTables(byte[] lengths, byte[] huffval, Span lookahead) - { - int x = 0, code = 0; - - for (int i = 0; i < 8; i++) - { - code <<= 1; - - for (int j = 0; j < lengths[i + 1]; j++) - { - // 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++) - { - lookahead[base2 | k] = lutValue; - } - - code++; - x++; - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs deleted file mode 100644 index 5d00fe7f6..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - - /// - /// Defines a pair of huffman tables - /// - internal sealed class PdfJsHuffmanTables : IDisposable - { - private readonly PdfJsHuffmanTable[] tables = new PdfJsHuffmanTable[4]; - - /// - /// Gets or sets the table at the given index. - /// - /// The index - /// The - public ref PdfJsHuffmanTable this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - 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/PdfJsIDCT.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs deleted file mode 100644 index c509d4357..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs +++ /dev/null @@ -1,511 +0,0 @@ -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - - /// - /// Performs the inverse Descrete Cosine Transform on each frame component. - /// - internal static class PdfJsIDCT - { - /// - /// Precomputed values scaled up by 14 bits - /// - public static readonly short[] Aanscales = - { - 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855, - 12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585, - 5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, - 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873, - 17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299, - 11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315, - 4520, 3552, 2446, 1247 - }; - - private const int DctCos1 = 4017; // cos(pi/16) - private const int DctSin1 = 799; // sin(pi/16) - private const int DctCos3 = 3406; // cos(3*pi/16) - private const int DctSin3 = 2276; // sin(3*pi/16) - private const int DctCos6 = 1567; // cos(6*pi/16) - private const int DctSin6 = 3784; // sin(6*pi/16) - private const int DctSqrt2 = 5793; // sqrt(2) - private const int DctSqrt1D2 = 2896; // sqrt(2) / 2 - -#pragma warning disable SA1310 // Field names must not contain underscore - private const int FIX_1_082392200 = 277; // FIX(1.082392200) - private const int FIX_1_414213562 = 362; // FIX(1.414213562) - private const int FIX_1_847759065 = 473; // FIX(1.847759065) - private const int FIX_2_613125930 = 669; // FIX(2.613125930) -#pragma warning restore SA1310 // Field names must not contain underscore - - private const int ConstBits = 8; - private const int Pass1Bits = 2; // Factional bits in scale factors - private const int MaxJSample = 255; - private const int CenterJSample = 128; - private const int RangeCenter = (MaxJSample * 2) + 2; - - // First segment of range limit table: limit[x] = 0 for x < 0 - // allow negative subscripts of simple table - private const int TableOffset = 2 * (MaxJSample + 1); - private const int LimitOffset = TableOffset - (RangeCenter - CenterJSample); - - // Each IDCT routine is responsible for range-limiting its results and - // converting them to unsigned form (0..MaxJSample). The raw outputs could - // be quite far out of range if the input data is corrupt, so a bulletproof - // range-limiting step is required. We use a mask-and-table-lookup method - // to do the combined operations quickly, assuming that MaxJSample+1 - // is a power of 2. - private const int RangeMask = (MaxJSample * 4) + 3; // 2 bits wider than legal samples - - private static readonly byte[] Limit = new byte[5 * (MaxJSample + 1)]; - - static PdfJsIDCT() - { - // Main part of range limit table: limit[x] = x - int i; - for (i = 0; i <= MaxJSample; i++) - { - Limit[TableOffset + i] = (byte)i; - } - - // End of range limit table: Limit[x] = MaxJSample for x > MaxJSample - for (; i < 3 * (MaxJSample + 1); i++) - { - Limit[TableOffset + i] = MaxJSample; - } - } - - /// - /// A port of Poppler's IDCT method which in turn is taken from: - /// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, - /// 'Practical Fast 1-D DCT Algorithms with 11 Multiplications', - /// IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, 988-991. - /// - /// The fram component - /// The block buffer offset - /// The computational buffer for holding temp values - /// The quantization table - public static void QuantizeAndInverse(ref PdfJsFrameComponent component, int blockBufferOffset, ref Span computationBuffer, ref Span quantizationTable) - { - Span blockData = component.BlockData.Slice(blockBufferOffset); - int v0, v1, v2, v3, v4, v5, v6, v7; - int p0, p1, p2, p3, p4, p5, p6, p7; - int t; - - // inverse DCT on rows - for (int row = 0; row < 64; row += 8) - { - // gather block data - p0 = blockData[row]; - p1 = blockData[row + 1]; - p2 = blockData[row + 2]; - p3 = blockData[row + 3]; - p4 = blockData[row + 4]; - p5 = blockData[row + 5]; - p6 = blockData[row + 6]; - p7 = blockData[row + 7]; - - // dequant p0 - p0 *= quantizationTable[row]; - - // check for all-zero AC coefficients - if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) - { - t = ((DctSqrt2 * p0) + 512) >> 10; - short st = (short)t; - computationBuffer[row] = st; - computationBuffer[row + 1] = st; - computationBuffer[row + 2] = st; - computationBuffer[row + 3] = st; - computationBuffer[row + 4] = st; - computationBuffer[row + 5] = st; - computationBuffer[row + 6] = st; - computationBuffer[row + 7] = st; - continue; - } - - // dequant p1 ... p7 - p1 *= quantizationTable[row + 1]; - p2 *= quantizationTable[row + 2]; - p3 *= quantizationTable[row + 3]; - p4 *= quantizationTable[row + 4]; - p5 *= quantizationTable[row + 5]; - p6 *= quantizationTable[row + 6]; - p7 *= quantizationTable[row + 7]; - - // stage 4 - v0 = ((DctSqrt2 * p0) + 128) >> 8; - v1 = ((DctSqrt2 * p4) + 128) >> 8; - v2 = p2; - v3 = p6; - v4 = ((DctSqrt1D2 * (p1 - p7)) + 128) >> 8; - v7 = ((DctSqrt1D2 * (p1 + p7)) + 128) >> 8; - v5 = p3 << 4; - v6 = p5 << 4; - - // stage 3 - v0 = (v0 + v1 + 1) >> 1; - v1 = v0 - v1; - t = ((v2 * DctSin6) + (v3 * DctCos6) + 128) >> 8; - v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 128) >> 8; - v3 = t; - v4 = (v4 + v6 + 1) >> 1; - v6 = v4 - v6; - v7 = (v7 + v5 + 1) >> 1; - v5 = v7 - v5; - - // stage 2 - v0 = (v0 + v3 + 1) >> 1; - v3 = v0 - v3; - v1 = (v1 + v2 + 1) >> 1; - v2 = v1 - v2; - t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12; - v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12; - v7 = t; - t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12; - v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12; - v6 = t; - - // stage 1 - computationBuffer[row] = (short)(v0 + v7); - computationBuffer[row + 7] = (short)(v0 - v7); - computationBuffer[row + 1] = (short)(v1 + v6); - computationBuffer[row + 6] = (short)(v1 - v6); - computationBuffer[row + 2] = (short)(v2 + v5); - computationBuffer[row + 5] = (short)(v2 - v5); - computationBuffer[row + 3] = (short)(v3 + v4); - computationBuffer[row + 4] = (short)(v3 - v4); - } - - // inverse DCT on columns - for (int col = 0; col < 8; ++col) - { - p0 = computationBuffer[col]; - p1 = computationBuffer[col + 8]; - p2 = computationBuffer[col + 16]; - p3 = computationBuffer[col + 24]; - p4 = computationBuffer[col + 32]; - p5 = computationBuffer[col + 40]; - p6 = computationBuffer[col + 48]; - p7 = computationBuffer[col + 56]; - - // check for all-zero AC coefficients - if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) - { - t = ((DctSqrt2 * p0) + 8192) >> 14; - - // convert to 8 bit - t = (t < -2040) ? 0 : (t >= 2024) ? MaxJSample : (t + 2056) >> 4; - short st = (short)t; - - blockData[col] = st; - blockData[col + 8] = st; - blockData[col + 16] = st; - blockData[col + 24] = st; - blockData[col + 32] = st; - blockData[col + 40] = st; - blockData[col + 48] = st; - blockData[col + 56] = st; - continue; - } - - // stage 4 - v0 = ((DctSqrt2 * p0) + 2048) >> 12; - v1 = ((DctSqrt2 * p4) + 2048) >> 12; - v2 = p2; - v3 = p6; - v4 = ((DctSqrt1D2 * (p1 - p7)) + 2048) >> 12; - v7 = ((DctSqrt1D2 * (p1 + p7)) + 2048) >> 12; - v5 = p3; - v6 = p5; - - // stage 3 - // Shift v0 by 128.5 << 5 here, so we don't need to shift p0...p7 when - // converting to UInt8 range later. - v0 = ((v0 + v1 + 1) >> 1) + 4112; - v1 = v0 - v1; - t = ((v2 * DctSin6) + (v3 * DctCos6) + 2048) >> 12; - v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 2048) >> 12; - v3 = t; - v4 = (v4 + v6 + 1) >> 1; - v6 = v4 - v6; - v7 = (v7 + v5 + 1) >> 1; - v5 = v7 - v5; - - // stage 2 - v0 = (v0 + v3 + 1) >> 1; - v3 = v0 - v3; - v1 = (v1 + v2 + 1) >> 1; - v2 = v1 - v2; - t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12; - v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12; - v7 = t; - t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12; - v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12; - v6 = t; - - // stage 1 - p0 = v0 + v7; - p7 = v0 - v7; - p1 = v1 + v6; - p6 = v1 - v6; - p2 = v2 + v5; - p5 = v2 - v5; - p3 = v3 + v4; - p4 = v3 - v4; - - // convert to 8-bit integers - p0 = (p0 < 16) ? 0 : (p0 >= 4080) ? MaxJSample : p0 >> 4; - p1 = (p1 < 16) ? 0 : (p1 >= 4080) ? MaxJSample : p1 >> 4; - p2 = (p2 < 16) ? 0 : (p2 >= 4080) ? MaxJSample : p2 >> 4; - p3 = (p3 < 16) ? 0 : (p3 >= 4080) ? MaxJSample : p3 >> 4; - p4 = (p4 < 16) ? 0 : (p4 >= 4080) ? MaxJSample : p4 >> 4; - p5 = (p5 < 16) ? 0 : (p5 >= 4080) ? MaxJSample : p5 >> 4; - p6 = (p6 < 16) ? 0 : (p6 >= 4080) ? MaxJSample : p6 >> 4; - p7 = (p7 < 16) ? 0 : (p7 >= 4080) ? MaxJSample : p7 >> 4; - - // store block data - blockData[col] = (short)p0; - blockData[col + 8] = (short)p1; - blockData[col + 16] = (short)p2; - blockData[col + 24] = (short)p3; - blockData[col + 32] = (short)p4; - blockData[col + 40] = (short)p5; - blockData[col + 48] = (short)p6; - blockData[col + 56] = (short)p7; - } - } - - /// - /// A port of - /// A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT - /// on each row(or vice versa, but it's more convenient to emit a row at - /// a time). Direct algorithms are also available, but they are much more - /// complex and seem not to be any faster when reduced to code. - /// - /// This implementation is based on Arai, Agui, and Nakajima's algorithm for - /// scaled DCT.Their original paper (Trans.IEICE E-71(11):1095) is in - /// Japanese, but the algorithm is described in the Pennebaker & Mitchell - /// JPEG textbook(see REFERENCES section in file README.ijg). The following - /// code is based directly on figure 4-8 in P&M. - /// While an 8-point DCT cannot be done in less than 11 multiplies, it is - /// possible to arrange the computation so that many of the multiplies are - /// simple scalings of the final outputs.These multiplies can then be - /// folded into the multiplications or divisions by the JPEG quantization - /// table entries. The AA&N method leaves only 5 multiplies and 29 adds - /// to be done in the DCT itself. - /// The primary disadvantage of this method is that with fixed-point math, - /// accuracy is lost due to imprecise representation of the scaled - /// quantization values.The smaller the quantization table entry, the less - /// precise the scaled value, so this implementation does worse with high - - /// quality - setting files than with low - quality ones. - /// - /// The frame component - /// The block buffer offset - /// The computational buffer for holding temp values - /// The multiplier table - public static void QuantizeAndInverseFast(ref PdfJsFrameComponent component, int blockBufferOffset, ref Span computationBuffer, ref Span multiplierTable) - { - Span blockData = component.BlockData.Slice(blockBufferOffset); - int p0, p1, p2, p3, p4, p5, p6, p7; - - for (int col = 0; col < 8; col++) - { - // Gather block data - p0 = blockData[col]; - p1 = blockData[col + 8]; - p2 = blockData[col + 16]; - p3 = blockData[col + 24]; - p4 = blockData[col + 32]; - p5 = blockData[col + 40]; - p6 = blockData[col + 48]; - p7 = blockData[col + 56]; - - int tmp0 = p0 * multiplierTable[col]; - - // Due to quantization, we will usually find that many of the input - // coefficients are zero, especially the AC terms. We can exploit this - // by short-circuiting the IDCT calculation for any column in which all - // the AC terms are zero. In that case each output is equal to the - // DC coefficient (with scale factor as needed). - // With typical images and quantization tables, half or more of the - // column DCT calculations can be simplified this way. - if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) - { - short dcval = (short)tmp0; - - computationBuffer[col] = dcval; - computationBuffer[col + 8] = dcval; - computationBuffer[col + 16] = dcval; - computationBuffer[col + 24] = dcval; - computationBuffer[col + 32] = dcval; - computationBuffer[col + 40] = dcval; - computationBuffer[col + 48] = dcval; - computationBuffer[col + 56] = dcval; - - continue; - } - - // Even part - int tmp1 = p2 * multiplierTable[col + 16]; - int tmp2 = p4 * multiplierTable[col + 32]; - int tmp3 = p6 * multiplierTable[col + 48]; - - int tmp10 = tmp0 + tmp2; // Phase 3 - int tmp11 = tmp0 - tmp2; - - int tmp13 = tmp1 + tmp3; // Phases 5-3 - int tmp12 = Multiply(tmp1 - tmp3, FIX_1_414213562) - tmp13; // 2*c4 - - tmp0 = tmp10 + tmp13; // Phase 2 - tmp3 = tmp10 - tmp13; - tmp1 = tmp11 + tmp12; - tmp2 = tmp11 - tmp12; - - // Odd Part - int tmp4 = p1 * multiplierTable[col + 8]; - int tmp5 = p3 * multiplierTable[col + 24]; - int tmp6 = p5 * multiplierTable[col + 40]; - int tmp7 = p7 * multiplierTable[col + 56]; - - int z13 = tmp6 + tmp5; // Phase 6 - int z10 = tmp6 - tmp5; - int z11 = tmp4 + tmp7; - int z12 = tmp4 - tmp7; - - tmp7 = z11 + z13; // Phase 5 - tmp11 = Multiply(z11 - z13, FIX_1_414213562); // 2*c4 - - int z5 = Multiply(z10 + z12, FIX_1_847759065); // 2*c2 - tmp10 = z5 - Multiply(z12, FIX_1_082392200); // 2*(c2-c6) - tmp12 = z5 - Multiply(z10, FIX_2_613125930); // 2*(c2+c6) - - tmp6 = tmp12 - tmp7; // Phase 2 - tmp5 = tmp11 - tmp6; - tmp4 = tmp10 - tmp5; - - computationBuffer[col] = (short)(tmp0 + tmp7); - computationBuffer[col + 56] = (short)(tmp0 - tmp7); - computationBuffer[col + 8] = (short)(tmp1 + tmp6); - computationBuffer[col + 48] = (short)(tmp1 - tmp6); - computationBuffer[col + 16] = (short)(tmp2 + tmp5); - computationBuffer[col + 40] = (short)(tmp2 - tmp5); - computationBuffer[col + 24] = (short)(tmp3 + tmp4); - computationBuffer[col + 32] = (short)(tmp3 - tmp4); - } - - // Pass 2: process rows from work array, store into output array. - // Note that we must descale the results by a factor of 8 == 2**3, - // and also undo the pass 1 bits scaling. - for (int row = 0; row < 64; row += 8) - { - p1 = computationBuffer[row + 1]; - p2 = computationBuffer[row + 2]; - p3 = computationBuffer[row + 3]; - p4 = computationBuffer[row + 4]; - p5 = computationBuffer[row + 5]; - p6 = computationBuffer[row + 6]; - p7 = computationBuffer[row + 7]; - - // Add range center and fudge factor for final descale and range-limit. - int z5 = computationBuffer[row] + (RangeCenter << (Pass1Bits + 3)) + (1 << (Pass1Bits + 2)); - - // Check for all-zero AC coefficients - if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) - { - byte dcval = Limit[LimitOffset + (RightShift(z5, Pass1Bits + 3) & RangeMask)]; - - blockData[row] = dcval; - blockData[row + 1] = dcval; - blockData[row + 2] = dcval; - blockData[row + 3] = dcval; - blockData[row + 4] = dcval; - blockData[row + 5] = dcval; - blockData[row + 6] = dcval; - blockData[row + 7] = dcval; - - continue; - } - - // Even part - int tmp10 = z5 + p4; - int tmp11 = z5 - p4; - - int tmp13 = p2 + p6; - int tmp12 = Multiply(p2 - p6, FIX_1_414213562) - tmp13; // 2*c4 - - int tmp0 = tmp10 + tmp13; - int tmp3 = tmp10 - tmp13; - int tmp1 = tmp11 + tmp12; - int tmp2 = tmp11 - tmp12; - - // Odd part - int z13 = p5 + p3; - int z10 = p5 - p3; - int z11 = p1 + p7; - int z12 = p1 - p7; - - int tmp7 = z11 + z13; // Phase 5 - tmp11 = Multiply(z11 - z13, FIX_1_414213562); // 2*c4 - - z5 = Multiply(z10 + z12, FIX_1_847759065); // 2*c2 - tmp10 = z5 - Multiply(z12, FIX_1_082392200); // 2*(c2-c6) - tmp12 = z5 - Multiply(z10, FIX_2_613125930); // 2*(c2+c6) - - int tmp6 = tmp12 - tmp7; // Phase 2 - int tmp5 = tmp11 - tmp6; - int tmp4 = tmp10 - tmp5; - - // Final output stage: scale down by a factor of 8, offset, and range-limit - blockData[row] = Limit[LimitOffset + (RightShift(tmp0 + tmp7, Pass1Bits + 3) & RangeMask)]; - blockData[row + 7] = Limit[LimitOffset + (RightShift(tmp0 - tmp7, Pass1Bits + 3) & RangeMask)]; - blockData[row + 1] = Limit[LimitOffset + (RightShift(tmp1 + tmp6, Pass1Bits + 3) & RangeMask)]; - blockData[row + 6] = Limit[LimitOffset + (RightShift(tmp1 - tmp6, Pass1Bits + 3) & RangeMask)]; - blockData[row + 2] = Limit[LimitOffset + (RightShift(tmp2 + tmp5, Pass1Bits + 3) & RangeMask)]; - blockData[row + 5] = Limit[LimitOffset + (RightShift(tmp2 - tmp5, Pass1Bits + 3) & RangeMask)]; - blockData[row + 3] = Limit[LimitOffset + (RightShift(tmp3 + tmp4, Pass1Bits + 3) & RangeMask)]; - blockData[row + 4] = Limit[LimitOffset + (RightShift(tmp3 - tmp4, Pass1Bits + 3) & RangeMask)]; - } - } - - /// - /// Descale and correctly round an int value that's scaled by bits. - /// We assume rounds towards minus infinity, so adding - /// the fudge factor is correct for either sign of . - /// - /// The value - /// The number of bits - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Descale(int value, int n) - { - return RightShift(value + (1 << (n - 1)), n); - } - - /// - /// Multiply a variable by an int constant, and immediately descale. - /// - /// The value - /// The multiplier - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Multiply(int val, int c) - { - return Descale(val * c, ConstBits); - } - - /// - /// Right-shifts the value by the given amount - /// - /// The value - /// The amount to shift by - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int RightShift(int value, int shift) - { - return value >> shift; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJFif.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJFif.cs deleted file mode 100644 index 3c7cba989..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJFif.cs +++ /dev/null @@ -1,79 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - - /// - /// Provides information about the JFIF marker segment - /// TODO: Thumbnail? - /// - internal struct PdfJsJFif : IEquatable - { - /// - /// The major version - /// - public byte MajorVersion; - - /// - /// The minor version - /// - public byte MinorVersion; - - /// - /// Units for the following pixel density fields - /// 00 : No units; width:height pixel aspect ratio = Ydensity:Xdensity - /// 01 : Pixels per inch (2.54 cm) - /// 02 : Pixels per centimeter - /// - public byte DensityUnits; - - /// - /// Horizontal pixel density. Must not be zero. - /// - public short XDensity; - - /// - /// Vertical pixel density. Must not be zero. - /// - public short YDensity; - - /// - public bool Equals(PdfJsJFif other) - { - return this.MajorVersion == other.MajorVersion - && this.MinorVersion == other.MinorVersion - && this.DensityUnits == other.DensityUnits - && this.XDensity == other.XDensity - && this.YDensity == other.YDensity; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - return obj is PdfJsJFif && this.Equals((PdfJsJFif)obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - int hashCode = this.MajorVersion.GetHashCode(); - hashCode = (hashCode * 397) ^ this.MinorVersion.GetHashCode(); - hashCode = (hashCode * 397) ^ this.DensityUnits.GetHashCode(); - hashCode = (hashCode * 397) ^ this.XDensity.GetHashCode(); - hashCode = (hashCode * 397) ^ this.YDensity.GetHashCode(); - return hashCode; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs deleted file mode 100644 index 09677b278..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs +++ /dev/null @@ -1,147 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - using System.Diagnostics; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp.Memory; - - /// - /// Represents a section of the jpeg component data laid out in pixel order. - /// - internal struct PdfJsJpegPixelArea : IDisposable - { - private readonly int imageWidth; - - private readonly int imageHeight; - - private Buffer componentData; - - private int rowStride; - - /// - /// Initializes a new instance of the struct. - /// - /// The image width - /// The image height - /// The number of components - public PdfJsJpegPixelArea(int imageWidth, int imageHeight, int numberOfComponents) - { - this.imageWidth = imageWidth; - this.imageHeight = imageHeight; - this.Width = 0; - this.Height = 0; - this.NumberOfComponents = numberOfComponents; - this.componentData = null; - this.rowStride = 0; - } - - /// - /// Gets the number of components - /// - public int NumberOfComponents { get; } - - /// - /// Gets the width - /// - public int Width { get; private set; } - - /// - /// Gets the height - /// - public int Height { get; private set; } - - /// - /// Organsizes the decoded jpeg components into a linear array ordered by component. - /// This must be called before attempting to retrieve the data. - /// - /// The jpeg component blocks - /// The pixel area width - /// The pixel area height - public void LinearizeBlockData(PdfJsComponentBlocks components, int width, int height) - { - this.Width = width; - this.Height = height; - int numberOfComponents = this.NumberOfComponents; - this.rowStride = width * numberOfComponents; - var scale = new Vector2(this.imageWidth / (float)width, this.imageHeight / (float)height); - - this.componentData = new Buffer(width * height * numberOfComponents); - Span componentDataSpan = this.componentData; - const uint Mask3Lsb = 0xFFFFFFF8; // Used to clear the 3 LSBs - - using (var xScaleBlockOffset = new Buffer(width)) - { - Span xScaleBlockOffsetSpan = xScaleBlockOffset; - for (int i = 0; i < numberOfComponents; i++) - { - ref PdfJsComponent component = ref components.Components[i]; - Vector2 componentScale = component.Scale * scale; - int offset = i; - Span output = component.Output; - int blocksPerScanline = (component.BlocksPerLine + 1) << 3; - - // Precalculate the xScaleBlockOffset - int j; - for (int x = 0; x < width; x++) - { - j = (int)(x * componentScale.X); - xScaleBlockOffsetSpan[x] = (int)((j & Mask3Lsb) << 3) | (j & 7); - } - - // Linearize the blocks of the component - for (int y = 0; y < height; y++) - { - j = (int)(y * componentScale.Y); - int index = blocksPerScanline * (int)(j & Mask3Lsb) | ((j & 7) << 3); - for (int x = 0; x < width; x++) - { - componentDataSpan[offset] = (byte)output[index + xScaleBlockOffsetSpan[x]]; - offset += numberOfComponents; - } - } - } - } - } - - /// - /// Gets a representing the row 'y' beginning from the the first byte on that row. - /// - /// The y-coordinate of the pixel row. Must be greater than or equal to zero and less than the height of the pixel area. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetRowSpan(int y) - { - this.CheckCoordinates(y); - return this.componentData.Slice(y * this.rowStride, this.rowStride); - } - - /// - public void Dispose() - { - this.componentData?.Dispose(); - this.componentData = null; - } - - /// - /// Checks the coordinates to ensure they are within bounds. - /// - /// The y-coordinate of the row. Must be greater than zero and less than the height of the area. - /// - /// Thrown if the coordinates are not within the bounds of the image. - /// - [Conditional("DEBUG")] - private void CheckCoordinates(int y) - { - if (y < 0 || y >= this.Height) - { - throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the area bounds."); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs deleted file mode 100644 index 996752ba3..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - - /// - /// Contains the quantization tables. - /// - internal sealed class PdfJsQuantizationTables : IDisposable - { - /// - /// Gets the ZigZag scan table - /// - public static byte[] DctZigZag - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - } - - = - { - 0, - 1, 8, - 16, 9, 2, - 3, 10, 17, 24, - 32, 25, 18, 11, 4, - 5, 12, 19, 26, 33, 40, - 48, 41, 34, 27, 20, 13, 6, - 7, 14, 21, 28, 35, 42, 49, 56, - 57, 50, 43, 36, 29, 22, 15, - 23, 30, 37, 44, 51, 58, - 59, 52, 45, 38, 31, - 39, 46, 53, 60, - 61, 54, 47, - 55, 62, - 63 - }; - - /// - /// Gets or sets the quantization tables. - /// - public Buffer2D Tables - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; set; - } - - = new Buffer2D(64, 4); - - /// - public void Dispose() - { - if (this.Tables != null) - { - this.Tables.Dispose(); - this.Tables = null; - } - } - } -} \ 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 deleted file mode 100644 index c250db4c4..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs +++ /dev/null @@ -1,947 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System; -#if DEBUG - using System.Diagnostics; -#endif - using System.IO; - using System.Runtime.CompilerServices; - - /// - /// Provides the means to decode a spectral scan - /// - internal struct PdfJsScanDecoder - { - private byte[] markerBuffer; - - private int bitsData; - - private int bitsCount; - -#pragma warning disable 414 - private int bitsUnRead; - - private int accumulator; -#pragma warning restore 414 - - private int specStart; - - private int specEnd; - - private int eobrun; - - private int compIndex; - - private int successiveState; - - private int successiveACState; - - private int successiveACNextValue; - - private bool endOfStreamReached; - - private bool unexpectedMarkerReached; - - /// - /// Decodes the spectral scan - /// - /// The image frame - /// The input stream - /// The DC Huffman tables - /// The AC Huffman tables - /// The scan components - /// The component index within the array - /// The length of the components. Different to the array length - /// The reset interval - /// The spectral selection start - /// The spectral selection end - /// The successive approximation bit high end - /// The successive approximation bit low end - public void DecodeScan( - PdfJsFrame frame, - Stream stream, - PdfJsHuffmanTables dcHuffmanTables, - PdfJsHuffmanTables acHuffmanTables, - PdfJsFrameComponent[] components, - int componentIndex, - int componentsLength, - ushort resetInterval, - int spectralStart, - int spectralEnd, - int successivePrev, - int successive) - { - this.markerBuffer = new byte[2]; - this.compIndex = componentIndex; - this.specStart = spectralStart; - this.specEnd = spectralEnd; - this.successiveState = successive; - this.endOfStreamReached = false; - this.unexpectedMarkerReached = false; - - bool progressive = frame.Progressive; - int mcusPerLine = frame.McusPerLine; - - int mcu = 0; - int mcuExpected; - if (componentsLength == 1) - { - mcuExpected = components[this.compIndex].BlocksPerLine * components[this.compIndex].BlocksPerColumn; - } - else - { - mcuExpected = mcusPerLine * frame.McusPerColumn; - } - - PdfJsFileMarker fileMarker; - while (mcu < mcuExpected) - { - // Reset interval stuff - int mcuToRead = resetInterval != 0 ? Math.Min(mcuExpected - mcu, resetInterval) : mcuExpected; - for (int i = 0; i < components.Length; i++) - { - ref PdfJsFrameComponent c = ref components[i]; - c.Pred = 0; - } - - this.eobrun = 0; - - if (!progressive) - { - this.DecodeScanBaseline(dcHuffmanTables, acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); - } - else - { - if (this.specStart == 0) - { - if (successivePrev == 0) - { - this.DecodeScanDCFirst(dcHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); - } - else - { - this.DecodeScanDCSuccessive(components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); - } - } - else - { - if (successivePrev == 0) - { - this.DecodeScanACFirst(acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); - } - else - { - this.DecodeScanACSuccessive(acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); - } - } - } - - // Find marker - this.bitsCount = 0; - this.accumulator = 0; - this.bitsUnRead = 0; - fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream); - - // Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past - // those to attempt to find a valid marker (fixes issue4090.pdf) in original code. - if (fileMarker.Invalid) - { -#if DEBUG - Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}"); -#endif - } - - ushort marker = fileMarker.Marker; - - // RSTn - We've alread read the bytes and altered the position so no need to skip - if (marker >= PdfJsJpegConstants.Markers.RST0 && marker <= PdfJsJpegConstants.Markers.RST7) - { - continue; - } - - if (!fileMarker.Invalid) - { - // We've found a valid marker. - // Rewind the stream to the position of the marker and break - stream.Position = fileMarker.Position; - break; - } - } - - 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. - if (fileMarker.Invalid) - { -#if DEBUG - Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}"); -#endif - } - else - { - // We've found a valid marker. - // Rewind the stream to the position of the marker - stream.Position = fileMarker.Position; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int GetBlockBufferOffset(PdfJsFrameComponent component, int row, int col) - { - return 64 * (((component.BlocksPerLine + 1) * row) + col); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeScanBaseline( - PdfJsHuffmanTables dcHuffmanTables, - PdfJsHuffmanTables acHuffmanTables, - PdfJsFrameComponent[] components, - int componentsLength, - int mcusPerLine, - int mcuToRead, - ref int mcu, - Stream stream) - { - if (componentsLength == 1) - { - ref PdfJsFrameComponent component = ref components[this.compIndex]; - ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; - ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; - - for (int n = 0; n < mcuToRead; n++) - { - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - continue; - } - - this.DecodeBlockBaseline(ref dcHuffmanTable, ref acHuffmanTable, ref component, mcu, stream); - mcu++; - } - } - else - { - for (int n = 0; n < mcuToRead; n++) - { - for (int i = 0; i < componentsLength; i++) - { - ref PdfJsFrameComponent component = ref components[i]; - ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; - ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; - int h = component.HorizontalFactor; - int v = component.VerticalFactor; - - for (int j = 0; j < v; j++) - { - for (int k = 0; k < h; k++) - { - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - continue; - } - - this.DecodeMcuBaseline(ref dcHuffmanTable, ref acHuffmanTable, ref component, mcusPerLine, mcu, j, k, stream); - } - } - } - - mcu++; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeScanDCFirst( - PdfJsHuffmanTables dcHuffmanTables, - PdfJsFrameComponent[] components, - int componentsLength, - int mcusPerLine, - int mcuToRead, - ref int mcu, - Stream stream) - { - if (componentsLength == 1) - { - ref PdfJsFrameComponent component = ref components[this.compIndex]; - ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; - - for (int n = 0; n < mcuToRead; n++) - { - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - continue; - } - - this.DecodeBlockDCFirst(ref dcHuffmanTable, ref component, mcu, stream); - mcu++; - } - } - else - { - for (int n = 0; n < mcuToRead; n++) - { - for (int i = 0; i < componentsLength; i++) - { - ref PdfJsFrameComponent component = ref components[i]; - ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; - int h = component.HorizontalFactor; - int v = component.VerticalFactor; - - for (int j = 0; j < v; j++) - { - for (int k = 0; k < h; k++) - { - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - continue; - } - - this.DecodeMcuDCFirst(ref dcHuffmanTable, ref component, mcusPerLine, mcu, j, k, stream); - } - } - } - - mcu++; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeScanDCSuccessive( - PdfJsFrameComponent[] components, - int componentsLength, - int mcusPerLine, - int mcuToRead, - ref int mcu, - Stream stream) - { - if (componentsLength == 1) - { - ref PdfJsFrameComponent component = ref components[this.compIndex]; - for (int n = 0; n < mcuToRead; n++) - { - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - continue; - } - - this.DecodeBlockDCSuccessive(ref component, mcu, stream); - mcu++; - } - } - else - { - for (int n = 0; n < mcuToRead; n++) - { - for (int i = 0; i < componentsLength; i++) - { - ref PdfJsFrameComponent component = ref components[i]; - int h = component.HorizontalFactor; - int v = component.VerticalFactor; - for (int j = 0; j < v; j++) - { - for (int k = 0; k < h; k++) - { - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - continue; - } - - this.DecodeMcuDCSuccessive(ref component, mcusPerLine, mcu, j, k, stream); - } - } - } - - mcu++; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeScanACFirst( - PdfJsHuffmanTables acHuffmanTables, - PdfJsFrameComponent[] components, - int componentsLength, - int mcusPerLine, - int mcuToRead, - ref int mcu, - Stream stream) - { - if (componentsLength == 1) - { - ref PdfJsFrameComponent component = ref components[this.compIndex]; - ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; - - for (int n = 0; n < mcuToRead; n++) - { - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - continue; - } - - this.DecodeBlockACFirst(ref acHuffmanTable, ref component, mcu, stream); - mcu++; - } - } - else - { - for (int n = 0; n < mcuToRead; n++) - { - for (int i = 0; i < componentsLength; i++) - { - ref PdfJsFrameComponent component = ref components[i]; - ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; - int h = component.HorizontalFactor; - int v = component.VerticalFactor; - - for (int j = 0; j < v; j++) - { - for (int k = 0; k < h; k++) - { - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - continue; - } - - this.DecodeMcuACFirst(ref acHuffmanTable, ref component, mcusPerLine, mcu, j, k, stream); - } - } - } - - mcu++; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeScanACSuccessive( - PdfJsHuffmanTables acHuffmanTables, - PdfJsFrameComponent[] components, - int componentsLength, - int mcusPerLine, - int mcuToRead, - ref int mcu, - Stream stream) - { - if (componentsLength == 1) - { - ref PdfJsFrameComponent component = ref components[this.compIndex]; - ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; - - for (int n = 0; n < mcuToRead; n++) - { - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - continue; - } - - this.DecodeBlockACSuccessive(ref acHuffmanTable, ref component, mcu, stream); - mcu++; - } - } - else - { - for (int n = 0; n < mcuToRead; n++) - { - for (int i = 0; i < componentsLength; i++) - { - ref PdfJsFrameComponent component = ref components[i]; - ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; - int h = component.HorizontalFactor; - int v = component.VerticalFactor; - - for (int j = 0; j < v; j++) - { - for (int k = 0; k < h; k++) - { - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - continue; - } - - this.DecodeMcuACSuccessive(ref acHuffmanTable, ref component, mcusPerLine, mcu, j, k, stream); - } - } - } - - mcu++; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBlockBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcu, Stream stream) - { - int blockRow = mcu / component.BlocksPerLine; - int blockCol = mcu % component.BlocksPerLine; - int offset = GetBlockBufferOffset(component, blockRow, blockCol); - this.DecodeBaseline(ref component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeMcuBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) - { - int mcuRow = mcu / mcusPerLine; - int mcuCol = mcu % mcusPerLine; - int blockRow = (mcuRow * component.VerticalFactor) + row; - int blockCol = (mcuCol * component.HorizontalFactor) + col; - int offset = GetBlockBufferOffset(component, blockRow, blockCol); - this.DecodeBaseline(ref component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBlockDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsFrameComponent component, int mcu, Stream stream) - { - int blockRow = mcu / component.BlocksPerLine; - int blockCol = mcu % component.BlocksPerLine; - int offset = GetBlockBufferOffset(component, blockRow, blockCol); - this.DecodeDCFirst(ref component, offset, ref dcHuffmanTable, stream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeMcuDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) - { - int mcuRow = mcu / mcusPerLine; - int mcuCol = mcu % mcusPerLine; - int blockRow = (mcuRow * component.VerticalFactor) + row; - int blockCol = (mcuCol * component.HorizontalFactor) + col; - int offset = GetBlockBufferOffset(component, blockRow, blockCol); - this.DecodeDCFirst(ref component, offset, ref dcHuffmanTable, stream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBlockDCSuccessive(ref PdfJsFrameComponent component, int mcu, Stream stream) - { - int blockRow = mcu / component.BlocksPerLine; - int blockCol = mcu % component.BlocksPerLine; - int offset = GetBlockBufferOffset(component, blockRow, blockCol); - this.DecodeDCSuccessive(ref component, offset, stream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeMcuDCSuccessive(ref PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) - { - int mcuRow = mcu / mcusPerLine; - int mcuCol = mcu % mcusPerLine; - int blockRow = (mcuRow * component.VerticalFactor) + row; - int blockCol = (mcuCol * component.HorizontalFactor) + col; - int offset = GetBlockBufferOffset(component, blockRow, blockCol); - this.DecodeDCSuccessive(ref component, offset, stream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBlockACFirst(ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcu, Stream stream) - { - int blockRow = mcu / component.BlocksPerLine; - int blockCol = mcu % component.BlocksPerLine; - int offset = GetBlockBufferOffset(component, blockRow, blockCol); - this.DecodeACFirst(ref component, offset, ref acHuffmanTable, stream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeMcuACFirst(ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) - { - int mcuRow = mcu / mcusPerLine; - int mcuCol = mcu % mcusPerLine; - int blockRow = (mcuRow * component.VerticalFactor) + row; - int blockCol = (mcuCol * component.HorizontalFactor) + col; - int offset = GetBlockBufferOffset(component, blockRow, blockCol); - this.DecodeACFirst(ref component, offset, ref acHuffmanTable, stream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBlockACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcu, Stream stream) - { - int blockRow = mcu / component.BlocksPerLine; - int blockCol = mcu % component.BlocksPerLine; - int offset = GetBlockBufferOffset(component, blockRow, blockCol); - this.DecodeACSuccessive(ref component, offset, ref acHuffmanTable, stream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeMcuACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) - { - int mcuRow = mcu / mcusPerLine; - int mcuCol = mcu % mcusPerLine; - int blockRow = (mcuRow * component.VerticalFactor) + row; - int blockCol = (mcuCol * component.HorizontalFactor) + col; - int offset = GetBlockBufferOffset(component, blockRow, blockCol); - this.DecodeACSuccessive(ref component, offset, ref acHuffmanTable, stream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int ReadBit(Stream stream) - { - // TODO: I wonder if we can do this two bytes at a time; libjpeg turbo seems to do that? - if (this.bitsCount > 0) - { - this.bitsCount--; - return (this.bitsData >> this.bitsCount) & 1; - } - - this.bitsData = stream.ReadByte(); - - if (this.bitsData == -0x1) - { - // We've encountered the end of the file stream which means there's no EOI marker in the image - this.endOfStreamReached = true; - } - - if (this.bitsData == PdfJsJpegConstants.Markers.Prefix) - { - int nextByte = stream.ReadByte(); - if (nextByte != 0) - { -#if DEBUG - Debug.WriteLine($"DecodeScan - Unexpected marker {(this.bitsData << 8) | nextByte:X} at {stream.Position}"); -#endif - - // We've encountered an unexpected marker. Reverse the stream and exit. - this.unexpectedMarkerReached = true; - stream.Position -= 2; - } - - // Unstuff 0 - } - - this.bitsCount = 7; - - return this.bitsData >> 7; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private short DecodeHuffman(ref PdfJsHuffmanTable tree, Stream stream) - { - short code = -1; - - // TODO: Adding this code introduces error into the decoder. - // NOTES # During investigation of the libjpeg implementation it appears that they pull 32bits at a time and operate on those bits - // using 3 methods: FillBits, PeekBits, and ReadBits. We should attempt to do the same. - // It doesn't appear to speed anything up either. - // if (this.bitsUnRead < 8) - // { - // if (this.bitsCount <= 0) - // { - // code = (short)this.ReadBit(stream); - // if (this.endOfStreamReached || this.unexpectedMarkerReached) - // { - // return -1; - // } - // - // this.bitsUnRead += 8; - // } - // - // this.accumulator = (this.accumulator << 8) | this.bitsData; - // int lutIndex = (this.accumulator >> (8 - this.bitsUnRead)) & 0xFF; - // int v = tree.Lookahead[lutIndex]; - // if (v != 0) - // { - // int nb = (v & 0xFF) - 1; - // this.bitsCount -= nb - 1; - // this.bitsUnRead -= nb; - // v = v >> 8; - // return (short)v; - // } - // } - if (code == -1) - { - code = (short)this.ReadBit(stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return -1; - } - } - - // "DECODE", section F.2.2.3, figure F.16, page 109 of T.81 - int i = 1; - - while (code > tree.MaxCode[i]) - { - code <<= 1; - code |= (short)this.ReadBit(stream); - - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return -1; - } - - i++; - } - - int j = tree.ValOffset[i]; - return tree.HuffVal[(j + code) & 0xFF]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int Receive(int length, Stream stream) - { - int n = 0; - while (length > 0) - { - int bit = this.ReadBit(stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return -1; - } - - n = (n << 1) | bit; - length--; - } - - return n; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int ReceiveAndExtend(int length, Stream stream) - { - if (length == 1) - { - return this.ReadBit(stream) == 1 ? 1 : -1; - } - - int n = this.Receive(length, stream); - if (n >= 1 << (length - 1)) - { - return n; - } - - return n + (-1 << length) + 1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeBaseline(ref PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) - { - int t = this.DecodeHuffman(ref dcHuffmanTable, stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return; - } - - int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream); - component.BlockData[offset] = (short)(component.Pred += diff); - - int k = 1; - while (k < 64) - { - int rs = this.DecodeHuffman(ref acHuffmanTable, stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return; - } - - int s = rs & 15; - int r = rs >> 4; - - if (s == 0) - { - if (r < 15) - { - break; - } - - k += 16; - continue; - } - - k += r; - - if (k > 63) - { - break; - } - - byte z = PdfJsQuantizationTables.DctZigZag[k]; - short re = (short)this.ReceiveAndExtend(s, stream); - component.BlockData[offset + z] = re; - k++; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeDCFirst(ref PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream) - { - int t = this.DecodeHuffman(ref dcHuffmanTable, stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return; - } - - int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream) << this.successiveState; - component.BlockData[offset] = (short)(component.Pred += diff); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeDCSuccessive(ref PdfJsFrameComponent component, int offset, Stream stream) - { - int bit = this.ReadBit(stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return; - } - - component.BlockData[offset] |= (short)(bit << this.successiveState); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeACFirst(ref PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) - { - if (this.eobrun > 0) - { - this.eobrun--; - return; - } - - Span componentBlockDataSpan = component.BlockData.Span; - int k = this.specStart; - int e = this.specEnd; - while (k <= e) - { - short rs = this.DecodeHuffman(ref acHuffmanTable, stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return; - } - - int s = rs & 15; - int r = rs >> 4; - - if (s == 0) - { - if (r < 15) - { - this.eobrun = this.Receive(r, stream) + (1 << r) - 1; - break; - } - - k += 16; - continue; - } - - k += r; - byte z = PdfJsQuantizationTables.DctZigZag[k]; - componentBlockDataSpan[offset + z] = (short)(this.ReceiveAndExtend(s, stream) * (1 << this.successiveState)); - k++; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void DecodeACSuccessive(ref PdfJsFrameComponent component, 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]; - switch (this.successiveACState) - { - case 0: // Initial state - short rs = this.DecodeHuffman(ref acHuffmanTable, stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return; - } - - int s = rs & 15; - r = rs >> 4; - if (s == 0) - { - if (r < 15) - { - this.eobrun = this.Receive(r, stream) + (1 << r); - this.successiveACState = 4; - } - else - { - r = 16; - this.successiveACState = 1; - } - } - else - { - if (s != 1) - { - throw new ImageFormatException("Invalid ACn encoding"); - } - - this.successiveACNextValue = this.ReceiveAndExtend(s, stream); - this.successiveACState = r > 0 ? 2 : 3; - } - - continue; - case 1: // Skipping r zero items - case 2: - if (componentBlockDataSpan[offset + z] != 0) - { - int bit = this.ReadBit(stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return; - } - - componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); - } - else - { - r--; - if (r == 0) - { - this.successiveACState = this.successiveACState == 2 ? 3 : 0; - } - } - - break; - case 3: // Set value for a zero item - if (componentBlockDataSpan[offset + z] != 0) - { - int bit = this.ReadBit(stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return; - } - - componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); - } - else - { - componentBlockDataSpan[offset + z] = (short)(this.successiveACNextValue << this.successiveState); - this.successiveACState = 0; - } - - break; - case 4: // Eob - if (componentBlockDataSpan[offset + z] != 0) - { - int bit = this.ReadBit(stream); - if (this.endOfStreamReached || this.unexpectedMarkerReached) - { - return; - } - - componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); - } - - break; - } - - k++; - } - - if (this.successiveACState == 4) - { - this.eobrun--; - if (this.eobrun == 0) - { - this.successiveACState = 0; - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs deleted file mode 100644 index 07c8f9830..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs +++ /dev/null @@ -1,128 +0,0 @@ -namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components -{ - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; - - /// - /// Provides 8-bit lookup tables for converting from YCbCr to Rgb colorspace. - /// Methods to build the tables are based on libjpeg implementation. - /// - internal struct PdfJsYCbCrToRgbTables - { - /// - /// The red red-chrominance table - /// - public static int[] CrRTable = new int[256]; - - /// - /// The blue blue-chrominance table - /// - public static int[] CbBTable = new int[256]; - - /// - /// The green red-chrominance table - /// - public static int[] CrGTable = new int[256]; - - /// - /// The green blue-chrominance table - /// - public static int[] CbGTable = new int[256]; - - // Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places. - private const int ScaleBits = 16; - - private const int Half = 1 << (ScaleBits - 1); - - private const int MinSample = 0; - - private const int HalfSample = 128; - - private const int MaxSample = 255; - - /// - /// Initializes the YCbCr tables - /// - public static void Create() - { - for (int i = 0, x = -128; i <= 255; i++, x++) - { - // i is the actual input pixel value, in the range 0..255 - // The Cb or Cr value we are thinking of is x = i - 128 - // Cr=>R value is nearest int to 1.402 * x - CrRTable[i] = RightShift((Fix(1.402F) * x) + Half); - - // Cb=>B value is nearest int to 1.772 * x - CbBTable[i] = RightShift((Fix(1.772F) * x) + Half); - - // Cr=>G value is scaled-up -0.714136286 - CrGTable[i] = (-Fix(0.714136286F)) * x; - - // Cb => G value is scaled - up - 0.344136286 * x - // We also add in Half so that need not do it in inner loop - CbGTable[i] = ((-Fix(0.344136286F)) * x) + Half; - } - } - - /// - /// Optimized method to pack bytes to the image from the YCbCr color space. - /// - /// The pixel format. - /// The packed pixel. - /// The y luminance component. - /// The cb chroma component. - /// The cr chroma component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void PackYCbCr(ref TPixel packed, byte y, byte cb, byte cr) - where TPixel : struct, IPixel - { - byte r = (byte)(y + CrRTable[cr]).Clamp(0, 255); - - // The values for the G calculation are left scaled up, since we must add them together before rounding. - byte g = (byte)(y + RightShift(CbGTable[cb] + CrGTable[cr])).Clamp(0, 255); - - byte b = (byte)(y + CbBTable[cb]).Clamp(0, 255); - - packed.PackFromRgba32(new Rgba32(r, g, b, 255)); - } - - /// - /// Optimized method to pack bytes to the image from the YccK color space. - /// - /// The pixel format. - /// The packed pixel. - /// The y luminance component. - /// The cb chroma component. - /// The cr chroma component. - /// The keyline component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void PackYccK(ref TPixel packed, byte y, byte cb, byte cr, byte k) - where TPixel : struct, IPixel - { - int c = (MaxSample - (y + CrRTable[cr])).Clamp(0, 255); - - // The values for the G calculation are left scaled up, since we must add them together before rounding. - int m = (MaxSample - (y + RightShift(CbGTable[cb] + CrGTable[cr]))).Clamp(0, 255); - - int cy = (MaxSample - (y + CbBTable[cb])).Clamp(0, 255); - - byte r = (byte)((c * k) / MaxSample); - byte g = (byte)((m * k) / MaxSample); - byte b = (byte)((cy * k) / MaxSample); - - packed.PackFromRgba32(new Rgba32(r, g, b, MaxSample)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Fix(float x) - { - return (int)((x * (1L << ScaleBits)) + 0.5F); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int RightShift(int x) - { - return x >> ScaleBits; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs deleted file mode 100644 index 8b3765e6c..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs +++ /dev/null @@ -1,359 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -// ReSharper disable InconsistentNaming -namespace ImageSharp.Formats.Jpeg.PdfJsPort -{ - /// - /// Contains jpeg constant values - /// - internal static class PdfJsJpegConstants - { - /// - /// Contains marker specific constants - /// - public static class Markers - { - /// - /// The prefix used for all markers. - /// - public const byte Prefix = 0xFF; - - /// - /// The Start of Image marker - /// - public const ushort SOI = 0xFFD8; - - /// - /// The End of Image marker - /// - public const ushort EOI = 0xFFD9; - - /// - /// Application specific marker for marking the jpeg format. - /// - /// - public const ushort APP0 = 0xFFE0; - - /// - /// Application specific marker for marking where to store metadata. - /// - public const ushort APP1 = 0xFFE1; - - /// - /// Application specific marker for marking where to store ICC profile information. - /// - public const ushort APP2 = 0xFFE2; - - /// - /// Application specific marker. - /// - public const ushort APP3 = 0xFFE3; - - /// - /// Application specific marker. - /// - public const ushort APP4 = 0xFFE4; - - /// - /// Application specific marker. - /// - public const ushort APP5 = 0xFFE5; - - /// - /// Application specific marker. - /// - public const ushort APP6 = 0xFFE6; - - /// - /// Application specific marker. - /// - public const ushort APP7 = 0xFFE7; - - /// - /// Application specific marker. - /// - public const ushort APP8 = 0xFFE8; - - /// - /// Application specific marker. - /// - public const ushort APP9 = 0xFFE9; - - /// - /// Application specific marker. - /// - public const ushort APP10 = 0xFFEA; - - /// - /// Application specific marker. - /// - public const ushort APP11 = 0xFFEB; - - /// - /// Application specific marker. - /// - public const ushort APP12 = 0xFFEC; - - /// - /// Application specific marker. - /// - public const ushort APP13 = 0xFFED; - - /// - /// Application specific marker used by Adobe for storing encoding information for DCT filters. - /// - public const ushort APP14 = 0xFFEE; - - /// - /// Application specific marker used by GraphicConverter to store JPEG quality. - /// - public const ushort APP15 = 0xFFEF; - - /// - /// The text comment marker - /// - public const ushort COM = 0xFFFE; - - /// - /// Define Quantization Table(s) marker - /// - /// Specifies one or more quantization tables. - /// - /// - public const ushort DQT = 0xFFDB; - - /// - /// Start of Frame (baseline DCT) - /// - /// Indicates that this is a baseline DCT-based JPEG, and specifies the width, height, number of components, - /// and component subsampling (e.g., 4:2:0). - /// - /// - public const ushort SOF0 = 0xFFC0; - - /// - /// Start Of Frame (Extended Sequential DCT) - /// - /// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components, - /// and component subsampling (e.g., 4:2:0). - /// - /// - public const ushort SOF1 = 0xFFC1; - - /// - /// Start Of Frame (progressive DCT) - /// - /// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components, - /// and component subsampling (e.g., 4:2:0). - /// - /// - public const ushort SOF2 = 0xFFC2; - - /// - /// Define Huffman Table(s) - /// - /// Specifies one or more Huffman tables. - /// - /// - public const ushort DHT = 0xFFC4; - - /// - /// Define Restart Interval - /// - /// Specifies the interval between RSTn markers, in macroblocks.This marker is followed by two bytes indicating the fixed size so it can be treated like any other variable size segment. - /// - /// - public const ushort DRI = 0xFFDD; - - /// - /// Start of Scan - /// - /// Begins a top-to-bottom scan of the image. In baseline DCT JPEG images, there is generally a single scan. - /// Progressive DCT JPEG images usually contain multiple scans. This marker specifies which slice of data it - /// will contain, and is immediately followed by entropy-coded data. - /// - /// - public const ushort SOS = 0xFFDA; - - /// - /// Define First Restart - /// - /// Inserted every r macroblocks, where r is the restart interval set by a DRI marker. - /// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7. - /// - /// - public const ushort RST0 = 0xFFD0; - - /// - /// Define Eigth Restart - /// - /// Inserted every r macroblocks, where r is the restart interval set by a DRI marker. - /// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7. - /// - /// - public const ushort RST7 = 0xFFD7; - - /// - /// Contains JFIF specific markers - /// - public static class JFif - { - /// - /// Represents J in ASCII - /// - public const byte J = 0x4A; - - /// - /// Represents F in ASCII - /// - public const byte F = 0x46; - - /// - /// Represents I in ASCII - /// - public const byte I = 0x49; - - /// - /// Represents the null "0" marker - /// - public const byte Null = 0x0; - } - - /// - /// Contains Adobe specific markers - /// - public static class Adobe - { - /// - /// Represents A in ASCII - /// - public const byte A = 0x41; - - /// - /// Represents d in ASCII - /// - public const byte D = 0x64; - - /// - /// Represents b in ASCII - /// - public const byte O = 0x6F; - - /// - /// Represents b in ASCII - /// - public const byte B = 0x62; - - /// - /// Represents e in ASCII - /// - public const byte E = 0x65; - - /// - /// The color transform is unknown.(RGB or CMYK) - /// - public const byte ColorTransformUnknown = 0; - - /// - /// The color transform is YCbCr (luminance, red chroma, blue chroma) - /// - public const byte ColorTransformYCbCr = 1; - - /// - /// The color transform is YCCK (luminance, red chroma, blue chroma, keyline) - /// - public const byte ColorTransformYcck = 2; - } - - /// - /// Contains EXIF specific markers - /// - public static class Exif - { - /// - /// Represents E in ASCII - /// - public const byte E = 0x45; - - /// - /// Represents x in ASCII - /// - public const byte X = 0x78; - - /// - /// Represents i in ASCII - /// - public const byte I = 0x69; - - /// - /// Represents f in ASCII - /// - public const byte F = 0x66; - - /// - /// Represents the null "0" marker - /// - public const byte Null = 0x0; - } - - /// - /// Contains ICC specific markers - /// - public static class ICC - { - /// - /// Represents I in ASCII - /// - public const byte I = 0x49; - - /// - /// Represents C in ASCII - /// - public const byte C = 0x43; - - /// - /// Represents _ in ASCII - /// - public const byte UnderScore = 0x5F; - - /// - /// Represents P in ASCII - /// - public const byte P = 0x50; - - /// - /// Represents R in ASCII - /// - public const byte R = 0x52; - - /// - /// Represents O in ASCII - /// - public const byte O = 0x4F; - - /// - /// Represents F in ASCII - /// - public const byte F = 0x46; - - /// - /// Represents L in ASCII - /// - public const byte L = 0x4C; - - /// - /// Represents E in ASCII - /// - public const byte E = 0x45; - - /// - /// Represents the null "0" marker - /// - public const byte Null = 0x0; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs deleted file mode 100644 index 25f739704..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace ImageSharp.Formats.Jpeg.PdfJsPort -{ - using System; - using System.IO; - - using ImageSharp.PixelFormats; - - internal sealed class PdfJsJpegDecoder : IImageDecoder, IJpegDecoderOptions - { - public bool IgnoreMetadata { get; set; } - - public Image Decode(Configuration configuration, Stream stream) - where TPixel : struct, IPixel - { - using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) - { - return decoder.Decode(stream); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs deleted file mode 100644 index ce90f0104..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ /dev/null @@ -1,985 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpeg.PdfJsPort -{ - using System; - using System.IO; - using System.Runtime.CompilerServices; - - using ImageSharp.Formats.Jpeg.PdfJsPort.Components; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Performs the jpeg decoding operation. - /// Ported from with additional fixes to handle common encoding errors - /// - internal sealed class PdfJsJpegDecoderCore : IDisposable - { - /// - /// The global configuration - /// - private readonly Configuration configuration; - - /// - /// Gets the temporary buffer used to store bytes read from the stream. - /// - private readonly byte[] temp = new byte[2 * 16 * 4]; - - private readonly byte[] markerBuffer = new byte[2]; - - private PdfJsQuantizationTables quantizationTables; - - private PdfJsHuffmanTables dcHuffmanTables; - - private PdfJsHuffmanTables acHuffmanTables; - - private PdfJsFrame frame; - - private PdfJsComponentBlocks components; - - private PdfJsJpegPixelArea pixelArea; - - private ushort resetInterval; - - private int imageWidth; - - private int imageHeight; - - private int numberOfComponents; - - /// - /// Whether the image has a EXIF header - /// - private bool isExif; - - /// - /// Contains information about the JFIF marker - /// - private PdfJsJFif jFif; - - /// - /// Contains information about the Adobe marker - /// - private PdfJsAdobe adobe; - - /// - /// Initializes static members of the class. - /// - static PdfJsJpegDecoderCore() - { - PdfJsYCbCrToRgbTables.Create(); - } - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The options. - public PdfJsJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) - { - this.configuration = configuration ?? Configuration.Default; - this.IgnoreMetadata = options.IgnoreMetadata; - } - - /// - /// Gets the input stream. - /// - public Stream InputStream { get; private set; } - - /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; } - - /// - /// Finds the next file marker within the byte stream. - /// - /// The buffer to read file markers to - /// The input stream - /// The - public static PdfJsFileMarker FindNextFileMarker(byte[] marker, Stream stream) - { - int value = stream.Read(marker, 0, 2); - - if (value == 0) - { - return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, (int)stream.Length - 2); - } - - if (marker[0] == PdfJsJpegConstants.Markers.Prefix) - { - // According to Section B.1.1.2: - // "Any marker may optionally be preceded by any number of fill bytes, which are bytes assigned code 0xFF." - while (marker[1] == PdfJsJpegConstants.Markers.Prefix) - { - int suffix = stream.ReadByte(); - if (suffix == -1) - { - return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, (int)stream.Length - 2); - } - - marker[1] = (byte)value; - } - - return new PdfJsFileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2)); - } - - return new PdfJsFileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2), true); - } - - /// - /// Decodes the image from the specified and sets the data to image. - /// - /// The pixel format. - /// The stream, where the image should be. - /// The decoded image. - public Image Decode(Stream stream) - where TPixel : struct, IPixel - { - this.InputStream = stream; - - var metadata = new ImageMetaData(); - this.ParseStream(metadata, false); - - var image = new Image(this.configuration, this.imageWidth, this.imageHeight, metadata); - this.FillPixelData(image); - this.AssignResolution(image); - return image; - } - - /// - public void Dispose() - { - this.frame?.Dispose(); - this.components?.Dispose(); - this.quantizationTables?.Dispose(); - this.dcHuffmanTables?.Dispose(); - this.acHuffmanTables?.Dispose(); - this.pixelArea.Dispose(); - - // Set large fields to null. - this.frame = null; - this.components = null; - this.quantizationTables = null; - this.dcHuffmanTables = null; - this.acHuffmanTables = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int GetBlockBufferOffset(ref PdfJsComponent component, int row, int col) - { - return 64 * (((component.BlocksPerLine + 1) * row) + col); - } - - /// - /// Parses the input stream for file markers - /// - /// Contains the metadata for an image - /// Whether to decode metadata only. - private void ParseStream(ImageMetaData metaData, bool metadataOnly) - { - // TODO: metadata only logic - // Check for the Start Of Image marker. - var fileMarker = new PdfJsFileMarker(this.ReadUint16(), 0); - if (fileMarker.Marker != PdfJsJpegConstants.Markers.SOI) - { - throw new ImageFormatException("Missing SOI marker."); - } - - ushort marker = this.ReadUint16(); - fileMarker = new PdfJsFileMarker(marker, (int)this.InputStream.Position - 2); - - this.quantizationTables = new PdfJsQuantizationTables(); - this.dcHuffmanTables = new PdfJsHuffmanTables(); - this.acHuffmanTables = new PdfJsHuffmanTables(); - - while (fileMarker.Marker != PdfJsJpegConstants.Markers.EOI) - { - // Get the marker length - int remaining = this.ReadUint16() - 2; - - switch (fileMarker.Marker) - { - case PdfJsJpegConstants.Markers.APP0: - this.ProcessApplicationHeaderMarker(remaining); - break; - - case PdfJsJpegConstants.Markers.APP1: - this.ProcessApp1Marker(remaining, metaData); - break; - - case PdfJsJpegConstants.Markers.APP2: - this.ProcessApp2Marker(remaining, metaData); - break; - case PdfJsJpegConstants.Markers.APP3: - case PdfJsJpegConstants.Markers.APP4: - case PdfJsJpegConstants.Markers.APP5: - case PdfJsJpegConstants.Markers.APP6: - case PdfJsJpegConstants.Markers.APP7: - case PdfJsJpegConstants.Markers.APP8: - case PdfJsJpegConstants.Markers.APP9: - case PdfJsJpegConstants.Markers.APP10: - case PdfJsJpegConstants.Markers.APP11: - case PdfJsJpegConstants.Markers.APP12: - case PdfJsJpegConstants.Markers.APP13: - this.InputStream.Skip(remaining); - break; - - case PdfJsJpegConstants.Markers.APP14: - this.ProcessApp14Marker(remaining); - break; - - case PdfJsJpegConstants.Markers.APP15: - case PdfJsJpegConstants.Markers.COM: - this.InputStream.Skip(remaining); - break; - - case PdfJsJpegConstants.Markers.DQT: - this.ProcessDefineQuantizationTablesMarker(remaining); - break; - - case PdfJsJpegConstants.Markers.SOF0: - case PdfJsJpegConstants.Markers.SOF1: - case PdfJsJpegConstants.Markers.SOF2: - this.ProcessStartOfFrameMarker(remaining, fileMarker); - break; - - case PdfJsJpegConstants.Markers.DHT: - this.ProcessDefineHuffmanTablesMarker(remaining); - break; - - case PdfJsJpegConstants.Markers.DRI: - this.ProcessDefineRestartIntervalMarker(remaining); - break; - - case PdfJsJpegConstants.Markers.SOS: - this.ProcessStartOfScanMarker(); - break; - } - - // Read on. - fileMarker = FindNextFileMarker(this.markerBuffer, this.InputStream); - } - - this.imageWidth = this.frame.SamplesPerLine; - this.imageHeight = this.frame.Scanlines; - this.components = new PdfJsComponentBlocks { Components = new PdfJsComponent[this.frame.ComponentCount] }; - - for (int i = 0; i < this.components.Components.Length; i++) - { - ref var frameComponent = ref this.frame.Components[i]; - var component = new PdfJsComponent - { - Scale = new System.Numerics.Vector2( - frameComponent.HorizontalFactor / (float)this.frame.MaxHorizontalFactor, - frameComponent.VerticalFactor / (float)this.frame.MaxVerticalFactor), - BlocksPerLine = frameComponent.BlocksPerLine, - BlocksPerColumn = frameComponent.BlocksPerColumn - }; - - this.BuildComponentData(ref component, ref frameComponent); - this.components.Components[i] = component; - } - - this.numberOfComponents = this.components.Components.Length; - } - - /// - /// Fills the given image with the color data - /// - /// The pixel format. - /// The image - private void FillPixelData(Image image) - where TPixel : struct, IPixel - { - if (this.numberOfComponents > 4) - { - throw new ImageFormatException( - $"Unsupported color mode. Max components 4; found {this.numberOfComponents}"); - } - - this.pixelArea = new PdfJsJpegPixelArea(image.Width, image.Height, this.numberOfComponents); - this.pixelArea.LinearizeBlockData(this.components, image.Width, image.Height); - - if (this.numberOfComponents == 1) - { - this.FillGrayScaleImage(image); - return; - } - - if (this.numberOfComponents == 3) - { - if (this.adobe.Equals(default(PdfJsAdobe)) - || this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYCbCr) - { - this.FillYCbCrImage(image); - } - else if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformUnknown) - { - this.FillRgbImage(image); - } - } - - if (this.numberOfComponents == 4) - { - if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYcck) - { - this.FillYcckImage(image); - } - else - { - this.FillCmykImage(image); - } - } - } - - /// - /// Assigns the horizontal and vertical resolution to the image if it has a JFIF header or EXIF metadata. - /// - /// The pixel format. - /// The image to assign the resolution to. - private void AssignResolution(Image image) - where TPixel : struct, IPixel - { - if (this.isExif) - { - ExifValue horizontal = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); - ExifValue vertical = image.MetaData.ExifProfile.GetValue(ExifTag.YResolution); - double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0; - double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0; - - if (horizontalValue > 0 && verticalValue > 0) - { - image.MetaData.HorizontalResolution = horizontalValue; - image.MetaData.VerticalResolution = verticalValue; - } - } - else if (this.jFif.XDensity > 0 && this.jFif.YDensity > 0) - { - image.MetaData.HorizontalResolution = this.jFif.XDensity; - image.MetaData.VerticalResolution = this.jFif.YDensity; - } - } - - /// - /// Processes the application header containing the JFIF identifier plus extra data. - /// - /// The remaining bytes in the segment block. - private void ProcessApplicationHeaderMarker(int remaining) - { - if (remaining < 5) - { - // Skip the application header length - this.InputStream.Skip(remaining); - return; - } - - this.InputStream.Read(this.temp, 0, 13); - remaining -= 13; - - bool isJfif = this.temp[0] == PdfJsJpegConstants.Markers.JFif.J - && this.temp[1] == PdfJsJpegConstants.Markers.JFif.F - && this.temp[2] == PdfJsJpegConstants.Markers.JFif.I - && this.temp[3] == PdfJsJpegConstants.Markers.JFif.F - && this.temp[4] == PdfJsJpegConstants.Markers.JFif.Null; - - if (isJfif) - { - this.jFif = new PdfJsJFif - { - MajorVersion = this.temp[5], - MinorVersion = this.temp[6], - DensityUnits = this.temp[7], - XDensity = (short)((this.temp[8] << 8) | this.temp[9]), - YDensity = (short)((this.temp[10] << 8) | this.temp[11]) - }; - } - - // TODO: thumbnail - if (remaining > 0) - { - this.InputStream.Skip(remaining); - } - } - - /// - /// Processes the App1 marker retrieving any stored metadata - /// - /// The remaining bytes in the segment block. - /// The image. - private void ProcessApp1Marker(int remaining, ImageMetaData metadata) - { - if (remaining < 6 || this.IgnoreMetadata) - { - // Skip the application header length - this.InputStream.Skip(remaining); - return; - } - - byte[] profile = new byte[remaining]; - this.InputStream.Read(profile, 0, remaining); - - if (profile[0] == PdfJsJpegConstants.Markers.Exif.E && profile[1] == PdfJsJpegConstants.Markers.Exif.X - && profile[2] == PdfJsJpegConstants.Markers.Exif.I && profile[3] == PdfJsJpegConstants.Markers.Exif.F - && profile[4] == PdfJsJpegConstants.Markers.Exif.Null - && profile[5] == PdfJsJpegConstants.Markers.Exif.Null) - { - this.isExif = true; - metadata.ExifProfile = new ExifProfile(profile); - } - } - - /// - /// Processes the App2 marker retrieving any stored ICC profile information - /// - /// The remaining bytes in the segment block. - /// The image. - private void ProcessApp2Marker(int remaining, ImageMetaData metadata) - { - // Length is 14 though we only need to check 12. - const int Icclength = 14; - if (remaining < Icclength || this.IgnoreMetadata) - { - this.InputStream.Skip(remaining); - return; - } - - byte[] identifier = new byte[Icclength]; - this.InputStream.Read(identifier, 0, Icclength); - remaining -= Icclength; // We have read it by this point - - if (identifier[0] == PdfJsJpegConstants.Markers.ICC.I && identifier[1] == PdfJsJpegConstants.Markers.ICC.C - && identifier[2] == PdfJsJpegConstants.Markers.ICC.C - && identifier[3] == PdfJsJpegConstants.Markers.ICC.UnderScore - && identifier[4] == PdfJsJpegConstants.Markers.ICC.P - && identifier[5] == PdfJsJpegConstants.Markers.ICC.R - && identifier[6] == PdfJsJpegConstants.Markers.ICC.O - && identifier[7] == PdfJsJpegConstants.Markers.ICC.F - && identifier[8] == PdfJsJpegConstants.Markers.ICC.I - && identifier[9] == PdfJsJpegConstants.Markers.ICC.L - && identifier[10] == PdfJsJpegConstants.Markers.ICC.E - && identifier[11] == PdfJsJpegConstants.Markers.ICC.Null) - { - byte[] profile = new byte[remaining]; - this.InputStream.Read(profile, 0, remaining); - - if (metadata.IccProfile == null) - { - metadata.IccProfile = new IccProfile(profile); - } - else - { - metadata.IccProfile.Extend(profile); - } - } - else - { - // Not an ICC profile we can handle. Skip the remaining bytes so we can carry on and ignore this. - this.InputStream.Skip(remaining); - } - } - - /// - /// Processes the application header containing the Adobe identifier - /// which stores image encoding information for DCT filters. - /// - /// The remaining bytes in the segment block. - private void ProcessApp14Marker(int remaining) - { - if (remaining < 12) - { - // Skip the application header length - this.InputStream.Skip(remaining); - return; - } - - this.InputStream.Read(this.temp, 0, 12); - remaining -= 12; - - bool isAdobe = this.temp[0] == PdfJsJpegConstants.Markers.Adobe.A - && this.temp[1] == PdfJsJpegConstants.Markers.Adobe.D - && this.temp[2] == PdfJsJpegConstants.Markers.Adobe.O - && this.temp[3] == PdfJsJpegConstants.Markers.Adobe.B - && this.temp[4] == PdfJsJpegConstants.Markers.Adobe.E; - - if (isAdobe) - { - this.adobe = new PdfJsAdobe - { - DCTEncodeVersion = (short)((this.temp[5] << 8) | this.temp[6]), - APP14Flags0 = (short)((this.temp[7] << 8) | this.temp[8]), - APP14Flags1 = (short)((this.temp[9] << 8) | this.temp[10]), - ColorTransform = this.temp[11] - }; - } - - if (remaining > 0) - { - this.InputStream.Skip(remaining); - } - } - - /// - /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1. - /// - /// The remaining bytes in the segment block. - /// - /// Thrown if the tables do not match the header - /// - private void ProcessDefineQuantizationTablesMarker(int remaining) - { - while (remaining > 0) - { - bool done = false; - remaining--; - int quantizationTableSpec = this.InputStream.ReadByte(); - - switch (quantizationTableSpec >> 4) - { - case 0: - { - // 8 bit values - if (remaining < 64) - { - done = true; - break; - } - - this.InputStream.Read(this.temp, 0, 64); - remaining -= 64; - - Span tableSpan = - this.quantizationTables.Tables.GetRowSpan(quantizationTableSpec & 15); - for (int j = 0; j < 64; j++) - { - tableSpan[PdfJsQuantizationTables.DctZigZag[j]] = this.temp[j]; - } - } - - break; - case 1: - { - // 16 bit values - if (remaining < 128) - { - done = true; - break; - } - - this.InputStream.Read(this.temp, 0, 128); - remaining -= 128; - - Span tableSpan = - this.quantizationTables.Tables.GetRowSpan(quantizationTableSpec & 15); - for (int j = 0; j < 64; j++) - { - tableSpan[PdfJsQuantizationTables.DctZigZag[j]] = - (short)((this.temp[2 * j] << 8) | this.temp[(2 * j) + 1]); - } - } - - break; - default: - throw new ImageFormatException("Bad Tq index value"); - } - - if (done) - { - break; - } - } - - if (remaining != 0) - { - throw new ImageFormatException("DQT has wrong length"); - } - } - - /// - /// Processes the Start of Frame marker. Specified in section B.2.2. - /// - /// The remaining bytes in the segment block. - /// The current frame marker. - private void ProcessStartOfFrameMarker(int remaining, PdfJsFileMarker frameMarker) - { - if (this.frame != null) - { - throw new ImageFormatException("Multiple SOF markers. Only single frame jpegs supported."); - } - - this.InputStream.Read(this.temp, 0, remaining); - - this.frame = new PdfJsFrame - { - Extended = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF1, - Progressive = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF2, - Precision = this.temp[0], - Scanlines = (short)((this.temp[1] << 8) | this.temp[2]), - SamplesPerLine = (short)((this.temp[3] << 8) | this.temp[4]), - ComponentCount = this.temp[5] - }; - - int maxH = 0; - int maxV = 0; - int index = 6; - - // No need to pool this. They max out at 4 - this.frame.ComponentIds = new byte[this.frame.ComponentCount]; - this.frame.Components = new PdfJsFrameComponent[this.frame.ComponentCount]; - - for (int i = 0; i < this.frame.Components.Length; i++) - { - int h = this.temp[index + 1] >> 4; - int v = this.temp[index + 1] & 15; - - if (maxH < h) - { - maxH = h; - } - - if (maxV < v) - { - maxV = v; - } - - ref var component = ref this.frame.Components[i]; - component.Id = this.temp[index]; - component.HorizontalFactor = h; - component.VerticalFactor = v; - component.QuantizationIdentifier = this.temp[index + 2]; - - this.frame.ComponentIds[i] = component.Id; - - index += 3; - } - - this.frame.MaxHorizontalFactor = maxH; - this.frame.MaxVerticalFactor = maxV; - this.PrepareComponents(); - } - - /// - /// Processes a Define Huffman Table marker, and initializes a huffman - /// struct from its contents. Specified in section B.2.4.2. - /// - /// The remaining bytes in the segment block. - private void ProcessDefineHuffmanTablesMarker(int remaining) - { - if (remaining < 17) - { - throw new ImageFormatException($"DHT has wrong length: {remaining}"); - } - - using (var huffmanData = Buffer.CreateClean(256)) - { - for (int i = 2; i < remaining;) - { - byte huffmanTableSpec = (byte)this.InputStream.ReadByte(); - this.InputStream.Read(huffmanData.Array, 0, 16); - - using (var codeLengths = Buffer.CreateClean(17)) - { - int codeLengthSum = 0; - - for (int j = 1; j < 17; j++) - { - codeLengthSum += codeLengths[j] = huffmanData[j - 1]; - } - - using (var huffmanValues = Buffer.CreateClean(256)) - { - this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum); - - i += 17 + codeLengthSum; - - this.BuildHuffmanTable( - huffmanTableSpec >> 4 == 0 ? this.dcHuffmanTables : this.acHuffmanTables, - huffmanTableSpec & 15, - codeLengths.Array, - huffmanValues.Array); - } - } - } - } - } - - /// - /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in - /// macroblocks - /// - /// The remaining bytes in the segment block. - private void ProcessDefineRestartIntervalMarker(int remaining) - { - if (remaining != 2) - { - throw new ImageFormatException($"DRI has wrong length: {remaining}"); - } - - this.resetInterval = this.ReadUint16(); - } - - /// - /// Processes the SOS (Start of scan marker). - /// - private void ProcessStartOfScanMarker() - { - int selectorsCount = this.InputStream.ReadByte(); - int componentIndex = -1; - for (int i = 0; i < selectorsCount; i++) - { - componentIndex = -1; - int selector = this.InputStream.ReadByte(); - - for (int j = 0; j < this.frame.ComponentIds.Length; j++) - { - byte id = this.frame.ComponentIds[j]; - if (selector == id) - { - componentIndex = j; - } - } - - if (componentIndex < 0) - { - throw new ImageFormatException("Unknown component selector"); - } - - ref PdfJsFrameComponent component = ref this.frame.Components[componentIndex]; - int tableSpec = this.InputStream.ReadByte(); - component.DCHuffmanTableId = tableSpec >> 4; - component.ACHuffmanTableId = tableSpec & 15; - } - - this.InputStream.Read(this.temp, 0, 3); - - int spectralStart = this.temp[0]; - int spectralEnd = this.temp[1]; - int successiveApproximation = this.temp[2]; - var scanDecoder = default(PdfJsScanDecoder); - - scanDecoder.DecodeScan( - this.frame, - this.InputStream, - this.dcHuffmanTables, - this.acHuffmanTables, - this.frame.Components, - componentIndex, - selectorsCount, - this.resetInterval, - spectralStart, - spectralEnd, - successiveApproximation >> 4, - successiveApproximation & 15); - } - - /// - /// Build the data for the given component - /// - /// The component - /// The frame component - private void BuildComponentData(ref PdfJsComponent component, ref PdfJsFrameComponent frameComponent) - { - int blocksPerLine = component.BlocksPerLine; - int blocksPerColumn = component.BlocksPerColumn; - using (var computationBuffer = Buffer.CreateClean(64)) - - // IDCTFast Only - // using (var multiplicationBuffer = Buffer.CreateClean(64)) - { - Span quantizationTable = - this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationIdentifier); - Span computationBufferSpan = computationBuffer; - - // For AA&N IDCT method, multiplier are equal to quantization - // coefficients scaled by scalefactor[row]*scalefactor[col], where - // scalefactor[0] = 1 - // scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 - // For integer operation, the multiplier table is to be scaled by 12. - - // IDCTFast Only - // Span multiplierSpan = multiplicationBuffer; - // for (int i = 0; i < 64; i++) - // { - // multiplierSpan[i] = (short)PdfJsIDCT.Descale(quantizationTable[i] * PdfJsIDCT.Aanscales[i], 12); - // } - for (int blockRow = 0; blockRow < blocksPerColumn; blockRow++) - { - for (int blockCol = 0; blockCol < blocksPerLine; blockCol++) - { - int offset = GetBlockBufferOffset(ref component, blockRow, blockCol); - - // IDCTFast Only - // PdfJsIDCT.QuantizeAndInverseFast( - // ref frameComponent, - // offset, - // ref computationBufferSpan, - // ref multiplierSpan); - PdfJsIDCT.QuantizeAndInverse( - ref frameComponent, - offset, - ref computationBufferSpan, - ref quantizationTable); - } - } - } - - component.Output = frameComponent.BlockData; - } - - /// - /// Builds the huffman tables - /// - /// The tables - /// The table index - /// The codelengths - /// The values - private void BuildHuffmanTable(PdfJsHuffmanTables tables, int index, byte[] codeLengths, byte[] values) - { - tables[index] = new PdfJsHuffmanTable(codeLengths, values); - } - - /// - /// Allocates the frame component blocks - /// - private void PrepareComponents() - { - int mcusPerLine = (int)MathF.Ceiling(this.frame.SamplesPerLine / 8F / this.frame.MaxHorizontalFactor); - int mcusPerColumn = (int)MathF.Ceiling(this.frame.Scanlines / 8F / this.frame.MaxVerticalFactor); - - for (int i = 0; i < this.frame.ComponentCount; i++) - { - ref var component = ref this.frame.Components[i]; - int blocksPerLine = (int)MathF.Ceiling( - MathF.Ceiling(this.frame.SamplesPerLine / 8F) * component.HorizontalFactor - / this.frame.MaxHorizontalFactor); - int blocksPerColumn = (int)MathF.Ceiling( - MathF.Ceiling(this.frame.Scanlines / 8F) * component.VerticalFactor / this.frame.MaxVerticalFactor); - int blocksPerLineForMcu = mcusPerLine * component.HorizontalFactor; - int blocksPerColumnForMcu = mcusPerColumn * component.VerticalFactor; - - int blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1); - - // Pooled. Disposed via frame disposal - component.BlockData = Buffer.CreateClean(blocksBufferSize); - component.BlocksPerLine = blocksPerLine; - component.BlocksPerColumn = blocksPerColumn; - } - - this.frame.McusPerLine = mcusPerLine; - this.frame.McusPerColumn = mcusPerColumn; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FillGrayScaleImage(Image image) - where TPixel : struct, IPixel - { - for (int y = 0; y < image.Height; y++) - { - Span imageRowSpan = image.GetRowSpan(y); - Span areaRowSpan = this.pixelArea.GetRowSpan(y); - - for (int x = 0; x < image.Width; x++) - { - ref byte luminance = ref areaRowSpan[x]; - ref TPixel pixel = ref imageRowSpan[x]; - var rgba = new Rgba32(luminance, luminance, luminance); - pixel.PackFromRgba32(rgba); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FillYCbCrImage(Image image) - where TPixel : struct, IPixel - { - for (int y = 0; y < image.Height; y++) - { - Span imageRowSpan = image.GetRowSpan(y); - Span areaRowSpan = this.pixelArea.GetRowSpan(y); - for (int x = 0, o = 0; x < image.Width; x++, o += 3) - { - ref byte yy = ref areaRowSpan[o]; - ref byte cb = ref areaRowSpan[o + 1]; - ref byte cr = ref areaRowSpan[o + 2]; - ref TPixel pixel = ref imageRowSpan[x]; - PdfJsYCbCrToRgbTables.PackYCbCr(ref pixel, yy, cb, cr); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FillYcckImage(Image image) - where TPixel : struct, IPixel - { - for (int y = 0; y < image.Height; y++) - { - Span imageRowSpan = image.GetRowSpan(y); - Span areaRowSpan = this.pixelArea.GetRowSpan(y); - for (int x = 0, o = 0; x < image.Width; x++, o += 4) - { - ref byte yy = ref areaRowSpan[o]; - ref byte cb = ref areaRowSpan[o + 1]; - ref byte cr = ref areaRowSpan[o + 2]; - ref byte k = ref areaRowSpan[o + 3]; - - ref TPixel pixel = ref imageRowSpan[x]; - PdfJsYCbCrToRgbTables.PackYccK(ref pixel, yy, cb, cr, k); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FillCmykImage(Image image) - where TPixel : struct, IPixel - { - for (int y = 0; y < image.Height; y++) - { - Span imageRowSpan = image.GetRowSpan(y); - Span areaRowSpan = this.pixelArea.GetRowSpan(y); - for (int x = 0, o = 0; x < image.Width; x++, o += 4) - { - ref byte c = ref areaRowSpan[o]; - ref byte m = ref areaRowSpan[o + 1]; - ref byte cy = ref areaRowSpan[o + 2]; - ref byte k = ref areaRowSpan[o + 3]; - - byte r = (byte)((c * k) / 255); - byte g = (byte)((m * k) / 255); - byte b = (byte)((cy * k) / 255); - - ref TPixel pixel = ref imageRowSpan[x]; - var rgba = new Rgba32(r, g, b); - pixel.PackFromRgba32(rgba); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FillRgbImage(Image image) - where TPixel : struct, IPixel - { - for (int y = 0; y < image.Height; y++) - { - Span imageRowSpan = image.GetRowSpan(y); - Span areaRowSpan = this.pixelArea.GetRowSpan(y); - - PixelOperations.Instance.PackFromRgb24Bytes(areaRowSpan, imageRowSpan, image.Width); - } - } - - /// - /// Reads a from the stream advancing it by two bytes - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ushort ReadUint16() - { - this.InputStream.Read(this.markerBuffer, 0, 2); - return (ushort)((this.markerBuffer[0] << 8) | this.markerBuffer[1]); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index e77ebb92b..234f4dcdc 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -11,7 +11,7 @@ namespace ImageSharp.Tests using System.Linq; using ImageSharp.Formats; - using ImageSharp.Formats.Jpeg.PdfJsPort; + using ImageSharp.PixelFormats; using ImageSharp.Tests.TestUtilities.ImageComparison; @@ -40,6 +40,10 @@ namespace ImageSharp.Tests private ITestOutputHelper Output { get; } + private static IImageDecoder OriginalDecoder => new JpegDecoder(); + + private static IImageDecoder GetPdfJsDecoder => throw new NotImplementedException(); + private float GetDifferenceInPercents(Image image, TestImageProvider provider) where TPixel : struct, IPixel { @@ -66,13 +70,13 @@ namespace ImageSharp.Tests this.Output.WriteLine(provider.SourceFileOrDescription); provider.Utility.TestName = nameof(this.DecodeBaselineJpeg); - using (Image image = provider.GetImage(new JpegDecoder())) + using (Image image = provider.GetImage(OriginalDecoder)) { double d = this.GetDifferenceInPercents(image, provider); this.Output.WriteLine($"Difference using ORIGINAL decoder: {d:0.0000}%"); } - using (Image image = provider.GetImage(new PdfJsJpegDecoder())) + using (Image image = provider.GetImage(GetPdfJsDecoder)) { double d = this.GetDifferenceInPercents(image, provider); this.Output.WriteLine($"Difference using PDFJS decoder: {d:0.0000}%"); @@ -84,7 +88,7 @@ namespace ImageSharp.Tests public void DecodeBaselineJpeg(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(new JpegDecoder())) + using (Image image = provider.GetImage(OriginalDecoder)) { image.DebugSave(provider); image.CompareToReferenceOutput(provider, VeryTolerantJpegComparer, appendPixelTypeToFileName: false); @@ -96,7 +100,7 @@ namespace ImageSharp.Tests public void DecodeBaselineJpeg_PdfJs(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(new PdfJsJpegDecoder())) + using (Image image = provider.GetImage(GetPdfJsDecoder)) { image.DebugSave(provider); @@ -110,7 +114,7 @@ namespace ImageSharp.Tests public void DecodeProgressiveJpeg(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(new JpegDecoder())) + using (Image image = provider.GetImage(OriginalDecoder)) { image.DebugSave(provider, VeryTolerantJpegComparer); } @@ -122,7 +126,7 @@ namespace ImageSharp.Tests public void DecodeProgressiveJpeg_PdfJs(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(new PdfJsJpegDecoder())) + using (Image image = provider.GetImage(GetPdfJsDecoder)) { image.DebugSave(provider, VeryTolerantJpegComparer); }