From 3a728e35898d3a9bc85e2740a5cb5adb561255c2 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 29 Dec 2016 01:32:27 +0100 Subject: [PATCH] ok StyleCop, this time I won! --- .../Formats/Jpg/Components/Block8x8F.cs | 7 - .../Jpg/Components/Decoder/HuffmanTree.cs | 26 +- .../Jpg/Components/Decoder/JpegScanDecoder.cs | 341 +++++++++--------- .../Formats/Jpg/Components/Decoder/Scan.cs | 7 +- src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs | 339 ++++++++--------- src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs | 6 +- .../Formats/Jpg/Block8x8FTests.cs | 16 - 7 files changed, 368 insertions(+), 374 deletions(-) diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs index 63d8b0e542..723ccd6b80 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs @@ -373,12 +373,5 @@ namespace ImageSharp.Formats.Jpg } } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(Block8x8F* dest, Block8x8F* source) - { - *dest = *source; - //Unsafe.CopyBlock(dest, source, (uint)sizeof(Block8x8F)); - } } } \ 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 44fce5cdc1..e06d644a7f 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs @@ -106,18 +106,6 @@ namespace ImageSharp.Formats.Jpg return result; } - /// - /// Initializes the Huffman tree - /// - private void Init() - { - this.Lut = UshortBuffer.Rent(1 << LutSize); - this.Values = ByteBuffer.Rent(MaxNCodes); - this.MinCodes = IntBuffer.Rent(MaxCodeLength); - this.MaxCodes = IntBuffer.Rent(MaxCodeLength); - this.Indices = IntBuffer.Rent(MaxCodeLength); - } - /// /// Disposes the underlying buffers /// @@ -136,7 +124,7 @@ namespace ImageSharp.Formats.Jpg /// The decoder instance /// The temporal buffer that holds the data that has been read from the Jpeg stream /// Remaining bits - internal void ProcessDefineHuffmanTablesMarkerLoop( + public void ProcessDefineHuffmanTablesMarkerLoop( JpegDecoderCore decoder, byte[] defineHuffmanTablesData, ref int remaining) @@ -226,5 +214,17 @@ namespace ImageSharp.Formats.Jpg c <<= 1; } } + + /// + /// Initializes the Huffman tree + /// + private void Init() + { + this.Lut = UshortBuffer.Rent(1 << LutSize); + this.Values = ByteBuffer.Rent(MaxNCodes); + this.MinCodes = IntBuffer.Rent(MaxCodeLength); + this.MaxCodes = IntBuffer.Rent(MaxCodeLength); + this.Indices = IntBuffer.Rent(MaxCodeLength); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs index d12f00ada4..39ee6687b0 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs @@ -1,4 +1,9 @@ -// ReSharper disable InconsistentNaming +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +// ReSharper disable InconsistentNaming namespace ImageSharp.Formats.Jpg { using System; @@ -12,128 +17,24 @@ namespace ImageSharp.Formats.Jpg internal unsafe struct JpegScanDecoder { /// - /// The AC table index + /// Number of MCU-s (Minimum Coded Units) in the image along the X axis /// - internal const int AcTableIndex = 1; + public int XNumberOfMCUs; /// - /// The DC table index + /// Number of MCU-s (Minimum Coded Units) in the image along the Y axis /// - internal const int DcTableIndex = 0; + public int YNumberOfMCUs; /// - /// Holds the "large" data blocks needed for computations + /// The AC table index /// - [StructLayout(LayoutKind.Sequential)] - public struct ComputationData - { - /// - /// The main input block - /// - public Block8x8F Block; - - /// - /// Temporal block 1 to store intermediate and/or final computation results - /// - public Block8x8F Temp1; - - /// - /// Temporal block 2 to store intermediate and/or final computation results - /// - public Block8x8F Temp2; - - /// - /// The quantization table as - /// - public Block8x8F QuantiazationTable; - - /// - /// The jpeg unzig data - /// - public UnzigData Unzig; - - /// - /// The no-idea-what's this data - /// - public fixed byte ScanData[3 * JpegDecoderCore.MaxComponents]; - - /// - /// The DC component values - /// - public fixed int Dc[JpegDecoderCore.MaxComponents]; - - /// - /// Creates and initializes a new instance - /// - /// - public static ComputationData Create() - { - ComputationData data = default(ComputationData); - data.Unzig = UnzigData.Create(); - return data; - } - } + private const int AcTableIndex = 1; /// - /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of + /// The DC table index /// - public struct DataPointers - { - /// - /// Pointer to - /// - public Block8x8F* Block; - - /// - /// Pointer to - /// - public Block8x8F* Temp1; - - /// - /// Pointer to - /// - public Block8x8F* Temp2; - - /// - /// Pointer to - /// - public Block8x8F* QuantiazationTable; - - /// - /// Pointer to as int* - /// - public int* Unzig; - - /// - /// Pointer to as Scan* - /// - public Scan* Scan; - - /// - /// Pointer to - /// - public int* Dc; - - public DataPointers(ComputationData* basePtr) - { - this.Block = &basePtr->Block; - this.Temp1 = &basePtr->Temp1; - this.Temp2 = &basePtr->Temp2; - this.QuantiazationTable = &basePtr->QuantiazationTable; - this.Unzig = basePtr->Unzig.Data; - this.Scan = (Scan*)basePtr->ScanData; - this.Dc = basePtr->Dc; - } - } - - private void ResetDc() - { - Unsafe.InitBlock(this.Pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents); - } - - - // bx and by are the - // blocks: the third block in the first row has (bx, by) = (2, 0). + private const int DcTableIndex = 0; /// /// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) @@ -180,32 +81,25 @@ namespace ImageSharp.Formats.Jpg /// private int al; - // XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image. - /// - /// Number of MCU-s (Minimum Coded Units) on X axis - /// - public int XNumberOfMCUs; - - /// - /// Number of MCU-s (Minimum Coded Units) in Y axis + /// The number of component scans /// - public int YNumberOfMCUs; + private int componentScanCount; /// - /// The number of component scans + /// End-of-Band run, specified in section G.1.2.2. /// - private int componentScanCount; + private ushort eobRun; /// /// The buffer /// - private ComputationData Data; + private ComputationData data; /// - /// Pointers to elements of + /// Pointers to elements of /// - private DataPointers Pointers; + private DataPointers pointers; /// /// Initializes the default instance after creation. @@ -215,8 +109,8 @@ namespace ImageSharp.Formats.Jpg /// The remaining bytes in the segment block. public static void Init(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining) { - p->Data = ComputationData.Create(); - p->Pointers = new DataPointers(&p->Data); + p->data = ComputationData.Create(); + p->pointers = new DataPointers(&p->data); p->InitImpl(decoder, remaining); } @@ -236,7 +130,7 @@ namespace ImageSharp.Formats.Jpg { for (int i = 0; i < this.componentScanCount; i++) { - int compIndex = this.Pointers.Scan[i].Index; + int compIndex = this.pointers.Scan[i].Index; int hi = decoder.ComponentArray[compIndex].HorizontalFactor; int vi = decoder.ComponentArray[compIndex].VerticalFactor; @@ -284,18 +178,17 @@ namespace ImageSharp.Formats.Jpg int qtIndex = decoder.ComponentArray[compIndex].Selector; // TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async. + this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; - this.Data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; - - //Load the previous partially decoded coefficients, if applicable. + // Load the previous partially decoded coefficients, if applicable. if (decoder.IsProgressive) { int blockIndex = ((this.by * this.XNumberOfMCUs) * hi) + this.bx; - this.Data.Block = decoder.ProgCoeffs[compIndex][blockIndex]; + this.data.Block = decoder.ProgCoeffs[compIndex][blockIndex]; } else { - this.Data.Block.Clear(); + this.data.Block.Clear(); } this.ProcessBlockImpl(decoder, i, compIndex, hi); @@ -330,14 +223,19 @@ namespace ImageSharp.Formats.Jpg this.ResetDc(); // Reset the progressive decoder state, as per section G.1.2.2. - decoder.EobRun = 0; + this.eobRun = 0; } } // for mx } } - + + private void ResetDc() + { + Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents); + } + /// /// The implementation part of as an instance method. /// @@ -368,7 +266,7 @@ namespace ImageSharp.Formats.Jpg for (int i = 0; i < this.componentScanCount; i++) { - this.ProcessScanImpl(decoder, i, ref this.Pointers.Scan[i], ref totalHv); + 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 @@ -414,7 +312,7 @@ namespace ImageSharp.Formats.Jpg { for (int i = 0; i < this.componentScanCount; i++) { - int compIndex = this.Pointers.Scan[i].Index; + int compIndex = this.pointers.Scan[i].Index; if (decoder.ProgCoeffs[compIndex] == null) { int size = this.XNumberOfMCUs * this.YNumberOfMCUs * decoder.ComponentArray[compIndex].HorizontalFactor @@ -435,9 +333,9 @@ namespace ImageSharp.Formats.Jpg /// Horizontal sampling factor at the given component index private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi) { - var b = this.Pointers.Block; - //var dc = this.Pointers.Dc; - int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].AcTableSelector; + var b = this.pointers.Block; + + int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.Scan[i].AcTableSelector; if (this.ah != 0) { this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); @@ -452,22 +350,22 @@ namespace ImageSharp.Formats.Jpg // Decode the DC coefficient, as specified in section F.2.2.1. byte value = decoder.DecodeHuffman( - ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].DcTableSelector]); + ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.Scan[i].DcTableSelector]); if (value > 16) { throw new ImageFormatException("Excessive DC component"); } int deltaDC = decoder.Bits.ReceiveExtend(value, decoder); - this.Pointers.Dc[compIndex] += deltaDC; + this.pointers.Dc[compIndex] += deltaDC; // b[0] = dc[compIndex] << al; - Block8x8F.SetScalarAt(b, 0, this.Pointers.Dc[compIndex] << al); + Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[compIndex] << this.al); } - if (zig <= this.zigEnd && decoder.EobRun > 0) + if (zig <= this.zigEnd && this.eobRun > 0) { - decoder.EobRun--; + this.eobRun--; } else { @@ -488,19 +386,19 @@ namespace ImageSharp.Formats.Jpg int ac = decoder.Bits.ReceiveExtend(val1, decoder); // b[Unzig[zig]] = ac << al; - Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], ac << this.al); + Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al); } else { if (val0 != 0x0f) { - decoder.EobRun = (ushort)(1 << val0); + this.eobRun = (ushort)(1 << val0); if (val0 != 0) { - decoder.EobRun |= (ushort)decoder.DecodeBits(val0); + this.eobRun |= (ushort)decoder.DecodeBits(val0); } - decoder.EobRun--; + this.eobRun--; break; } @@ -514,7 +412,7 @@ namespace ImageSharp.Formats.Jpg { if (this.zigEnd != Block8x8F.ScalarCount - 1 || this.al != 0) { - // We haven't completely decoded this 8x8 block. Save the coefficients. + // We haven't completely decoded this 8x8 block. Save the coefficients. // this.ProgCoeffs[compIndex][((@by * XNumberOfMCUs) * hi) + bx] = b.Clone(); decoder.ProgCoeffs[compIndex][((this.by * this.XNumberOfMCUs) * hi) + this.bx] = *b; @@ -528,13 +426,13 @@ namespace ImageSharp.Formats.Jpg } // Dequantize, perform the inverse DCT and store the block to the image. - Block8x8F.UnZig(b, this.Pointers.QuantiazationTable, this.Pointers.Unzig); + Block8x8F.UnZig(b, this.pointers.QuantiazationTable, this.pointers.Unzig); - DCT.TransformIDCT(ref *b, ref *this.Pointers.Temp1, ref *this.Pointers.Temp2); + DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2); var destChannel = decoder.GetDestinationChannel(compIndex); var destArea = destChannel.GetOffsetedSubAreaForBlock(this.bx, this.by); - destArea.LoadColorsFrom(this.Pointers.Temp1, this.Pointers.Temp2); + destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); } private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv) @@ -561,7 +459,6 @@ namespace ImageSharp.Formats.Jpg this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); } - private void ProcessComponentImpl( JpegDecoderCore decoder, int i, @@ -576,7 +473,7 @@ namespace ImageSharp.Formats.Jpg // into comp are unique. for (int j = 0; j < i; j++) { - if (currentScan.Index == this.Pointers.Scan[j].Index) + if (currentScan.Index == this.pointers.Scan[j].Index) { throw new ImageFormatException("Repeated component selector"); } @@ -600,12 +497,13 @@ namespace ImageSharp.Formats.Jpg /// /// Decodes a successive approximation refinement block, as specified in section G.1.2. /// + /// The decoder instance /// The Huffman tree /// The low transform offset - /// The decoder instance private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta) { - Block8x8F* b = this.Pointers.Block; + Block8x8F* b = this.pointers.Block; + // Refining a DC component is trivial. if (this.zigStart == 0) { @@ -631,7 +529,7 @@ namespace ImageSharp.Formats.Jpg // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3. int zig = this.zigStart; - if (decoder.EobRun == 0) + if (this.eobRun == 0) { for (; zig <= this.zigEnd; zig++) { @@ -646,10 +544,10 @@ namespace ImageSharp.Formats.Jpg case 0: if (val0 != 0x0f) { - decoder.EobRun = (ushort)(1 << val0); + this.eobRun = (ushort)(1 << val0); if (val0 != 0) { - decoder.EobRun |= (ushort)decoder.DecodeBits(val0); + this.eobRun |= (ushort)decoder.DecodeBits(val0); } done = true; @@ -683,14 +581,14 @@ namespace ImageSharp.Formats.Jpg if (z != 0) { // b[Unzig[zig]] = z; - Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], z); + Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], z); } } } - if (decoder.EobRun > 0) + if (this.eobRun > 0) { - decoder.EobRun--; + this.eobRun--; this.RefineNonZeroes(decoder, zig, -1, delta); } } @@ -706,10 +604,10 @@ namespace ImageSharp.Formats.Jpg /// The private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta) { - var b = this.Pointers.Block; + var b = this.pointers.Block; for (; zig <= this.zigEnd; zig++) { - int u = this.Pointers.Unzig[zig]; + int u = this.pointers.Unzig[zig]; float bu = Block8x8F.GetScalarAt(b, u); // TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary? @@ -744,5 +642,114 @@ namespace ImageSharp.Formats.Jpg return zig; } + + /// + /// Holds the "large" data blocks needed for computations + /// + [StructLayout(LayoutKind.Sequential)] + public struct ComputationData + { + /// + /// The main input block + /// + public Block8x8F Block; + + /// + /// Temporal block 1 to store intermediate and/or final computation results + /// + public Block8x8F Temp1; + + /// + /// Temporal block 2 to store intermediate and/or final computation results + /// + public Block8x8F Temp2; + + /// + /// The quantization table as + /// + public Block8x8F QuantiazationTable; + + /// + /// The jpeg unzig data + /// + public UnzigData Unzig; + + /// + /// The no-idea-what's this data + /// + public fixed byte ScanData[3 * JpegDecoderCore.MaxComponents]; + + /// + /// The DC component values + /// + public fixed int Dc[JpegDecoderCore.MaxComponents]; + + /// + /// Creates and initializes a new instance + /// + /// The + public static ComputationData Create() + { + ComputationData data = default(ComputationData); + data.Unzig = UnzigData.Create(); + return data; + } + } + + /// + /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of + /// + public struct DataPointers + { + /// + /// Pointer to + /// + public Block8x8F* Block; + + /// + /// Pointer to + /// + public Block8x8F* Temp1; + + /// + /// Pointer to + /// + public Block8x8F* Temp2; + + /// + /// Pointer to + /// + public Block8x8F* QuantiazationTable; + + /// + /// Pointer to as int* + /// + public int* Unzig; + + /// + /// Pointer to as Scan* + /// + public Scan* Scan; + + /// + /// Pointer to + /// + public int* Dc; + + /// + /// Initializes a new instance of the struct. + /// + /// The pointer pointing to + public DataPointers(ComputationData* basePtr) + { + this.Block = &basePtr->Block; + this.Temp1 = &basePtr->Temp1; + this.Temp2 = &basePtr->Temp2; + this.QuantiazationTable = &basePtr->QuantiazationTable; + this.Unzig = basePtr->Unzig.Data; + this.Scan = (Scan*)basePtr->ScanData; + this.Dc = basePtr->Dc; + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs index a140d989ce..799c3cc319 100644 --- a/src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs +++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs @@ -1,4 +1,9 @@ -namespace ImageSharp.Formats.Jpg +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Jpg { using System.Runtime.InteropServices; diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs index 541515cb41..a792a19912 100644 --- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs @@ -21,30 +21,33 @@ namespace ImageSharp.Formats /// public const int MaxComponents = 4; + // Complex value type field + mutable + available to other classes == the field MUST NOT be private :P +#pragma warning disable SA1401 // FieldsMustBePrivate /// - /// The App14 marker color-space + /// Holds the unprocessed bits that have been taken from the byte-stream. /// - private byte adobeTransform; + public Bits Bits; /// - /// Whether the image is in CMYK format with an App14 marker + /// The byte buffer. /// - private bool adobeTransformValid; + public Bytes Bytes; +#pragma warning restore SA401 /// - /// Holds the unprocessed bits that have been taken from the byte-stream. + /// The maximum number of quantization tables /// - public Bits Bits; + private const int MaxTq = 3; /// - /// The byte buffer. + /// The App14 marker color-space /// - public Bytes Bytes; - + private byte adobeTransform; + /// - /// End-of-Band run, specified in section G.1.2.2. + /// Whether the image is in CMYK format with an App14 marker /// - public ushort EobRun; + private bool adobeTransformValid; /// /// The black image to decode to. @@ -60,17 +63,12 @@ namespace ImageSharp.Formats /// The horizontal resolution. Calculated if the image has a JFIF header. /// private short horizontalResolution; - - /// - /// The maximum number of quantization tables - /// - private const int MaxTq = 3; - + /// /// Whether the image has a JFIF header /// private bool isJfif; - + /// /// The vertical resolution. Calculated if the image has a JFIF header. /// @@ -112,43 +110,44 @@ namespace ImageSharp.Formats MissingFF00 } - /// - /// The component array + /// Gets the component array /// public Component[] ComponentArray { get; } /// - /// The huffman trees + /// Gets the huffman trees /// public HuffmanTree[] HuffmanTrees { get; } /// - /// Saved state between progressive-mode scans. + /// Gets the saved state between progressive-mode scans. /// public Block8x8F[][] ProgCoeffs { get; } /// - /// Quantization tables, in zigzag order. + /// Gets the quantization tables, in zigzag order. /// public Block8x8F[] QuantizationTables { get; } /// - /// A temporary buffer for holding pixels + /// Gets the temporary buffer for holding pixel (and other?) data /// // TODO: the usage rules of this buffer seem to be unclean + need to consider stack-allocating it for perf public byte[] Temp { get; } + /// - /// The number of color components within the image. + /// Gets the number of color components within the image. /// public int ComponentCount { get; private set; } + /// - /// The image height + /// Gets the image height /// public int ImageHeight { get; private set; } /// - /// The image width + /// Gets the image width /// public int ImageWidth { get; private set; } @@ -156,16 +155,17 @@ namespace ImageSharp.Formats /// Gets the input stream. /// public Stream InputStream { get; private set; } + /// - /// Whether the image is interlaced (progressive) + /// Gets a value indicating whether the image is interlaced (progressive) /// public bool IsProgressive { get; private set; } /// - /// The restart interval + /// Gets the restart interval /// public int RestartInterval { get; private set; } - + /// /// Decodes the image from the specified this._stream and sets /// the data to image. @@ -407,18 +407,39 @@ namespace ImageSharp.Formats /// /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal byte ReadByte() + public byte ReadByte() { return this.Bytes.ReadByte(this.InputStream); } + /// + /// Decodes a single bit + /// + /// The + public bool DecodeBit() + { + if (this.Bits.UnreadBits == 0) + { + ErrorCodes errorCode = this.Bits.EnsureNBits(1, this); + if (errorCode != ErrorCodes.NoError) + { + throw new MissingFF00Exception(); + } + } + + bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0; + this.Bits.UnreadBits--; + this.Bits.Mask >>= 1; + return ret; + } + /// /// Reads exactly length bytes into data. It does not care about byte stuffing. /// /// The data to write to. /// The offset in the source buffer /// The number of bytes to read - internal void ReadFull(byte[] data, int offset, int length) + public void ReadFull(byte[] data, int offset, int length) { // Unread the overshot bytes, if any. if (this.Bytes.UnreadableBytes != 0) @@ -451,6 +472,125 @@ namespace ImageSharp.Formats } } + /// + /// Decodes the given number of bits + /// + /// The number of bits to decode. + /// The + public uint DecodeBits(int count) + { + if (this.Bits.UnreadBits < count) + { + ErrorCodes errorCode = this.Bits.EnsureNBits(count, this); + if (errorCode != ErrorCodes.NoError) + { + throw new MissingFF00Exception(); + } + } + + uint ret = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); + ret = (uint)(ret & ((1 << count) - 1)); + this.Bits.UnreadBits -= count; + this.Bits.Mask >>= count; + return ret; + } + + /// + /// Returns the next Huffman-coded value from the bit-stream, decoded according to the given value. + /// + /// The huffman value + /// The + public byte DecodeHuffman(ref HuffmanTree huffmanTree) + { + // Copy stuff to the stack: + if (huffmanTree.Length == 0) + { + throw new ImageFormatException("Uninitialized Huffman table"); + } + + if (this.Bits.UnreadBits < 8) + { + ErrorCodes errorCode = this.Bits.EnsureNBits(8, this); + + if (errorCode == ErrorCodes.NoError) + { + ushort v = + huffmanTree.Lut[(this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSize)) & 0xff]; + + if (v != 0) + { + byte n = (byte)((v & 0xff) - 1); + this.Bits.UnreadBits -= n; + this.Bits.Mask >>= n; + return (byte)(v >> 8); + } + } + else + { + this.UnreadByteStuffedByte(); + } + } + + int code = 0; + for (int i = 0; i < HuffmanTree.MaxCodeLength; i++) + { + if (this.Bits.UnreadBits == 0) + { + ErrorCodes errorCode = this.Bits.EnsureNBits(1, this); + if (errorCode != ErrorCodes.NoError) + { + throw new MissingFF00Exception(); + } + } + + if ((this.Bits.Accumulator & this.Bits.Mask) != 0) + { + code |= 1; + } + + this.Bits.UnreadBits--; + this.Bits.Mask >>= 1; + + if (code <= huffmanTree.MaxCodes[i]) + { + return huffmanTree.Values[huffmanTree.Indices[i] + code - huffmanTree.MinCodes[i]]; + } + + code <<= 1; + } + + throw new ImageFormatException("Bad Huffman code"); + } + + /// + /// Gets the representing the channel at a given component index + /// + /// The component index + /// The of the channel + public JpegPixelArea GetDestinationChannel(int compIndex) + { + if (this.ComponentCount == 1) + { + return this.grayImage; + } + else + { + switch (compIndex) + { + case 0: + return this.ycbcrImage.YChannel; + case 1: + return this.ycbcrImage.CbChannel; + case 2: + return this.ycbcrImage.CrChannel; + case 3: + return this.blackImage; + default: + throw new ImageFormatException("Too many components"); + } + } + } + /// /// Optimized method to pack bytes to the image from the YCbCr color space. /// This is faster than implicit casting as it avoids double packing. @@ -684,141 +824,6 @@ namespace ImageSharp.Formats this.AssignResolution(image); } - /// - /// Decodes a single bit - /// - /// The - internal bool DecodeBit() - { - if (this.Bits.UnreadBits == 0) - { - ErrorCodes errorCode = this.Bits.EnsureNBits(1, this); - if (errorCode != ErrorCodes.NoError) - { - throw new MissingFF00Exception(); - } - } - - bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0; - this.Bits.UnreadBits--; - this.Bits.Mask >>= 1; - return ret; - } - - /// - /// Decodes the given number of bits - /// - /// The number of bits to decode. - /// The - internal uint DecodeBits(int count) - { - if (this.Bits.UnreadBits < count) - { - ErrorCodes errorCode = this.Bits.EnsureNBits(count, this); - if (errorCode != ErrorCodes.NoError) - { - throw new MissingFF00Exception(); - } - } - - uint ret = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); - ret = (uint)(ret & ((1 << count) - 1)); - this.Bits.UnreadBits -= count; - this.Bits.Mask >>= count; - return ret; - } - - /// - /// Returns the next Huffman-coded value from the bit-stream, decoded according to the given value. - /// - /// The huffman value - /// The - internal byte DecodeHuffman(ref HuffmanTree huffmanTree) - { - // Copy stuff to the stack: - if (huffmanTree.Length == 0) - { - throw new ImageFormatException("Uninitialized Huffman table"); - } - - if (this.Bits.UnreadBits < 8) - { - ErrorCodes errorCode = this.Bits.EnsureNBits(8, this); - - if (errorCode == ErrorCodes.NoError) - { - ushort v = - huffmanTree.Lut[(this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSize)) & 0xff]; - - if (v != 0) - { - byte n = (byte)((v & 0xff) - 1); - this.Bits.UnreadBits -= n; - this.Bits.Mask >>= n; - return (byte)(v >> 8); - } - } - else - { - this.UnreadByteStuffedByte(); - } - } - - int code = 0; - for (int i = 0; i < HuffmanTree.MaxCodeLength; i++) - { - if (this.Bits.UnreadBits == 0) - { - ErrorCodes errorCode = this.Bits.EnsureNBits(1, this); - if (errorCode != ErrorCodes.NoError) - { - throw new MissingFF00Exception(); - } - } - - if ((this.Bits.Accumulator & this.Bits.Mask) != 0) - { - code |= 1; - } - - this.Bits.UnreadBits--; - this.Bits.Mask >>= 1; - - if (code <= huffmanTree.MaxCodes[i]) - { - return huffmanTree.Values[huffmanTree.Indices[i] + code - huffmanTree.MinCodes[i]]; - } - - code <<= 1; - } - - throw new ImageFormatException("Bad Huffman code"); - } - - internal JpegPixelArea GetDestinationChannel(int compIndex) - { - if (this.ComponentCount == 1) - { - return this.grayImage; - } - else - { - switch (compIndex) - { - case 0: - return this.ycbcrImage.YChannel; - case 1: - return this.ycbcrImage.CbChannel; - case 2: - return this.ycbcrImage.CrChannel; - case 3: - return this.blackImage; - default: - throw new ImageFormatException("Too many components"); - } - } - } - /// /// Returns a value indicating whether the image in an RGB image. /// diff --git a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs index cd0ec0af48..59dc5ce39c 100644 --- a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs @@ -420,7 +420,7 @@ namespace ImageSharp.Formats private void Encode444(PixelAccessor pixels) where TColor : struct, IPackedPixel, IEquatable { - // TODO: Need a JpegEncoderScanProcessor struct to encapsulate all this mess: + // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) Block8x8F b = default(Block8x8F); Block8x8F cb = default(Block8x8F); Block8x8F cr = default(Block8x8F); @@ -759,7 +759,7 @@ namespace ImageSharp.Formats private void WriteStartOfScan(PixelAccessor pixels) where TColor : struct, IPackedPixel, IEquatable { - // TODO: This method should be the entry point for a JpegEncoderScanProcessor struct + // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: We should allow grayscale writing. this.outputStream.Write(SosHeaderYCbCr, 0, SosHeaderYCbCr.Length); @@ -786,7 +786,7 @@ namespace ImageSharp.Formats private void Encode420(PixelAccessor pixels) where TColor : struct, IPackedPixel, IEquatable { - // TODO: Need a JpegEncoderScanProcessor struct to encapsulate all this mess: + // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) Block8x8F b = default(Block8x8F); BlockQuad cb = default(BlockQuad); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 9ac6386b15..6736548e6a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -433,21 +433,5 @@ namespace ImageSharp.Tests Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); } - - [Fact] - public unsafe void Copy_FromHeap() - { - Block8x8F[] blox = new Block8x8F[1]; - - blox[0].LoadFrom(Create8x8FloatData()); - Block8x8F clone = default(Block8x8F); - - fixed (Block8x8F* p = &blox[0]) - { - Block8x8F.Copy(&clone, p); - } - - Assert.Equal(blox[0], clone); - } } } \ No newline at end of file