diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
index 723ccd6b8..63d8b0e54 100644
--- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
@@ -373,5 +373,12 @@ 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/DecoderScanProcessor.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs
index b0c44c65b..c18b9cd99 100644
--- a/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs
+++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/DecoderScanProcessor.cs
@@ -1,9 +1,12 @@
namespace ImageSharp.Formats.Jpg
{
using System;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
internal unsafe struct DecoderScanProcessor
{
+ [StructLayout(LayoutKind.Sequential)]
public struct ComponentData
{
public Block8x8F Block;
@@ -12,11 +15,13 @@
public Block8x8F Temp2;
+ public Block8x8F QuantiazationTable;
+
public UnzigData Unzig;
- public fixed byte ScanData [3 * JpegDecoderCore.MaxComponents];
+ //public fixed byte ScanData [3 * JpegDecoderCore.MaxComponents];
- public fixed int Dc [JpegDecoderCore.MaxComponents];
+ //public fixed int Dc [JpegDecoderCore.MaxComponents];
public static ComponentData Create()
{
@@ -24,7 +29,6 @@
data.Unzig = UnzigData.Create();
return data;
}
-
}
public struct ComponentPointers
@@ -35,23 +39,31 @@
public Block8x8F* Temp2;
+ public Block8x8F* QuantiazationTable;
+
public int* Unzig;
- public Scan* Scan;
+ //public Scan* Scan;
- public int* Dc;
+ //public int* Dc;
public ComponentPointers(ComponentData* 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;
+ //this.Scan = (Scan*) basePtr->ScanData;
+ //this.Dc = basePtr->Dc;
}
}
-
+
+ //private void ResetDc()
+ //{
+ // Unsafe.InitBlock(this.Pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents);
+ //}
+
public int bx;
public int by;
@@ -68,17 +80,20 @@
public int myy;
+ public int scanComponentCount;
+
public ComponentData Data;
public ComponentPointers Pointers;
- public static void Init(DecoderScanProcessor* p, JpegDecoderCore decoder, int remaining)
+ public static void Init(DecoderScanProcessor* p, JpegDecoderCore decoder, int remaining, Scan[] scan)
{
+ p->Data = ComponentData.Create();
p->Pointers = new ComponentPointers(&p->Data);
-
+ p->InitImpl(decoder, remaining, scan);
}
- private void InitCommon(JpegDecoderCore decoder, int remaining)
+ private void InitImpl(JpegDecoderCore decoder, int remaining, Scan[] scan)
{
if (decoder.ComponentCount == 0)
{
@@ -91,9 +106,9 @@
}
decoder.ReadFull(decoder.Temp, 0, remaining);
- byte scanComponentCount = decoder.Temp[0];
+ this.scanComponentCount = decoder.Temp[0];
- int scanComponentCountX2 = 2 * scanComponentCount;
+ int scanComponentCountX2 = 2 * this.scanComponentCount;
if (remaining != 4 + scanComponentCountX2)
{
throw new ImageFormatException("SOS length inconsistent with number of components");
@@ -101,9 +116,9 @@
int totalHv = 0;
- for (int i = 0; i < scanComponentCount; i++)
+ for (int i = 0; i < this.scanComponentCount; i++)
{
- this.ProcessScanImpl(decoder, i, ref this.Pointers.Scan[i], ref totalHv);
+ this.ProcessScanImpl(decoder, i, ref scan[i], ref totalHv, scan);
}
// 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.
@@ -127,7 +142,7 @@
throw new ImageFormatException("Bad spectral selection bounds");
}
- if (this.zigStart != 0 && scanComponentCount != 1)
+ if (this.zigStart != 0 && this.scanComponentCount != 1)
{
throw new ImageFormatException("Progressive AC coefficients for more than one component");
}
@@ -146,12 +161,12 @@
if (decoder.IsProgressive)
{
- for (int i = 0; i < scanComponentCount; i++)
+ for (int i = 0; i < this.scanComponentCount; i++)
{
- int compIndex = this.Pointers.Scan[i].Index;
+ int compIndex = scan[i].Index;
if (decoder.ProgCoeffs[compIndex] == null)
{
- int size = mxx * myy * decoder.ComponentArray[compIndex].HorizontalFactor
+ int size = this.mxx * this.myy * decoder.ComponentArray[compIndex].HorizontalFactor
* decoder.ComponentArray[compIndex].VerticalFactor;
decoder.ProgCoeffs[compIndex] = new Block8x8F[size];
@@ -160,7 +175,7 @@
}
}
- private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv)
+ private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv, Scan[] scan)
{
// Component selector.
int cs = decoder.Temp[1 + (2 * i)];
@@ -181,7 +196,7 @@
currentScan.Index = (byte)compIndex;
- this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]);
+ this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex], scan);
}
@@ -190,7 +205,7 @@
int i,
ref Scan currentScan,
ref int totalHv,
- ref Component currentComponent)
+ ref Component currentComponent, Scan[] scan)
{
// 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
@@ -199,7 +214,7 @@
// into comp are unique.
for (int j = 0; j < i; j++)
{
- if (currentScan.Index == this.Pointers.Scan[j].Index)
+ if (currentScan.Index == scan[j].Index)
{
throw new ImageFormatException("Repeated component selector");
}
@@ -220,9 +235,391 @@
}
}
- private void InitProgressive(JpegDecoderCore decoder)
+ public void ProcessBlocks(JpegDecoderCore decoder, Scan[] scan, ref int[] dc)
+ {
+ int blockCount = 0;
+ int mcu = 0;
+ byte expectedRst = JpegConstants.Markers.RST0;
+
+ for (int my = 0; my < this.myy; my++)
+ {
+ for (int mx = 0; mx < this.mxx; mx++)
+ {
+ for (int i = 0; i < this.scanComponentCount; i++)
+ {
+ int compIndex = scan[i].Index;
+ int hi = decoder.ComponentArray[compIndex].HorizontalFactor;
+ int vi = decoder.ComponentArray[compIndex].VerticalFactor;
+
+ for (int j = 0; j < hi * vi; j++)
+ {
+ // The blocks are traversed one MCU at a time. For 4:2:0 chroma
+ // subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
+ // For a baseline 32x16 pixel image, the Y blocks visiting order is:
+ // 0 1 4 5
+ // 2 3 6 7
+ // For progressive images, the interleaved scans (those with component count > 1)
+ // are traversed as above, but non-interleaved scans are traversed left
+ // to right, top to bottom:
+ // 0 1 2 3
+ // 4 5 6 7
+ // Only DC scans (zigStart == 0) can be interleave AC scans must have
+ // only one component.
+ // To further complicate matters, for non-interleaved scans, there is no
+ // data for any blocks that are inside the image at the MCU level but
+ // outside the image at the pixel level. For example, a 24x16 pixel 4:2:0
+ // progressive image consists of two 16x16 MCUs. The interleaved scans
+ // will process 8 Y blocks:
+ // 0 1 4 5
+ // 2 3 6 7
+ // The non-interleaved scans will process only 6 Y blocks:
+ // 0 1 2
+ // 3 4 5
+ if (this.scanComponentCount != 1)
+ {
+ this.bx = (hi * mx) + (j % hi);
+ this.by = (vi * my) + (j / hi);
+ }
+ else
+ {
+ int q = this.mxx * hi;
+ this.bx = blockCount % q;
+ this.by = blockCount / q;
+ blockCount++;
+ if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight)
+ {
+ continue;
+ }
+ }
+
+ 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];
+
+ //Load the previous partially decoded coefficients, if applicable.
+ if (decoder.IsProgressive)
+ {
+ int blockIndex = ((this.by * this.mxx) * hi) + this.bx;
+ this.Data.Block = decoder.ProgCoeffs[compIndex][blockIndex];
+ }
+ else
+ {
+ this.Data.Block.Clear();
+ }
+
+ this.ProcessBlockImpl(decoder, i, compIndex, hi, scan, ref dc);
+
+
+ //fixed (Block8x8F* qtp = &decoder.QuantizationTables[qtIndex])
+ //{
+ // // Load the previous partially decoded coefficients, if applicable.
+ // if (decoder.IsProgressive)
+ // {
+ // int blockIndex = ((@by * mxx) * hi) + bx;
+
+ // fixed (Block8x8F* bp = &decoder.ProgCoeffs[compIndex][blockIndex])
+ // {
+ // Unsafe.CopyBlock(this.Pointers.Block, bp, (uint)sizeof(Block8x8F));
+ // }
+ // }
+ // else
+ // {
+ // this.Data.Block.Clear();
+ // }
+ // decoder.ProcessBlockImpl(this.ah, this.Pointers.Block, this.Pointers.Temp1, this.Pointers.Temp2, this.Pointers.Unzig, scan, i, this.zigStart, this.zigEnd, this.al, dc, compIndex, this.@by, this.mxx, hi, this.bx, qtp);
+ //}
+
+
+ }
+
+ // for j
+ }
+
+ // for i
+ mcu++;
+
+ if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < this.mxx * this.myy)
+ {
+ // 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.
+ decoder.ReadFull(decoder.Temp, 0, 2);
+ if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst)
+ {
+ throw new ImageFormatException("Bad RST marker");
+ }
+
+ expectedRst++;
+ if (expectedRst == JpegConstants.Markers.RST7 + 1)
+ {
+ expectedRst = JpegConstants.Markers.RST0;
+ }
+
+ // Reset the Huffman decoder.
+ decoder.Bits = default(Bits);
+
+ // Reset the DC components, as per section F.2.1.3.1.
+ //this.ResetDc();
+ dc = new int[JpegDecoderCore.MaxComponents];
+
+ // Reset the progressive decoder state, as per section G.1.2.2.
+ decoder.EobRun = 0;
+ }
+ }
+
+ // for mx
+ }
+ }
+
+ ///
+ /// The AC table index
+ ///
+ internal const int AcTable = 1;
+
+ ///
+ /// The DC table index
+ ///
+ internal const int DcTable = 0;
+
+ private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta)
+ {
+ Block8x8F* b = this.Pointers.Block;
+ // Refining a DC component is trivial.
+ if (this.zigStart == 0)
+ {
+ if (this.zigEnd != 0)
+ {
+ throw new ImageFormatException("Invalid state for zig DC component");
+ }
+
+ bool bit = decoder.DecodeBit();
+ if (bit)
+ {
+ int stuff = (int)Block8x8F.GetScalarAt(b, 0);
+
+ // int stuff = (int)b[0];
+ stuff |= delta;
+
+ // b[0] = stuff;
+ Block8x8F.SetScalarAt(b, 0, stuff);
+ }
+
+ return;
+ }
+
+ // 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)
+ {
+ for (; zig <= this.zigEnd; zig++)
+ {
+ bool done = false;
+ int z = 0;
+ byte val = decoder.DecodeHuffman(ref h);
+ int val0 = val >> 4;
+ int val1 = val & 0x0f;
+
+ switch (val1)
+ {
+ case 0:
+ if (val0 != 0x0f)
+ {
+ decoder.EobRun = (ushort)(1 << val0);
+ if (val0 != 0)
+ {
+ decoder.EobRun |= (ushort)decoder.DecodeBits(val0);
+ }
+
+ done = true;
+ }
+
+ break;
+ case 1:
+ z = delta;
+ bool bit = decoder.DecodeBit();
+ if (!bit)
+ {
+ z = -z;
+ }
+
+ break;
+ default:
+ throw new ImageFormatException("Unexpected Huffman code");
+ }
+
+ if (done)
+ {
+ break;
+ }
+
+ int blah = zig;
+
+ zig = this.RefineNonZeroes(decoder, zig, val0, delta);
+ if (zig > this.zigEnd)
+ {
+ throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}");
+ }
+
+ if (z != 0)
+ {
+ // b[Unzig[zig]] = z;
+ Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], z);
+ }
+ }
+ }
+
+ if (decoder.EobRun > 0)
+ {
+ decoder.EobRun--;
+ this.RefineNonZeroes(decoder, zig,-1, delta);
+ }
+ }
+
+ private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta)
+ {
+ var b = this.Pointers.Block;
+ for (; zig <= zigEnd; 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?
+ if (bu == 0)
+ {
+ if (nz == 0)
+ {
+ break;
+ }
+
+ nz--;
+ continue;
+ }
+
+ bool bit = decoder.DecodeBit();
+ if (!bit)
+ {
+ continue;
+ }
+
+ if (bu >= 0)
+ {
+ // b[u] += delta;
+ Block8x8F.SetScalarAt(b, u, bu + delta);
+ }
+ else
+ {
+ // b[u] -= delta;
+ Block8x8F.SetScalarAt(b, u, bu - delta);
+ }
+ }
+
+ return zig;
+ }
+
+ private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi, Scan[] scan, ref int[] dc)
{
-
+ var b = this.Pointers.Block;
+ //var dc = this.Pointers.Dc;
+ int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector;
+ if (this.ah != 0)
+ {
+ this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al);
+ }
+ else
+ {
+ int zig = zigStart;
+ if (zig == 0)
+ {
+ zig++;
+
+ // Decode the DC coefficient, as specified in section F.2.2.1.
+ byte value =
+ decoder.DecodeHuffman(
+ ref decoder.HuffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]);
+ if (value > 16)
+ {
+ throw new ImageFormatException("Excessive DC component");
+ }
+
+ int deltaDC = decoder.Bits.ReceiveExtend(value, decoder);
+ dc[compIndex] += deltaDC;
+
+ // b[0] = dc[compIndex] << al;
+ Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al);
+ }
+
+ if (zig <= this.zigEnd && decoder.EobRun > 0)
+ {
+ decoder.EobRun--;
+ }
+ else
+ {
+ // Decode the AC coefficients, as specified in section F.2.2.2.
+ // Huffman huffv = ;
+ for (; zig <= this.zigEnd; zig++)
+ {
+ byte value = decoder.DecodeHuffman(ref decoder.HuffmanTrees[huffmannIdx]);
+ byte val0 = (byte)(value >> 4);
+ byte val1 = (byte)(value & 0x0f);
+ if (val1 != 0)
+ {
+ zig += val0;
+ if (zig > this.zigEnd)
+ {
+ break;
+ }
+
+ int ac = decoder.Bits.ReceiveExtend(val1, decoder);
+
+ // b[Unzig[zig]] = ac << al;
+ Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], ac << this.al);
+ }
+ else
+ {
+ if (val0 != 0x0f)
+ {
+ decoder.EobRun = (ushort)(1 << val0);
+ if (val0 != 0)
+ {
+ decoder.EobRun |= (ushort)decoder.DecodeBits(val0);
+ }
+
+ decoder.EobRun--;
+ break;
+ }
+
+ zig += 0x0f;
+ }
+ }
+ }
+ }
+
+ if (decoder.IsProgressive)
+ {
+ if (this.zigEnd != Block8x8F.ScalarCount - 1 || this.al != 0)
+ {
+ // We haven't completely decoded this 8x8 block. Save the coefficients.
+ // this.ProgCoeffs[compIndex][((@by * mxx) * hi) + bx] = b.Clone();
+ decoder.ProgCoeffs[compIndex][((this.by * this.mxx) * hi) + this.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
+ // *image.YCbCr buffers (the whole point of progressive encoding), but in Go,
+ // the jpeg.Decode function does not return until the entire image is decoded,
+ // so we "continue" here to avoid wasted computation.
+ return;
+ }
+ }
+
+ // Dequantize, perform the inverse DCT and store the block to the image.
+ Block8x8F.UnZig(b, this.Pointers.QuantiazationTable, this.Pointers.Unzig);
+
+ 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);
}
}
diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
index c17a2856e..7af8e534c 100644
--- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
@@ -7,6 +7,7 @@ namespace ImageSharp.Formats
using System;
using System.IO;
using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
using System.Threading.Tasks;
using ImageSharp.Formats.Jpg;
@@ -19,12 +20,12 @@ namespace ImageSharp.Formats
///
/// The AC table index
///
- private const int AcTable = 1;
+ internal const int AcTable = 1;
///
/// The DC table index
///
- private const int DcTable = 0;
+ internal const int DcTable = 0;
///
/// The maximum number of color components
@@ -44,7 +45,7 @@ namespace ImageSharp.Formats
///
/// The huffman trees
///
- private readonly HuffmanTree[] huffmanTrees;
+ internal HuffmanTree[] HuffmanTrees { get; }
///
/// Saved state between progressive-mode scans.
@@ -76,11 +77,11 @@ namespace ImageSharp.Formats
///
/// Holds the unprocessed bits that have been taken from the byte-stream.
///
- private Bits bits;
+ internal Bits Bits;
private JpegPixelArea blackImage;
- private int blockIndex;
+ //private int blockIndex;
///
/// The byte buffer.
@@ -95,7 +96,7 @@ namespace ImageSharp.Formats
///
/// End-of-Band run, specified in section G.1.2.2.
///
- private ushort eobRun;
+ internal ushort EobRun;
///
/// A grayscale image to decode to.
@@ -135,7 +136,7 @@ namespace ImageSharp.Formats
///
/// The restart interval
///
- private int restartInterval;
+ internal int RestartInterval { get; private set; }
///
/// The vertical resolution. Calculated if the image has a JFIF header.
@@ -152,15 +153,15 @@ namespace ImageSharp.Formats
///
public JpegDecoderCore()
{
- this.huffmanTrees = HuffmanTree.CreateHuffmanTrees();
+ 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.bits = default(Bits);
+ this.Bits = default(Bits);
this.bytes = Bytes.Create();
}
-
+
///
/// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent)
/// It's better tho have an error code for this!
@@ -333,7 +334,7 @@ namespace ImageSharp.Formats
return;
}
- this.ProcessStartOfScan(remaining);
+ this.ProcessStartOfScan22(remaining);
break;
case JpegConstants.Markers.DRI:
if (configOnly)
@@ -430,9 +431,9 @@ namespace ImageSharp.Formats
///
public void Dispose()
{
- for (int i = 0; i < this.huffmanTrees.Length; i++)
+ for (int i = 0; i < this.HuffmanTrees.Length; i++)
{
- this.huffmanTrees[i].Dispose();
+ this.HuffmanTrees[i].Dispose();
}
this.ycbcrImage?.Dispose();
@@ -440,7 +441,7 @@ namespace ImageSharp.Formats
this.grayImage.ReturnPooled();
this.blackImage.ReturnPooled();
}
-
+
///
/// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
///
@@ -462,7 +463,7 @@ namespace ImageSharp.Formats
// Unread the overshot bytes, if any.
if (this.bytes.UnreadableBytes != 0)
{
- if (this.bits.UnreadBits >= 8)
+ if (this.Bits.UnreadBits >= 8)
{
this.UnreadByteStuffedByte();
}
@@ -727,20 +728,20 @@ namespace ImageSharp.Formats
/// Decodes a single bit
///
/// The
- private bool DecodeBit()
+ internal bool DecodeBit()
{
- if (this.bits.UnreadBits == 0)
+ if (this.Bits.UnreadBits == 0)
{
- ErrorCodes errorCode = this.bits.EnsureNBits(1, this);
+ 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;
+ bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0;
+ this.Bits.UnreadBits--;
+ this.Bits.Mask >>= 1;
return ret;
}
@@ -749,21 +750,21 @@ namespace ImageSharp.Formats
///
/// The number of bits to decode.
/// The
- private uint DecodeBits(int count)
+ internal uint DecodeBits(int count)
{
- if (this.bits.UnreadBits < count)
+ if (this.Bits.UnreadBits < count)
{
- ErrorCodes errorCode = this.bits.EnsureNBits(count, this);
+ ErrorCodes errorCode = this.Bits.EnsureNBits(count, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
}
- uint ret = this.bits.Accumulator >> (this.bits.UnreadBits - count);
+ uint ret = this.Bits.Accumulator >> (this.Bits.UnreadBits - count);
ret = (uint)(ret & ((1 << count) - 1));
- this.bits.UnreadBits -= count;
- this.bits.Mask >>= count;
+ this.Bits.UnreadBits -= count;
+ this.Bits.Mask >>= count;
return ret;
}
@@ -772,7 +773,7 @@ namespace ImageSharp.Formats
///
/// The huffman value
/// The
- private byte DecodeHuffman(ref HuffmanTree huffmanTree)
+ internal byte DecodeHuffman(ref HuffmanTree huffmanTree)
{
// Copy stuff to the stack:
if (huffmanTree.Length == 0)
@@ -780,20 +781,20 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Uninitialized Huffman table");
}
- if (this.bits.UnreadBits < 8)
+ if (this.Bits.UnreadBits < 8)
{
- ErrorCodes errorCode = this.bits.EnsureNBits(8, this);
+ ErrorCodes errorCode = this.Bits.EnsureNBits(8, this);
if (errorCode == ErrorCodes.NoError)
{
ushort v =
- huffmanTree.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - HuffmanTree.LutSize)) & 0xff];
+ 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;
+ this.Bits.UnreadBits -= n;
+ this.Bits.Mask >>= n;
return (byte)(v >> 8);
}
}
@@ -806,22 +807,22 @@ namespace ImageSharp.Formats
int code = 0;
for (int i = 0; i < HuffmanTree.MaxCodeLength; i++)
{
- if (this.bits.UnreadBits == 0)
+ if (this.Bits.UnreadBits == 0)
{
- ErrorCodes errorCode = this.bits.EnsureNBits(1, this);
+ ErrorCodes errorCode = this.Bits.EnsureNBits(1, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
}
- if ((this.bits.Accumulator & this.bits.Mask) != 0)
+ if ((this.Bits.Accumulator & this.Bits.Mask) != 0)
{
code |= 1;
}
- this.bits.UnreadBits--;
- this.bits.Mask >>= 1;
+ this.Bits.UnreadBits--;
+ this.Bits.Mask >>= 1;
if (code <= huffmanTree.MaxCodes[i])
{
@@ -834,7 +835,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Bad Huffman code");
}
- private JpegPixelArea GetDestinationChannel(int compIndex)
+ internal JpegPixelArea GetDestinationChannel(int compIndex)
{
if (this.ComponentCount == 1)
{
@@ -1089,7 +1090,7 @@ namespace ImageSharp.Formats
}
}
- private void ProcessBlockImpl(
+ internal void ProcessBlockImpl(
int ah,
Block8x8F* b,
Block8x8F* temp1,
@@ -1111,7 +1112,7 @@ namespace ImageSharp.Formats
int huffmannIdx = (AcTable * HuffmanTree.ThRowSize) + scan[i].AcTableSelector;
if (ah != 0)
{
- this.Refine(b, ref this.huffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al);
+ this.Refine(b, ref this.HuffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al);
}
else
{
@@ -1123,22 +1124,22 @@ namespace ImageSharp.Formats
// Decode the DC coefficient, as specified in section F.2.2.1.
byte value =
this.DecodeHuffman(
- ref this.huffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]);
+ ref this.HuffmanTrees[(DcTable * HuffmanTree.ThRowSize) + scan[i].DcTableSelector]);
if (value > 16)
{
throw new ImageFormatException("Excessive DC component");
}
- int deltaDC = this.bits.ReceiveExtend(value, this);
+ int deltaDC = this.Bits.ReceiveExtend(value, this);
dc[compIndex] += deltaDC;
// b[0] = dc[compIndex] << al;
Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al);
}
- if (zig <= zigEnd && this.eobRun > 0)
+ if (zig <= zigEnd && this.EobRun > 0)
{
- this.eobRun--;
+ this.EobRun--;
}
else
{
@@ -1146,7 +1147,7 @@ namespace ImageSharp.Formats
// Huffman huffv = ;
for (; zig <= zigEnd; zig++)
{
- byte value = this.DecodeHuffman(ref this.huffmanTrees[huffmannIdx]);
+ byte value = this.DecodeHuffman(ref this.HuffmanTrees[huffmannIdx]);
byte val0 = (byte)(value >> 4);
byte val1 = (byte)(value & 0x0f);
if (val1 != 0)
@@ -1157,7 +1158,7 @@ namespace ImageSharp.Formats
break;
}
- int ac = this.bits.ReceiveExtend(val1, this);
+ int ac = this.Bits.ReceiveExtend(val1, this);
// b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al);
@@ -1166,13 +1167,13 @@ namespace ImageSharp.Formats
{
if (val0 != 0x0f)
{
- this.eobRun = (ushort)(1 << val0);
+ this.EobRun = (ushort)(1 << val0);
if (val0 != 0)
{
- this.eobRun |= (ushort)this.DecodeBits(val0);
+ this.EobRun |= (ushort)this.DecodeBits(val0);
}
- this.eobRun--;
+ this.EobRun--;
break;
}
@@ -1276,7 +1277,7 @@ namespace ImageSharp.Formats
}
int huffTreeIndex = (tc * HuffmanTree.ThRowSize) + th;
- this.huffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.Temp, ref remaining);
+ this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.Temp, ref remaining);
}
}
@@ -1293,7 +1294,7 @@ namespace ImageSharp.Formats
}
this.ReadFull(this.Temp, 0, 2);
- this.restartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1];
+ this.RestartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1];
}
///
@@ -1584,9 +1585,14 @@ namespace ImageSharp.Formats
private void ProcessStartOfScan22(int remaining)
{
- DecoderScanProcessor processor = default(DecoderScanProcessor);
- DecoderScanProcessor.Init(&processor, this, remaining);
- this.MakeImage(processor.mxx, processor.myy);
+ Scan[] scan = new Scan[MaxComponents];
+ int[] dc = new int[MaxComponents];
+
+ DecoderScanProcessor p = default(DecoderScanProcessor);
+ DecoderScanProcessor.Init(&p, this, remaining, scan);
+ this.MakeImage(p.mxx, p.myy);
+ p.ProcessBlocks(this, scan, ref dc);
+
}
///
@@ -1699,7 +1705,7 @@ namespace ImageSharp.Formats
}
}
- this.bits = default(Bits);
+ this.Bits = default(Bits);
int mcu = 0;
byte expectedRst = JpegConstants.Markers.RST0;
@@ -1785,34 +1791,19 @@ namespace ImageSharp.Formats
// Load the previous partially decoded coefficients, if applicable.
if (this.IsProgressive)
{
- this.blockIndex = ((@by * mxx) * hi) + bx;
-
- fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][this.blockIndex])
+ int blockIndex = ((@by * mxx) * hi) + bx;
+
+ fixed (Block8x8F* bp = &this.ProgCoeffs[compIndex][blockIndex])
{
- this.ProcessBlockImpl(
- ah,
- bp,
- &temp1,
- &temp2,
- unzigPtr,
- scan,
- i,
- zigStart,
- zigEnd,
- al,
- dc,
- compIndex,
- @by,
- mxx,
- hi,
- bx,
- qtp);
+ Unsafe.CopyBlock(&b, bp, (uint) sizeof(Block8x8F) );
}
}
else
{
b.Clear();
- this.ProcessBlockImpl(
+ }
+
+ this.ProcessBlockImpl(
ah,
&b,
&temp1,
@@ -1830,7 +1821,6 @@ namespace ImageSharp.Formats
hi,
bx,
qtp);
- }
}
}
@@ -1840,7 +1830,7 @@ namespace ImageSharp.Formats
// for i
mcu++;
- if (this.restartInterval > 0 && mcu % this.restartInterval == 0 && mcu < mxx * myy)
+ if (this.RestartInterval > 0 && mcu % this.RestartInterval == 0 && mcu < mxx * myy)
{
// 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.
@@ -1857,13 +1847,13 @@ namespace ImageSharp.Formats
}
// Reset the Huffman decoder.
- this.bits = default(Bits);
+ this.Bits = default(Bits);
// Reset the DC components, as per section F.2.1.3.1.
dc = new int[MaxComponents];
// Reset the progressive decoder state, as per section G.1.2.2.
- this.eobRun = 0;
+ this.EobRun = 0;
}
}
@@ -1909,7 +1899,7 @@ namespace ImageSharp.Formats
// Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3.
int zig = zigStart;
- if (this.eobRun == 0)
+ if (this.EobRun == 0)
{
for (; zig <= zigEnd; zig++)
{
@@ -1924,10 +1914,10 @@ namespace ImageSharp.Formats
case 0:
if (val0 != 0x0f)
{
- this.eobRun = (ushort)(1 << val0);
+ this.EobRun = (ushort)(1 << val0);
if (val0 != 0)
{
- this.eobRun |= (ushort)this.DecodeBits(val0);
+ this.EobRun |= (ushort)this.DecodeBits(val0);
}
done = true;
@@ -1968,9 +1958,9 @@ namespace ImageSharp.Formats
}
}
- if (this.eobRun > 0)
+ if (this.EobRun > 0)
{
- this.eobRun--;
+ this.EobRun--;
this.RefineNonZeroes(b, zig, zigEnd, -1, delta, unzigPtr);
}
}
@@ -2035,7 +2025,7 @@ namespace ImageSharp.Formats
// Unread the overshot bytes, if any.
if (this.bytes.UnreadableBytes != 0)
{
- if (this.bits.UnreadBits >= 8)
+ if (this.Bits.UnreadBits >= 8)
{
this.UnreadByteStuffedByte();
}
@@ -2073,11 +2063,11 @@ namespace ImageSharp.Formats
{
this.bytes.I -= this.bytes.UnreadableBytes;
this.bytes.UnreadableBytes = 0;
- if (this.bits.UnreadBits >= 8)
+ if (this.Bits.UnreadBits >= 8)
{
- this.bits.Accumulator >>= 8;
- this.bits.UnreadBits -= 8;
- this.bits.Mask >>= 8;
+ this.Bits.Accumulator >>= 8;
+ this.Bits.UnreadBits -= 8;
+ this.Bits.Mask >>= 8;
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
index 57b3c56f8..9bae8d9f3 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
@@ -428,5 +428,21 @@ 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