diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
index 4b3b26da95..541515cb41 100644
--- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
@@ -7,139 +7,82 @@ namespace ImageSharp.Formats
using System;
using System.IO;
using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
using System.Threading.Tasks;
using ImageSharp.Formats.Jpg;
-
+
///
- /// Performs the jpeg decoding operation.
+ /// Performs the jpeg decoding operation.
///
internal unsafe class JpegDecoderCore : IDisposable
{
///
- /// The maximum number of color components
- ///
- internal const int MaxComponents = 4;
-
- ///
- /// The maximum number of quantization tables
- ///
- private const int MaxTq = 3;
-
- ///
- /// The component array
- ///
- internal Component[] ComponentArray { get; }
-
- ///
- /// The huffman trees
- ///
- internal HuffmanTree[] HuffmanTrees { get; }
-
- ///
- /// Saved state between progressive-mode scans.
+ /// The maximum number of color components
///
- internal Block8x8F[][] ProgCoeffs { get; }
+ public const int MaxComponents = 4;
///
- /// Quantization tables, in zigzag order.
- ///
- internal Block8x8F[] QuantizationTables { get; }
-
- ///
- /// A temporary buffer for holding pixels
- ///
- internal byte[] Temp { get; }
-
- // TODO: the usage of this buffer is unclean + need to move it to the stack for performance
-
- ///
- /// The App14 marker color-space
+ /// The App14 marker color-space
///
private byte adobeTransform;
///
- /// Whether the image is in CMYK format with an App14 marker
+ /// Whether the image is in CMYK format with an App14 marker
///
private bool adobeTransformValid;
///
- /// Holds the unprocessed bits that have been taken from the byte-stream.
+ /// Holds the unprocessed bits that have been taken from the byte-stream.
///
- internal Bits Bits;
-
- private JpegPixelArea blackImage;
-
- //private int blockIndex;
+ public Bits Bits;
///
- /// The byte buffer.
+ /// The byte buffer.
///
- private Bytes bytes;
-
+ public Bytes Bytes;
+
///
- /// The number of color components within the image.
+ /// End-of-Band run, specified in section G.1.2.2.
///
- internal int ComponentCount { get; private set; }
+ public ushort EobRun;
///
- /// End-of-Band run, specified in section G.1.2.2.
+ /// The black image to decode to.
///
- internal ushort EobRun;
+ private JpegPixelArea blackImage;
///
- /// A grayscale image to decode to.
+ /// A grayscale image to decode to.
///
private JpegPixelArea grayImage;
///
- /// The horizontal resolution. Calculated if the image has a JFIF header.
+ /// The horizontal resolution. Calculated if the image has a JFIF header.
///
private short horizontalResolution;
-
- ///
- /// The image height
- ///
- internal int ImageHeight { get; private set; }
-
- ///
- /// The image width
- ///
- internal int ImageWidth { get; private set; }
-
+
///
- /// The byte buffer.
+ /// The maximum number of quantization tables
///
- private Stream inputStream;
-
+ private const int MaxTq = 3;
+
///
- /// Whether the image has a JFIF header
+ /// Whether the image has a JFIF header
///
private bool isJfif;
-
- ///
- /// Whether the image is interlaced (progressive)
- ///
- public bool IsProgressive { get; private set; }
-
- ///
- /// The restart interval
- ///
- internal int RestartInterval { get; private set; }
-
+
///
- /// The vertical resolution. Calculated if the image has a JFIF header.
+ /// The vertical resolution. Calculated if the image has a JFIF header.
///
private short verticalResolution;
///
- /// The full color image to decode to.
+ /// The full color image to decode to.
///
private YCbCrImage ycbcrImage;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
public JpegDecoderCore()
{
@@ -149,56 +92,83 @@ namespace ImageSharp.Formats
this.ComponentArray = new Component[MaxComponents];
this.ProgCoeffs = new Block8x8F[MaxComponents][];
this.Bits = default(Bits);
- this.bytes = Bytes.Create();
+ this.Bytes = Bytes.Create();
}
-
+
///
- /// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent)
- /// It's better tho have an error code for this!
+ /// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent)
+ /// It's better tho have an error code for this!
///
internal enum ErrorCodes
{
///
- /// NoError
+ /// NoError
///
NoError,
///
- /// MissingFF00
+ /// MissingFF00
///
MissingFF00
}
+
///
- /// Gets or sets the byte buffer.
+ /// The component array
///
- public Bytes Bytes
- {
- get
- {
- return this.bytes;
- }
+ public Component[] ComponentArray { get; }
- set
- {
- this.bytes = value;
- }
- }
+ ///
+ /// The huffman trees
+ ///
+ public HuffmanTree[] HuffmanTrees { get; }
///
- /// Gets the input stream.
+ /// Saved state between progressive-mode scans.
///
- public Stream InputStream
- {
- get
- {
- return this.inputStream;
- }
- }
+ public Block8x8F[][] ProgCoeffs { get; }
+
+ ///
+ /// Quantization tables, in zigzag order.
+ ///
+ public Block8x8F[] QuantizationTables { get; }
+
+ ///
+ /// A temporary buffer for holding pixels
+ ///
+ // 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.
+ ///
+ public int ComponentCount { get; private set; }
+ ///
+ /// The image height
+ ///
+ public int ImageHeight { get; private set; }
+
+ ///
+ /// The image width
+ ///
+ public int ImageWidth { get; private set; }
+
+ ///
+ /// Gets the input stream.
+ ///
+ public Stream InputStream { get; private set; }
+ ///
+ /// Whether the image is interlaced (progressive)
+ ///
+ public bool IsProgressive { get; private set; }
///
- /// Decodes the image from the specified this._stream and sets
- /// the data to image.
+ /// The restart interval
+ ///
+ public int RestartInterval { get; private set; }
+
+ ///
+ /// Decodes the image from the specified this._stream and sets
+ /// the data to image.
///
/// The pixel format.
/// The image, where the data should be set to.
@@ -207,7 +177,7 @@ namespace ImageSharp.Formats
public void Decode(Image image, Stream stream, bool configOnly)
where TColor : struct, IPackedPixel, IEquatable
{
- this.inputStream = stream;
+ this.InputStream = stream;
// Check for the Start Of Image marker.
this.ReadFull(this.Temp, 0, 2);
@@ -417,7 +387,7 @@ namespace ImageSharp.Formats
}
///
- /// Dispose
+ /// Dispose
///
public void Dispose()
{
@@ -427,23 +397,23 @@ namespace ImageSharp.Formats
}
this.ycbcrImage?.Dispose();
- this.bytes.Dispose();
+ this.Bytes.Dispose();
this.grayImage.ReturnPooled();
this.blackImage.ReturnPooled();
}
-
+
///
- /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
+ /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
///
/// The
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByte()
{
- return this.bytes.ReadByte(this.inputStream);
+ return this.Bytes.ReadByte(this.InputStream);
}
///
- /// Reads exactly length bytes into data. It does not care about byte stuffing.
+ /// Reads exactly length bytes into data. It does not care about byte stuffing.
///
/// The data to write to.
/// The offset in the source buffer
@@ -451,39 +421,39 @@ namespace ImageSharp.Formats
internal void ReadFull(byte[] data, int offset, int length)
{
// Unread the overshot bytes, if any.
- if (this.bytes.UnreadableBytes != 0)
+ if (this.Bytes.UnreadableBytes != 0)
{
if (this.Bits.UnreadBits >= 8)
{
this.UnreadByteStuffedByte();
}
- this.bytes.UnreadableBytes = 0;
+ this.Bytes.UnreadableBytes = 0;
}
while (length > 0)
{
- if (this.bytes.J - this.bytes.I >= length)
+ if (this.Bytes.J - this.Bytes.I >= length)
{
- Array.Copy(this.bytes.Buffer, this.bytes.I, data, offset, length);
- this.bytes.I += length;
+ Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, length);
+ this.Bytes.I += length;
length -= length;
}
else
{
- Array.Copy(this.bytes.Buffer, this.bytes.I, data, offset, this.bytes.J - this.bytes.I);
- offset += this.bytes.J - this.bytes.I;
- length -= this.bytes.J - this.bytes.I;
- this.bytes.I += this.bytes.J - this.bytes.I;
+ Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, this.Bytes.J - this.Bytes.I);
+ offset += this.Bytes.J - this.Bytes.I;
+ length -= this.Bytes.J - this.Bytes.I;
+ this.Bytes.I += this.Bytes.J - this.Bytes.I;
- this.bytes.Fill(this.inputStream);
+ this.Bytes.Fill(this.InputStream);
}
}
}
///
- /// Optimized method to pack bytes to the image from the YCbCr color space.
- /// This is faster than implicit casting as it avoids double packing.
+ /// Optimized method to pack bytes to the image from the YCbCr color space.
+ /// This is faster than implicit casting as it avoids double packing.
///
/// The pixel format.
/// The packed pixel.
@@ -505,7 +475,7 @@ namespace ImageSharp.Formats
}
///
- /// Assigns the horizontal and vertical resolution to the image if it has a JFIF header.
+ /// Assigns the horizontal and vertical resolution to the image if it has a JFIF header.
///
/// The pixel format.
/// The image to assign the resolution to.
@@ -520,7 +490,7 @@ namespace ImageSharp.Formats
}
///
- /// Converts the image from the original CMYK image pixels.
+ /// Converts the image from the original CMYK image pixels.
///
/// The pixel format.
/// The image width.
@@ -539,28 +509,28 @@ namespace ImageSharp.Formats
0,
height,
y =>
+ {
+ int yo = this.ycbcrImage.GetRowYOffset(y);
+ int co = this.ycbcrImage.GetRowCOffset(y);
+
+ for (int x = 0; x < width; x++)
{
- int yo = this.ycbcrImage.GetRowYOffset(y);
- int co = this.ycbcrImage.GetRowCOffset(y);
-
- for (int x = 0; x < width; x++)
- {
- byte cyan = this.ycbcrImage.YPixels[yo + x];
- byte magenta = this.ycbcrImage.CbPixels[co + (x / scale)];
- byte yellow = this.ycbcrImage.CrPixels[co + (x / scale)];
-
- TColor packed = default(TColor);
- this.PackCmyk(ref packed, cyan, magenta, yellow, x, y);
- pixels[x, y] = packed;
- }
- });
+ byte cyan = this.ycbcrImage.YPixels[yo + x];
+ byte magenta = this.ycbcrImage.CbPixels[co + (x / scale)];
+ byte yellow = this.ycbcrImage.CrPixels[co + (x / scale)];
+
+ TColor packed = default(TColor);
+ this.PackCmyk(ref packed, cyan, magenta, yellow, x, y);
+ pixels[x, y] = packed;
+ }
+ });
}
this.AssignResolution(image);
}
///
- /// Converts the image from the original grayscale image pixels.
+ /// Converts the image from the original grayscale image pixels.
///
/// The pixel format.
/// The image width.
@@ -578,24 +548,24 @@ namespace ImageSharp.Formats
height,
Bootstrapper.ParallelOptions,
y =>
+ {
+ int yoff = this.grayImage.GetRowOffset(y);
+ for (int x = 0; x < width; x++)
{
- int yoff = this.grayImage.GetRowOffset(y);
- for (int x = 0; x < width; x++)
- {
- byte rgb = this.grayImage.Pixels[yoff + x];
-
- TColor packed = default(TColor);
- packed.PackFromBytes(rgb, rgb, rgb, 255);
- pixels[x, y] = packed;
- }
- });
+ byte rgb = this.grayImage.Pixels[yoff + x];
+
+ TColor packed = default(TColor);
+ packed.PackFromBytes(rgb, rgb, rgb, 255);
+ pixels[x, y] = packed;
+ }
+ });
}
this.AssignResolution(image);
}
///
- /// Converts the image from the original RBG image pixels.
+ /// Converts the image from the original RBG image pixels.
///
/// The pixel format.
/// The image width.
@@ -614,28 +584,28 @@ namespace ImageSharp.Formats
height,
Bootstrapper.ParallelOptions,
y =>
+ {
+ int yo = this.ycbcrImage.GetRowYOffset(y);
+ int co = this.ycbcrImage.GetRowCOffset(y);
+
+ for (int x = 0; x < width; x++)
{
- int yo = this.ycbcrImage.GetRowYOffset(y);
- int co = this.ycbcrImage.GetRowCOffset(y);
-
- for (int x = 0; x < width; x++)
- {
- byte red = this.ycbcrImage.YPixels[yo + x];
- byte green = this.ycbcrImage.CbPixels[co + (x / scale)];
- byte blue = this.ycbcrImage.CrPixels[co + (x / scale)];
-
- TColor packed = default(TColor);
- packed.PackFromBytes(red, green, blue, 255);
- pixels[x, y] = packed;
- }
- });
+ byte red = this.ycbcrImage.YPixels[yo + x];
+ byte green = this.ycbcrImage.CbPixels[co + (x / scale)];
+ byte blue = this.ycbcrImage.CrPixels[co + (x / scale)];
+
+ TColor packed = default(TColor);
+ packed.PackFromBytes(red, green, blue, 255);
+ pixels[x, y] = packed;
+ }
+ });
}
this.AssignResolution(image);
}
///
- /// Converts the image from the original YCbCr image pixels.
+ /// Converts the image from the original YCbCr image pixels.
///
/// The pixel format.
/// The image width.
@@ -654,28 +624,28 @@ namespace ImageSharp.Formats
height,
Bootstrapper.ParallelOptions,
y =>
+ {
+ int yo = this.ycbcrImage.GetRowYOffset(y);
+ int co = this.ycbcrImage.GetRowCOffset(y);
+
+ for (int x = 0; x < width; x++)
{
- int yo = this.ycbcrImage.GetRowYOffset(y);
- int co = this.ycbcrImage.GetRowCOffset(y);
-
- for (int x = 0; x < width; x++)
- {
- byte yy = this.ycbcrImage.YPixels[yo + x];
- byte cb = this.ycbcrImage.CbPixels[co + (x / scale)];
- byte cr = this.ycbcrImage.CrPixels[co + (x / scale)];
-
- TColor packed = default(TColor);
- PackYcbCr(ref packed, yy, cb, cr);
- pixels[x, y] = packed;
- }
- });
+ byte yy = this.ycbcrImage.YPixels[yo + x];
+ byte cb = this.ycbcrImage.CbPixels[co + (x / scale)];
+ byte cr = this.ycbcrImage.CrPixels[co + (x / scale)];
+
+ TColor packed = default(TColor);
+ PackYcbCr(ref packed, yy, cb, cr);
+ pixels[x, y] = packed;
+ }
+ });
}
this.AssignResolution(image);
}
///
- /// Converts the image from the original YCCK image pixels.
+ /// Converts the image from the original YCCK image pixels.
///
/// The pixel format.
/// The image width.
@@ -694,28 +664,28 @@ namespace ImageSharp.Formats
0,
height,
y =>
+ {
+ int yo = this.ycbcrImage.GetRowYOffset(y);
+ int co = this.ycbcrImage.GetRowCOffset(y);
+
+ for (int x = 0; x < width; x++)
{
- int yo = this.ycbcrImage.GetRowYOffset(y);
- int co = this.ycbcrImage.GetRowCOffset(y);
-
- for (int x = 0; x < width; x++)
- {
- byte yy = this.ycbcrImage.YPixels[yo + x];
- byte cb = this.ycbcrImage.CbPixels[co + (x / scale)];
- byte cr = this.ycbcrImage.CrPixels[co + (x / scale)];
-
- TColor packed = default(TColor);
- this.PackYcck(ref packed, yy, cb, cr, x, y);
- pixels[x, y] = packed;
- }
- });
+ byte yy = this.ycbcrImage.YPixels[yo + x];
+ byte cb = this.ycbcrImage.CbPixels[co + (x / scale)];
+ byte cr = this.ycbcrImage.CrPixels[co + (x / scale)];
+
+ TColor packed = default(TColor);
+ this.PackYcck(ref packed, yy, cb, cr, x, y);
+ pixels[x, y] = packed;
+ }
+ });
}
this.AssignResolution(image);
}
///
- /// Decodes a single bit
+ /// Decodes a single bit
///
/// The
internal bool DecodeBit()
@@ -736,7 +706,7 @@ namespace ImageSharp.Formats
}
///
- /// Decodes the given number of bits
+ /// Decodes the given number of bits
///
/// The number of bits to decode.
/// The
@@ -759,7 +729,7 @@ namespace ImageSharp.Formats
}
///
- /// Returns the next Huffman-coded value from the bit-stream, decoded according to the given value.
+ /// Returns the next Huffman-coded value from the bit-stream, decoded according to the given value.
///
/// The huffman value
/// The
@@ -850,10 +820,10 @@ namespace ImageSharp.Formats
}
///
- /// Returns a value indicating whether the image in an RGB image.
+ /// Returns a value indicating whether the image in an RGB image.
///
///
- /// The .
+ /// The .
///
private bool IsRGB()
{
@@ -874,7 +844,7 @@ namespace ImageSharp.Formats
}
///
- /// Makes the image from the buffer.
+ /// Makes the image from the buffer.
///
/// The horizontal MCU count
/// The vertical MCU count
@@ -932,8 +902,8 @@ namespace ImageSharp.Formats
}
///
- /// Optimized method to pack bytes to the image from the CMYK color space.
- /// This is faster than implicit casting as it avoids double packing.
+ /// Optimized method to pack bytes to the image from the CMYK color space.
+ /// This is faster than implicit casting as it avoids double packing.
///
/// The pixel format.
/// The packed pixel.
@@ -957,8 +927,8 @@ namespace ImageSharp.Formats
}
///
- /// Optimized method to pack bytes to the image from the YCCK color space.
- /// This is faster than implicit casting as it avoids double packing.
+ /// Optimized method to pack bytes to the image from the YCCK color space.
+ /// This is faster than implicit casting as it avoids double packing.
///
/// The pixel format.
/// The packed pixel.
@@ -995,9 +965,9 @@ namespace ImageSharp.Formats
}
///
- /// Processes the "Adobe" APP14 segment stores image encoding information for DCT filters.
- /// This segment may be copied or deleted as a block using the Extra "Adobe" tag, but note that it is not
- /// deleted by default when deleting all metadata because it may affect the appearance of the image.
+ /// Processes the "Adobe" APP14 segment stores image encoding information for DCT filters.
+ /// This segment may be copied or deleted as a block using the Extra "Adobe" tag, but note that it is not
+ /// deleted by default when deleting all metadata because it may affect the appearance of the image.
///
/// The remaining number of bytes in the stream.
private void ProcessApp14Marker(int remaining)
@@ -1025,7 +995,7 @@ namespace ImageSharp.Formats
}
///
- /// Processes the App1 marker retrieving any stored metadata
+ /// Processes the App1 marker retrieving any stored metadata
///
/// The pixel format.
/// The remaining bytes in the segment block.
@@ -1050,7 +1020,7 @@ namespace ImageSharp.Formats
}
///
- /// Processes the application header containing the JFIF identifier plus extra data.
+ /// Processes the application header containing the JFIF identifier plus extra data.
///
/// The remaining bytes in the segment block.
private void ProcessApplicationHeader(int remaining)
@@ -1081,8 +1051,8 @@ namespace ImageSharp.Formats
}
///
- /// Processes a Define Huffman Table marker, and initializes a huffman
- /// struct from its contents. Specified in section B.2.4.2.
+ /// Processes a Define Huffman Table marker, and initializes a huffman
+ /// struct from its contents. Specified in section B.2.4.2.
///
/// The remaining bytes in the segment block.
private void ProcessDefineHuffmanTablesMarker(int remaining)
@@ -1114,8 +1084,8 @@ namespace ImageSharp.Formats
}
///
- /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in
- /// macroblocks
+ /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in
+ /// macroblocks
///
/// The remaining bytes in the segment block.
private void ProcessDefineRestartIntervalMarker(int remaining)
@@ -1130,11 +1100,11 @@ namespace ImageSharp.Formats
}
///
- /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1.
+ /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1.
///
/// The remaining bytes in the segment block.
///
- /// Thrown if the tables do not match the header
+ /// Thrown if the tables do not match the header
///
private void ProcessDqt(int remaining)
{
@@ -1201,7 +1171,7 @@ namespace ImageSharp.Formats
}
///
- /// Processes the Start of Frame marker. Specified in section B.2.2.
+ /// Processes the Start of Frame marker. Specified in section B.2.2.
///
/// The remaining bytes in the segment block.
private void ProcessStartOfFrameMarker(int remaining)
@@ -1409,52 +1379,52 @@ namespace ImageSharp.Formats
}
///
- /// Skips the next n bytes.
+ /// Skips the next n bytes.
///
/// The number of bytes to ignore.
private void Skip(int count)
{
// Unread the overshot bytes, if any.
- if (this.bytes.UnreadableBytes != 0)
+ if (this.Bytes.UnreadableBytes != 0)
{
if (this.Bits.UnreadBits >= 8)
{
this.UnreadByteStuffedByte();
}
- this.bytes.UnreadableBytes = 0;
+ this.Bytes.UnreadableBytes = 0;
}
while (true)
{
- int m = this.bytes.J - this.bytes.I;
+ int m = this.Bytes.J - this.Bytes.I;
if (m > count)
{
m = count;
}
- this.bytes.I += m;
+ this.Bytes.I += m;
count -= m;
if (count == 0)
{
break;
}
- this.bytes.Fill(this.inputStream);
+ this.Bytes.Fill(this.InputStream);
}
}
///
- /// Undoes the most recent ReadByteStuffedByte call,
- /// giving a byte of data back from bits to bytes. The Huffman look-up table
- /// requires at least 8 bits for look-up, which means that Huffman decoding can
- /// sometimes overshoot and read one or two too many bytes. Two-byte overshoot
- /// can happen when expecting to read a 0xff 0x00 byte-stuffed byte.
+ /// Undoes the most recent ReadByteStuffedByte call,
+ /// giving a byte of data back from bits to bytes. The Huffman look-up table
+ /// requires at least 8 bits for look-up, which means that Huffman decoding can
+ /// sometimes overshoot and read one or two too many bytes. Two-byte overshoot
+ /// can happen when expecting to read a 0xff 0x00 byte-stuffed byte.
///
private void UnreadByteStuffedByte()
{
- this.bytes.I -= this.bytes.UnreadableBytes;
- this.bytes.UnreadableBytes = 0;
+ this.Bytes.I -= this.Bytes.UnreadableBytes;
+ this.Bytes.UnreadableBytes = 0;
if (this.Bits.UnreadBits >= 8)
{
this.Bits.Accumulator >>= 8;
@@ -1464,22 +1434,22 @@ namespace ImageSharp.Formats
}
///
- /// The EOF (End of File exception).
- /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker
+ /// The EOF (End of File exception).
+ /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker
///
internal class EOFException : Exception
{
}
///
- /// The missing ff00 exception.
+ /// The missing ff00 exception.
///
internal class MissingFF00Exception : Exception
{
}
///
- /// The short huffman data exception.
+ /// The short huffman data exception.
///
private class ShortHuffmanDataException : Exception
{