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