From 38d6e3277afb7b7187a84aa14fd92cd719b7cf2c Mon Sep 17 00:00:00 2001 From: antonfirsov Date: Tue, 27 Dec 2016 02:29:55 +0100 Subject: [PATCH] started work on DecoderScanProcessor --- ImageSharp.sln.DotSettings | 1 + .../Decoder/DecoderScanProcessor.cs | 229 ++++++++++++++ .../Jpg/Components/Decoder/HuffmanTree.cs | 6 +- .../Formats/Jpg/Components/Decoder/Scan.cs | 26 ++ src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs | 288 +++++++++--------- 5 files changed, 397 insertions(+), 153 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs create mode 100644 src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings index 5acec071a1..b0f5aa6927 100644 --- a/ImageSharp.sln.DotSettings +++ b/ImageSharp.sln.DotSettings @@ -345,6 +345,7 @@ FDCT IDCT JPEG + MCU PNG RGB RLE diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs new file mode 100644 index 0000000000..b0c44c65b5 --- /dev/null +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs @@ -0,0 +1,229 @@ +namespace ImageSharp.Formats.Jpg +{ + using System; + + internal unsafe struct DecoderScanProcessor + { + public struct ComponentData + { + public Block8x8F Block; + + public Block8x8F Temp1; + + public Block8x8F Temp2; + + public UnzigData Unzig; + + public fixed byte ScanData [3 * JpegDecoderCore.MaxComponents]; + + public fixed int Dc [JpegDecoderCore.MaxComponents]; + + public static ComponentData Create() + { + ComponentData data = default(ComponentData); + data.Unzig = UnzigData.Create(); + return data; + } + + } + + public struct ComponentPointers + { + public Block8x8F* Block; + + public Block8x8F* Temp1; + + public Block8x8F* Temp2; + + public int* Unzig; + + public Scan* Scan; + + public int* Dc; + + public ComponentPointers(ComponentData* basePtr) + { + this.Block = &basePtr->Block; + this.Temp1 = &basePtr->Temp1; + this.Temp2 = &basePtr->Temp2; + this.Unzig = basePtr->Unzig.Data; + this.Scan = (Scan*) basePtr->ScanData; + this.Dc = basePtr->Dc; + } + } + + public int bx; + + public int by; + + public int zigStart; + + public int zigEnd; + + public int ah; + + public int al; + + public int mxx; + + public int myy; + + public ComponentData Data; + + public ComponentPointers Pointers; + + public static void Init(DecoderScanProcessor* p, JpegDecoderCore decoder, int remaining) + { + p->Pointers = new ComponentPointers(&p->Data); + + } + + private void InitCommon(JpegDecoderCore decoder, int remaining) + { + if (decoder.ComponentCount == 0) + { + throw new ImageFormatException("Missing SOF marker"); + } + + if (remaining < 6 || 4 + (2 * decoder.ComponentCount) < remaining || remaining % 2 != 0) + { + throw new ImageFormatException("SOS has wrong length"); + } + + decoder.ReadFull(decoder.Temp, 0, remaining); + byte scanComponentCount = decoder.Temp[0]; + + int scanComponentCountX2 = 2 * scanComponentCount; + if (remaining != 4 + scanComponentCountX2) + { + throw new ImageFormatException("SOS length inconsistent with number of components"); + } + + int totalHv = 0; + + for (int i = 0; i < scanComponentCount; i++) + { + this.ProcessScanImpl(decoder, i, ref this.Pointers.Scan[i], ref totalHv); + } + // Section B.2.3 states that if there is more than one component then the + // total H*V values in a scan must be <= 10. + if (decoder.ComponentCount > 1 && totalHv > 10) + { + throw new ImageFormatException("Total sampling factors too large."); + } + + this.zigEnd = Block8x8F.ScalarCount - 1; + + if (decoder.IsProgressive) + { + this.zigStart = decoder.Temp[1 + scanComponentCountX2]; + this.zigEnd = decoder.Temp[2 + scanComponentCountX2]; + this.ah = decoder.Temp[3 + scanComponentCountX2] >> 4; + this.al = decoder.Temp[3 + scanComponentCountX2] & 0x0f; + + if ((this.zigStart == 0 && this.zigEnd != 0) || this.zigStart > this.zigEnd + || this.zigEnd >= Block8x8F.ScalarCount) + { + throw new ImageFormatException("Bad spectral selection bounds"); + } + + if (this.zigStart != 0 && scanComponentCount != 1) + { + throw new ImageFormatException("Progressive AC coefficients for more than one component"); + } + + if (this.ah != 0 && this.ah != this.al + 1) + { + throw new ImageFormatException("Bad successive approximation values"); + } + } + + // mxx and myy are the number of MCUs (Minimum Coded Units) in the image. + int h0 = decoder.ComponentArray[0].HorizontalFactor; + int v0 = decoder.ComponentArray[0].VerticalFactor; + this.mxx = (decoder.ImageWidth + (8 * h0) - 1) / (8 * h0); + this.myy = (decoder.ImageHeight + (8 * v0) - 1) / (8 * v0); + + if (decoder.IsProgressive) + { + for (int i = 0; i < scanComponentCount; i++) + { + int compIndex = this.Pointers.Scan[i].Index; + if (decoder.ProgCoeffs[compIndex] == null) + { + int size = mxx * myy * decoder.ComponentArray[compIndex].HorizontalFactor + * decoder.ComponentArray[compIndex].VerticalFactor; + + decoder.ProgCoeffs[compIndex] = new Block8x8F[size]; + } + } + } + } + + private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv) + { + // Component selector. + int cs = decoder.Temp[1 + (2 * i)]; + int compIndex = -1; + for (int j = 0; j < decoder.ComponentCount; j++) + { + // Component compv = ; + if (cs == decoder.ComponentArray[j].Identifier) + { + compIndex = j; + } + } + + if (compIndex < 0) + { + throw new ImageFormatException("Unknown component selector"); + } + + currentScan.Index = (byte)compIndex; + + this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); + } + + + private void ProcessComponentImpl( + JpegDecoderCore decoder, + int i, + ref Scan currentScan, + ref int totalHv, + ref Component currentComponent) + { + // Section B.2.3 states that "the value of Cs_j shall be different from + // the values of Cs_1 through Cs_(j-1)". Since we have previously + // verified that a frame's component identifiers (C_i values in section + // B.2.2) are unique, it suffices to check that the implicit indexes + // into comp are unique. + for (int j = 0; j < i; j++) + { + if (currentScan.Index == this.Pointers.Scan[j].Index) + { + throw new ImageFormatException("Repeated component selector"); + } + } + + totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; + + currentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); + if (currentScan.DcTableSelector > HuffmanTree.MaxTh) + { + throw new ImageFormatException("Bad DC table selector value"); + } + + currentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); + if (currentScan.AcTableSelector > HuffmanTree.MaxTh) + { + throw new ImageFormatException("Bad AC table selector value"); + } + } + + private void InitProgressive(JpegDecoderCore decoder) + { + + } + } + +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs index 12acdfc6cb..bdf3468e68 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs @@ -91,7 +91,7 @@ namespace ImageSharp.Formats.Jpg /// /// Creates and initializes an array of instances of size /// - /// An array of instances representing the Huffman table + /// An array of instances representing the Huffman tables public static HuffmanTree[] CreateHuffmanTrees() { HuffmanTree[] result = new HuffmanTree[NumberOfTrees]; @@ -109,7 +109,7 @@ namespace ImageSharp.Formats.Jpg /// /// Initializes the Huffman tree /// - public void Init() + private void Init() { this.Lut = UshortBuffer.Rent(1 << LutSize); this.Values = ByteBuffer.Rent(MaxNCodes); @@ -134,7 +134,7 @@ namespace ImageSharp.Formats.Jpg /// Internal part of the DHT processor, whatever does it mean /// /// The decoder instance - /// The temporal buffer that holds the data that has been read from stream + /// The temporal buffer that holds the data that has been read from the Jpeg stream /// Remaining bits internal void ProcessDefineHuffmanTablesMarkerLoop( JpegDecoderCore decoder, diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs new file mode 100644 index 0000000000..a2f439c5a2 --- /dev/null +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs @@ -0,0 +1,26 @@ +namespace ImageSharp.Formats.Jpg +{ + using System.Runtime.InteropServices; + + /// + /// Represents a component scan + /// + [StructLayout(LayoutKind.Sequential)] + internal struct Scan + { + /// + /// Gets or sets the component index. + /// + public byte Index; + + /// + /// Gets or sets the DC table selector + /// + public byte DcTableSelector; + + /// + /// Gets or sets the AC table selector + /// + public byte AcTableSelector; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs index 1696d0811a..c17a2856e4 100644 --- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs @@ -10,7 +10,7 @@ namespace ImageSharp.Formats using System.Threading.Tasks; using ImageSharp.Formats.Jpg; - + /// /// Performs the jpeg decoding operation. /// @@ -29,7 +29,7 @@ namespace ImageSharp.Formats /// /// The maximum number of color components /// - private const int MaxComponents = 4; + internal const int MaxComponents = 4; /// /// The maximum number of quantization tables @@ -39,7 +39,7 @@ namespace ImageSharp.Formats /// /// The component array /// - private readonly Component[] componentArray; + internal Component[] ComponentArray { get; } /// /// The huffman trees @@ -49,17 +49,17 @@ namespace ImageSharp.Formats /// /// Saved state between progressive-mode scans. /// - private readonly Block8x8F[][] progCoeffs; + internal Block8x8F[][] ProgCoeffs { get; } /// /// Quantization tables, in zigzag order. /// - private readonly Block8x8F[] quantizationTables; + internal Block8x8F[] QuantizationTables { get; } /// /// A temporary buffer for holding pixels /// - private readonly byte[] temp; + internal byte[] Temp { get; } // TODO: the usage of this buffer is unclean + need to move it to the stack for performance @@ -90,7 +90,7 @@ namespace ImageSharp.Formats /// /// The number of color components within the image. /// - private int componentCount; + internal int ComponentCount { get; private set; } /// /// End-of-Band run, specified in section G.1.2.2. @@ -110,12 +110,12 @@ namespace ImageSharp.Formats /// /// The image height /// - private int imageHeight; + internal int ImageHeight { get; private set; } /// /// The image width /// - private int imageWidth; + internal int ImageWidth { get; private set; } /// /// The byte buffer. @@ -130,7 +130,7 @@ namespace ImageSharp.Formats /// /// Whether the image is interlaced (progressive) /// - private bool isProgressive; + public bool IsProgressive { get; private set; } /// /// The restart interval @@ -153,10 +153,10 @@ namespace ImageSharp.Formats public JpegDecoderCore() { this.huffmanTrees = HuffmanTree.CreateHuffmanTrees(); - this.quantizationTables = new Block8x8F[MaxTq + 1]; - this.temp = new byte[2 * Block8x8F.ScalarCount]; - this.componentArray = new Component[MaxComponents]; - this.progCoeffs = new Block8x8F[MaxComponents][]; + this.QuantizationTables = new Block8x8F[MaxTq + 1]; + this.Temp = new byte[2 * Block8x8F.ScalarCount]; + this.ComponentArray = new Component[MaxComponents]; + this.ProgCoeffs = new Block8x8F[MaxComponents][]; this.bits = default(Bits); this.bytes = Bytes.Create(); } @@ -219,8 +219,8 @@ namespace ImageSharp.Formats this.inputStream = stream; // Check for the Start Of Image marker. - this.ReadFull(this.temp, 0, 2); - if (this.temp[0] != JpegConstants.Markers.XFF || this.temp[1] != JpegConstants.Markers.SOI) + this.ReadFull(this.Temp, 0, 2); + if (this.Temp[0] != JpegConstants.Markers.XFF || this.Temp[1] != JpegConstants.Markers.SOI) { throw new ImageFormatException("Missing SOI marker."); } @@ -228,8 +228,8 @@ namespace ImageSharp.Formats // Process the remaining segments until the End Of Image marker. while (true) { - this.ReadFull(this.temp, 0, 2); - while (this.temp[0] != 0xff) + this.ReadFull(this.Temp, 0, 2); + while (this.Temp[0] != 0xff) { // Strictly speaking, this is a format error. However, libjpeg is // liberal in what it accepts. As of version 9, next_marker in @@ -248,11 +248,11 @@ namespace ImageSharp.Formats // mechanism within a scan (the RST[0-7] markers). // Note that extraneous 0xff bytes in e.g. SOS data are escaped as // "\xff\x00", and so are detected a little further down below. - this.temp[0] = this.temp[1]; - this.temp[1] = this.ReadByte(); + this.Temp[0] = this.Temp[1]; + this.Temp[1] = this.ReadByte(); } - byte marker = this.temp[1]; + byte marker = this.Temp[1]; if (marker == 0) { // Treat "\xff\x00" as extraneous data. @@ -285,8 +285,8 @@ namespace ImageSharp.Formats // Read the 16-bit length of the segment. The value includes the 2 bytes for the // length itself, so we subtract 2 to get the number of remaining bytes. - this.ReadFull(this.temp, 0, 2); - int remaining = (this.temp[0] << 8) + this.temp[1] - 2; + this.ReadFull(this.Temp, 0, 2); + int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2; if (remaining < 0) { throw new ImageFormatException("Short segment length."); @@ -297,7 +297,7 @@ namespace ImageSharp.Formats case JpegConstants.Markers.SOF0: case JpegConstants.Markers.SOF1: case JpegConstants.Markers.SOF2: - this.isProgressive = marker == JpegConstants.Markers.SOF2; + this.IsProgressive = marker == JpegConstants.Markers.SOF2; this.ProcessStartOfFrameMarker(remaining); if (configOnly && this.isJfif) { @@ -377,11 +377,11 @@ namespace ImageSharp.Formats if (this.grayImage.IsInitialized) { - this.ConvertFromGrayScale(this.imageWidth, this.imageHeight, image); + this.ConvertFromGrayScale(this.ImageWidth, this.ImageHeight, image); } else if (this.ycbcrImage != null) { - if (this.componentCount == 4) + if (this.ComponentCount == 4) { if (!this.adobeTransformValid) { @@ -394,26 +394,26 @@ namespace ImageSharp.Formats // TODO: YCbCrA? if (this.adobeTransform == JpegConstants.Adobe.ColorTransformYcck) { - this.ConvertFromYcck(this.imageWidth, this.imageHeight, image); + this.ConvertFromYcck(this.ImageWidth, this.ImageHeight, image); } else if (this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown) { // Assume CMYK - this.ConvertFromCmyk(this.imageWidth, this.imageHeight, image); + this.ConvertFromCmyk(this.ImageWidth, this.ImageHeight, image); } return; } - if (this.componentCount == 3) + if (this.ComponentCount == 3) { if (this.IsRGB()) { - this.ConvertFromRGB(this.imageWidth, this.imageHeight, image); + this.ConvertFromRGB(this.ImageWidth, this.ImageHeight, image); return; } - this.ConvertFromYCbCr(this.imageWidth, this.imageHeight, image); + this.ConvertFromYCbCr(this.ImageWidth, this.ImageHeight, image); return; } @@ -538,7 +538,7 @@ namespace ImageSharp.Formats private void ConvertFromCmyk(int width, int height, Image image) where TColor : struct, IPackedPixel, IEquatable { - int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor; + int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; image.InitPixels(width, height); @@ -613,7 +613,7 @@ namespace ImageSharp.Formats private void ConvertFromRGB(int width, int height, Image image) where TColor : struct, IPackedPixel, IEquatable { - int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor; + int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; image.InitPixels(width, height); using (PixelAccessor pixels = image.Lock()) @@ -653,7 +653,7 @@ namespace ImageSharp.Formats private void ConvertFromYCbCr(int width, int height, Image image) where TColor : struct, IPackedPixel, IEquatable { - int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor; + int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; image.InitPixels(width, height); using (PixelAccessor pixels = image.Lock()) @@ -693,7 +693,7 @@ namespace ImageSharp.Formats private void ConvertFromYcck(int width, int height, Image image) where TColor : struct, IPackedPixel, IEquatable { - int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor; + int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; image.InitPixels(width, height); @@ -836,7 +836,7 @@ namespace ImageSharp.Formats private JpegPixelArea GetDestinationChannel(int compIndex) { - if (this.componentCount == 1) + if (this.ComponentCount == 1) { return this.grayImage; } @@ -878,8 +878,8 @@ namespace ImageSharp.Formats return true; } - return this.componentArray[0].Identifier == 'R' && this.componentArray[1].Identifier == 'G' - && this.componentArray[2].Identifier == 'B'; + return this.ComponentArray[0].Identifier == 'R' && this.ComponentArray[1].Identifier == 'G' + && this.ComponentArray[2].Identifier == 'B'; } /// @@ -894,16 +894,16 @@ namespace ImageSharp.Formats return; } - if (this.componentCount == 1) + if (this.ComponentCount == 1) { this.grayImage = JpegPixelArea.CreatePooled(8 * mxx, 8 * myy); } else { - int h0 = this.componentArray[0].HorizontalFactor; - int v0 = this.componentArray[0].VerticalFactor; - int horizontalRatio = h0 / this.componentArray[1].HorizontalFactor; - int verticalRatio = v0 / this.componentArray[1].VerticalFactor; + int h0 = this.ComponentArray[0].HorizontalFactor; + int v0 = this.ComponentArray[0].VerticalFactor; + int horizontalRatio = h0 / this.ComponentArray[1].HorizontalFactor; + int verticalRatio = v0 / this.ComponentArray[1].VerticalFactor; YCbCrImage.YCbCrSubsampleRatio ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444; switch ((horizontalRatio << 4) | verticalRatio) @@ -930,10 +930,10 @@ namespace ImageSharp.Formats this.ycbcrImage = new YCbCrImage(8 * h0 * mxx, 8 * v0 * myy, ratio); - if (this.componentCount == 4) + if (this.ComponentCount == 4) { - int h3 = this.componentArray[3].HorizontalFactor; - int v3 = this.componentArray[3].VerticalFactor; + int h3 = this.ComponentArray[3].HorizontalFactor; + int v3 = this.ComponentArray[3].VerticalFactor; this.blackImage = JpegPixelArea.CreatePooled(8 * h3 * mxx, 8 * v3 * myy); } @@ -1017,14 +1017,14 @@ namespace ImageSharp.Formats return; } - this.ReadFull(this.temp, 0, 12); + this.ReadFull(this.Temp, 0, 12); remaining -= 12; - if (this.temp[0] == 'A' && this.temp[1] == 'd' && this.temp[2] == 'o' && this.temp[3] == 'b' - && this.temp[4] == 'e') + if (this.Temp[0] == 'A' && this.Temp[1] == 'd' && this.Temp[2] == 'o' && this.Temp[3] == 'b' + && this.Temp[4] == 'e') { this.adobeTransformValid = true; - this.adobeTransform = this.temp[11]; + this.adobeTransform = this.Temp[11]; } if (remaining > 0) @@ -1070,17 +1070,17 @@ namespace ImageSharp.Formats return; } - this.ReadFull(this.temp, 0, 13); + this.ReadFull(this.Temp, 0, 13); remaining -= 13; // TODO: We should be using constants for this. - this.isJfif = this.temp[0] == 'J' && this.temp[1] == 'F' && this.temp[2] == 'I' && this.temp[3] == 'F' - && this.temp[4] == '\x00'; + this.isJfif = this.Temp[0] == 'J' && this.Temp[1] == 'F' && this.Temp[2] == 'I' && this.Temp[3] == 'F' + && this.Temp[4] == '\x00'; if (this.isJfif) { - this.horizontalResolution = (short)(this.temp[9] + (this.temp[10] << 8)); - this.verticalResolution = (short)(this.temp[11] + (this.temp[12] << 8)); + this.horizontalResolution = (short)(this.Temp[9] + (this.Temp[10] << 8)); + this.verticalResolution = (short)(this.Temp[11] + (this.Temp[12] << 8)); } if (remaining > 0) @@ -1182,7 +1182,7 @@ namespace ImageSharp.Formats } } - if (this.isProgressive) + if (this.IsProgressive) { if (zigEnd != Block8x8F.ScalarCount - 1 || al != 0) { @@ -1190,8 +1190,8 @@ namespace ImageSharp.Formats // TODO!!! // throw new NotImplementedException(); - // this.progCoeffs[compIndex][((@by * mxx) * hi) + bx] = b.Clone(); - this.progCoeffs[compIndex][((@by * mxx) * hi) + bx] = *b; + // this.ProgCoeffs[compIndex][((@by * mxx) * hi) + bx] = b.Clone(); + this.ProgCoeffs[compIndex][((@by * mxx) * hi) + bx] = *b; // At this point, we could execute the rest of the loop body to dequantize and // perform the inverse DCT, to save early stages of a progressive image to the @@ -1234,13 +1234,13 @@ namespace ImageSharp.Formats totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; - currentScan.DcTableSelector = (byte)(this.temp[2 + (2 * i)] >> 4); + currentScan.DcTableSelector = (byte)(this.Temp[2 + (2 * i)] >> 4); if (currentScan.DcTableSelector > HuffmanTree.MaxTh) { throw new ImageFormatException("Bad DC table selector value"); } - currentScan.AcTableSelector = (byte)(this.temp[2 + (2 * i)] & 0x0f); + currentScan.AcTableSelector = (byte)(this.Temp[2 + (2 * i)] & 0x0f); if (currentScan.AcTableSelector > HuffmanTree.MaxTh) { throw new ImageFormatException("Bad AC table selector value"); @@ -1261,22 +1261,22 @@ namespace ImageSharp.Formats throw new ImageFormatException("DHT has wrong length"); } - this.ReadFull(this.temp, 0, 17); + this.ReadFull(this.Temp, 0, 17); - int tc = this.temp[0] >> 4; + int tc = this.Temp[0] >> 4; if (tc > HuffmanTree.MaxTc) { throw new ImageFormatException("Bad Tc value"); } - int th = this.temp[0] & 0x0f; - if (th > HuffmanTree.MaxTh || (!this.isProgressive && (th > 1))) + int th = this.Temp[0] & 0x0f; + if (th > HuffmanTree.MaxTh || (!this.IsProgressive && (th > 1))) { throw new ImageFormatException("Bad Th value"); } int huffTreeIndex = (tc * HuffmanTree.ThRowSize) + th; - this.huffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.temp, ref remaining); + this.huffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.Temp, ref remaining); } } @@ -1292,8 +1292,8 @@ namespace ImageSharp.Formats throw new ImageFormatException("DRI has wrong length"); } - this.ReadFull(this.temp, 0, 2); - this.restartInterval = ((int)this.temp[0] << 8) + (int)this.temp[1]; + this.ReadFull(this.Temp, 0, 2); + this.restartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1]; } /// @@ -1327,11 +1327,11 @@ namespace ImageSharp.Formats } remaining -= Block8x8F.ScalarCount; - this.ReadFull(this.temp, 0, Block8x8F.ScalarCount); + this.ReadFull(this.Temp, 0, Block8x8F.ScalarCount); for (int i = 0; i < Block8x8F.ScalarCount; i++) { - this.quantizationTables[tq][i] = this.temp[i]; + this.QuantizationTables[tq][i] = this.Temp[i]; } break; @@ -1343,11 +1343,11 @@ namespace ImageSharp.Formats } remaining -= 2 * Block8x8F.ScalarCount; - this.ReadFull(this.temp, 0, 2 * Block8x8F.ScalarCount); + this.ReadFull(this.Temp, 0, 2 * Block8x8F.ScalarCount); for (int i = 0; i < Block8x8F.ScalarCount; i++) { - this.quantizationTables[tq][i] = (this.temp[2 * i] << 8) | this.temp[(2 * i) + 1]; + this.QuantizationTables[tq][i] = (this.Temp[2 * i] << 8) | this.Temp[(2 * i) + 1]; } break; @@ -1370,12 +1370,12 @@ namespace ImageSharp.Formats private void ProcessScanImpl(int i, ref Scan currentScan, Scan[] scan, ref int totalHv) { // Component selector. - int cs = this.temp[1 + (2 * i)]; + int cs = this.Temp[1 + (2 * i)]; int compIndex = -1; - for (int j = 0; j < this.componentCount; j++) + for (int j = 0; j < this.ComponentCount; j++) { // Component compv = ; - if (cs == this.componentArray[j].Identifier) + if (cs == this.ComponentArray[j].Identifier) { compIndex = j; } @@ -1388,7 +1388,7 @@ namespace ImageSharp.Formats currentScan.Index = (byte)compIndex; - this.ProcessComponentImpl(i, ref currentScan, scan, ref totalHv, ref this.componentArray[compIndex]); + this.ProcessComponentImpl(i, ref currentScan, scan, ref totalHv, ref this.ComponentArray[compIndex]); } /// @@ -1397,7 +1397,7 @@ namespace ImageSharp.Formats /// The remaining bytes in the segment block. private void ProcessStartOfFrameMarker(int remaining) { - if (this.componentCount != 0) + if (this.ComponentCount != 0) { throw new ImageFormatException("Multiple SOF markers"); } @@ -1405,54 +1405,54 @@ namespace ImageSharp.Formats switch (remaining) { case 6 + (3 * 1): // Grayscale image. - this.componentCount = 1; + this.ComponentCount = 1; break; case 6 + (3 * 3): // YCbCr or RGB image. - this.componentCount = 3; + this.ComponentCount = 3; break; case 6 + (3 * 4): // YCbCrK or CMYK image. - this.componentCount = 4; + this.ComponentCount = 4; break; default: throw new ImageFormatException("Incorrect number of components"); } - this.ReadFull(this.temp, 0, remaining); + this.ReadFull(this.Temp, 0, remaining); // We only support 8-bit precision. - if (this.temp[0] != 8) + if (this.Temp[0] != 8) { throw new ImageFormatException("Only 8-Bit precision supported."); } - this.imageHeight = (this.temp[1] << 8) + this.temp[2]; - this.imageWidth = (this.temp[3] << 8) + this.temp[4]; - if (this.temp[5] != this.componentCount) + this.ImageHeight = (this.Temp[1] << 8) + this.Temp[2]; + this.ImageWidth = (this.Temp[3] << 8) + this.Temp[4]; + if (this.Temp[5] != this.ComponentCount) { throw new ImageFormatException("SOF has wrong length"); } - for (int i = 0; i < this.componentCount; i++) + for (int i = 0; i < this.ComponentCount; i++) { - this.componentArray[i].Identifier = this.temp[6 + (3 * i)]; + this.ComponentArray[i].Identifier = this.Temp[6 + (3 * i)]; // Section B.2.2 states that "the value of C_i shall be different from // the values of C_1 through C_(i-1)". for (int j = 0; j < i; j++) { - if (this.componentArray[i].Identifier == this.componentArray[j].Identifier) + if (this.ComponentArray[i].Identifier == this.ComponentArray[j].Identifier) { throw new ImageFormatException("Repeated component identifier"); } } - this.componentArray[i].Selector = this.temp[8 + (3 * i)]; - if (this.componentArray[i].Selector > MaxTq) + this.ComponentArray[i].Selector = this.Temp[8 + (3 * i)]; + if (this.ComponentArray[i].Selector > MaxTq) { throw new ImageFormatException("Bad Tq value"); } - byte hv = this.temp[7 + (3 * i)]; + byte hv = this.Temp[7 + (3 * i)]; int h = hv >> 4; int v = hv & 0x0f; if (h < 1 || h > 4 || v < 1 || v > 4) @@ -1465,7 +1465,7 @@ namespace ImageSharp.Formats throw new ImageFormatException("Lnsupported subsampling ratio"); } - switch (this.componentCount) + switch (this.ComponentCount) { case 1: @@ -1512,8 +1512,8 @@ namespace ImageSharp.Formats case 1: { // Cb. - if (this.componentArray[0].HorizontalFactor % h != 0 - || this.componentArray[0].VerticalFactor % v != 0) + if (this.ComponentArray[0].HorizontalFactor % h != 0 + || this.ComponentArray[0].VerticalFactor % v != 0) { throw new ImageFormatException("Unsupported subsampling ratio"); } @@ -1524,8 +1524,8 @@ namespace ImageSharp.Formats case 2: { // Cr. - if (this.componentArray[1].HorizontalFactor != h - || this.componentArray[1].VerticalFactor != v) + if (this.ComponentArray[1].HorizontalFactor != h + || this.ComponentArray[1].VerticalFactor != v) { throw new ImageFormatException("Unsupported subsampling ratio"); } @@ -1565,8 +1565,8 @@ namespace ImageSharp.Formats break; case 3: - if (this.componentArray[0].HorizontalFactor != h - || this.componentArray[0].VerticalFactor != v) + if (this.ComponentArray[0].HorizontalFactor != h + || this.ComponentArray[0].VerticalFactor != v) { throw new ImageFormatException("Unsupported subsampling ratio"); } @@ -1577,11 +1577,18 @@ namespace ImageSharp.Formats break; } - this.componentArray[i].HorizontalFactor = h; - this.componentArray[i].VerticalFactor = v; + this.ComponentArray[i].HorizontalFactor = h; + this.ComponentArray[i].VerticalFactor = v; } } + private void ProcessStartOfScan22(int remaining) + { + DecoderScanProcessor processor = default(DecoderScanProcessor); + DecoderScanProcessor.Init(&processor, this, remaining); + this.MakeImage(processor.mxx, processor.myy); + } + /// /// Processes the SOS (Start of scan marker). /// @@ -1595,18 +1602,18 @@ namespace ImageSharp.Formats /// private void ProcessStartOfScan(int remaining) { - if (this.componentCount == 0) + if (this.ComponentCount == 0) { throw new ImageFormatException("Missing SOF marker"); } - if (remaining < 6 || 4 + (2 * this.componentCount) < remaining || remaining % 2 != 0) + if (remaining < 6 || 4 + (2 * this.ComponentCount) < remaining || remaining % 2 != 0) { throw new ImageFormatException("SOS has wrong length"); } - this.ReadFull(this.temp, 0, remaining); - byte scanComponentCount = this.temp[0]; + this.ReadFull(this.Temp, 0, remaining); + byte scanComponentCount = this.Temp[0]; int scanComponentCountX2 = 2 * scanComponentCount; if (remaining != 4 + scanComponentCountX2) @@ -1624,7 +1631,7 @@ namespace ImageSharp.Formats // Section B.2.3 states that if there is more than one component then the // total H*V values in a scan must be <= 10. - if (this.componentCount > 1 && totalHv > 10) + if (this.ComponentCount > 1 && totalHv > 10) { throw new ImageFormatException("Total sampling factors too large."); } @@ -1648,12 +1655,12 @@ namespace ImageSharp.Formats int ah = 0; int al = 0; - if (this.isProgressive) + if (this.IsProgressive) { - zigStart = this.temp[1 + scanComponentCountX2]; - zigEnd = this.temp[2 + scanComponentCountX2]; - ah = this.temp[3 + scanComponentCountX2] >> 4; - al = this.temp[3 + scanComponentCountX2] & 0x0f; + zigStart = this.Temp[1 + scanComponentCountX2]; + zigEnd = this.Temp[2 + scanComponentCountX2]; + ah = this.Temp[3 + scanComponentCountX2] >> 4; + al = this.Temp[3 + scanComponentCountX2] & 0x0f; if ((zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || zigEnd >= Block8x8F.ScalarCount) { @@ -1672,24 +1679,22 @@ namespace ImageSharp.Formats } // mxx and myy are the number of MCUs (Minimum Coded Units) in the image. - int h0 = this.componentArray[0].HorizontalFactor; - int v0 = this.componentArray[0].VerticalFactor; - int mxx = (this.imageWidth + (8 * h0) - 1) / (8 * h0); - int myy = (this.imageHeight + (8 * v0) - 1) / (8 * v0); - - this.MakeImage(mxx, myy); + int h0 = this.ComponentArray[0].HorizontalFactor; + int v0 = this.ComponentArray[0].VerticalFactor; + int mxx = (this.ImageWidth + (8 * h0) - 1) / (8 * h0); + int myy = (this.ImageHeight + (8 * v0) - 1) / (8 * v0); - if (this.isProgressive) + if (this.IsProgressive) { for (int i = 0; i < scanComponentCount; i++) { int compIndex = scan[i].Index; - if (this.progCoeffs[compIndex] == null) + if (this.ProgCoeffs[compIndex] == null) { - int size = mxx * myy * this.componentArray[compIndex].HorizontalFactor - * this.componentArray[compIndex].VerticalFactor; + int size = mxx * myy * this.ComponentArray[compIndex].HorizontalFactor + * this.ComponentArray[compIndex].VerticalFactor; - this.progCoeffs[compIndex] = new Block8x8F[size]; + this.ProgCoeffs[compIndex] = new Block8x8F[size]; } } } @@ -1716,6 +1721,10 @@ namespace ImageSharp.Formats int* unzigPtr = unzig.Data; + + this.MakeImage(mxx, myy); + + for (int my = 0; my < myy; my++) { for (int mx = 0; mx < mxx; mx++) @@ -1723,8 +1732,8 @@ namespace ImageSharp.Formats for (int i = 0; i < scanComponentCount; i++) { int compIndex = scan[i].Index; - int hi = this.componentArray[compIndex].HorizontalFactor; - int vi = this.componentArray[compIndex].VerticalFactor; + int hi = this.ComponentArray[compIndex].HorizontalFactor; + int vi = this.ComponentArray[compIndex].VerticalFactor; for (int j = 0; j < hi * vi; j++) { @@ -1761,24 +1770,24 @@ namespace ImageSharp.Formats bx = blockCount % q; by = blockCount / q; blockCount++; - if (bx * 8 >= this.imageWidth || by * 8 >= this.imageHeight) + if (bx * 8 >= this.ImageWidth || by * 8 >= this.ImageHeight) { continue; } } - int qtIndex = this.componentArray[compIndex].Selector; + int qtIndex = this.ComponentArray[compIndex].Selector; // TODO: A DecoderScanProcessor struct could clean up this mess // TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async. - fixed (Block8x8F* qtp = &this.quantizationTables[qtIndex]) + fixed (Block8x8F* qtp = &this.QuantizationTables[qtIndex]) { // Load the previous partially decoded coefficients, if applicable. - if (this.isProgressive) + if (this.IsProgressive) { this.blockIndex = ((@by * mxx) * hi) + bx; - fixed (Block8x8F* bp = &this.progCoeffs[compIndex][this.blockIndex]) + fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][this.blockIndex]) { this.ProcessBlockImpl( ah, @@ -1835,8 +1844,8 @@ namespace ImageSharp.Formats { // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, // but this one assumes well-formed input, and hence the restart marker follows immediately. - this.ReadFull(this.temp, 0, 2); - if (this.temp[0] != 0xff || this.temp[1] != expectedRst) + this.ReadFull(this.Temp, 0, 2); + if (this.Temp[0] != 0xff || this.Temp[1] != expectedRst) { throw new ImageFormatException("Bad RST marker"); } @@ -2072,27 +2081,6 @@ namespace ImageSharp.Formats } } - /// - /// Represents a component scan - /// - private struct Scan - { - /// - /// Gets or sets the component index. - /// - public byte Index { get; set; } - - /// - /// Gets or sets the DC table selector - /// - public byte DcTableSelector { get; set; } - - /// - /// Gets or sets the AC table selector - /// - public byte AcTableSelector { get; set; } - } - /// /// The EOF (End of File exception). /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker