|
|
@ -7,7 +7,6 @@ namespace ImageSharp.Formats |
|
|
using System; |
|
|
using System; |
|
|
using System.IO; |
|
|
using System.IO; |
|
|
using System.Runtime.CompilerServices; |
|
|
using System.Runtime.CompilerServices; |
|
|
using System.Runtime.InteropServices; |
|
|
|
|
|
using System.Threading.Tasks; |
|
|
using System.Threading.Tasks; |
|
|
|
|
|
|
|
|
using ImageSharp.Formats.Jpg; |
|
|
using ImageSharp.Formats.Jpg; |
|
|
@ -20,39 +19,7 @@ namespace ImageSharp.Formats |
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// The maximum number of color components
|
|
|
/// The maximum number of color components
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
internal const int MaxComponents = 4; |
|
|
public const int MaxComponents = 4; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The maximum number of quantization tables
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private const int MaxTq = 3; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The component array
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal Component[] ComponentArray { get; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The huffman trees
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal HuffmanTree[] HuffmanTrees { get; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Saved state between progressive-mode scans.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal Block8x8F[][] ProgCoeffs { get; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Quantization tables, in zigzag order.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal Block8x8F[] QuantizationTables { get; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// A temporary buffer for holding pixels
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal byte[] Temp { get; } |
|
|
|
|
|
|
|
|
|
|
|
// TODO: the usage of this buffer is unclean + need to move it to the stack for performance
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// The App14 marker color-space
|
|
|
/// The App14 marker color-space
|
|
|
@ -67,26 +34,22 @@ namespace ImageSharp.Formats |
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Holds the unprocessed bits that have been taken from the byte-stream.
|
|
|
/// Holds the unprocessed bits that have been taken from the byte-stream.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
internal Bits Bits; |
|
|
public Bits Bits; |
|
|
|
|
|
|
|
|
private JpegPixelArea blackImage; |
|
|
|
|
|
|
|
|
|
|
|
//private int blockIndex;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// The byte buffer.
|
|
|
/// The byte buffer.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
private Bytes bytes; |
|
|
public Bytes Bytes; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// The number of color components within the image.
|
|
|
/// End-of-Band run, specified in section G.1.2.2.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
internal int ComponentCount { get; private set; } |
|
|
public ushort EobRun; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// End-of-Band run, specified in section G.1.2.2.
|
|
|
/// The black image to decode to.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
internal ushort EobRun; |
|
|
private JpegPixelArea blackImage; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// A grayscale image to decode to.
|
|
|
/// A grayscale image to decode to.
|
|
|
@ -99,35 +62,15 @@ namespace ImageSharp.Formats |
|
|
private short horizontalResolution; |
|
|
private short horizontalResolution; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// The image height
|
|
|
/// The maximum number of quantization tables
|
|
|
/// </summary>
|
|
|
|
|
|
internal int ImageHeight { get; private set; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The image width
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal int ImageWidth { get; private set; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The byte buffer.
|
|
|
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
private Stream inputStream; |
|
|
private const int MaxTq = 3; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Whether the image has a JFIF header
|
|
|
/// Whether the image has a JFIF header
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
private bool isJfif; |
|
|
private bool isJfif; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Whether the image is interlaced (progressive)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool IsProgressive { get; private set; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The restart interval
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal int RestartInterval { get; private set; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// The vertical resolution. Calculated if the image has a JFIF header.
|
|
|
/// The vertical resolution. Calculated if the image has a JFIF header.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
@ -149,7 +92,7 @@ namespace ImageSharp.Formats |
|
|
this.ComponentArray = new Component[MaxComponents]; |
|
|
this.ComponentArray = new Component[MaxComponents]; |
|
|
this.ProgCoeffs = new Block8x8F[MaxComponents][]; |
|
|
this.ProgCoeffs = new Block8x8F[MaxComponents][]; |
|
|
this.Bits = default(Bits); |
|
|
this.Bits = default(Bits); |
|
|
this.bytes = Bytes.Create(); |
|
|
this.Bytes = Bytes.Create(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
@ -169,32 +112,59 @@ namespace ImageSharp.Formats |
|
|
MissingFF00 |
|
|
MissingFF00 |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Gets or sets the byte buffer.
|
|
|
/// The component array
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
public Bytes Bytes |
|
|
public Component[] ComponentArray { get; } |
|
|
{ |
|
|
|
|
|
get |
|
|
|
|
|
{ |
|
|
|
|
|
return this.bytes; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
set |
|
|
/// <summary>
|
|
|
{ |
|
|
/// The huffman trees
|
|
|
this.bytes = value; |
|
|
/// </summary>
|
|
|
} |
|
|
public HuffmanTree[] HuffmanTrees { get; } |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Saved state between progressive-mode scans.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Block8x8F[][] ProgCoeffs { get; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Quantization tables, in zigzag order.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Block8x8F[] QuantizationTables { get; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// A temporary buffer for holding pixels
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
// TODO: the usage rules of this buffer seem to be unclean + need to consider stack-allocating it for perf
|
|
|
|
|
|
public byte[] Temp { get; } |
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The number of color components within the image.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int ComponentCount { get; private set; } |
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The image height
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int ImageHeight { get; private set; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The image width
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int ImageWidth { get; private set; } |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Gets the input stream.
|
|
|
/// Gets the input stream.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
public Stream InputStream |
|
|
public Stream InputStream { get; private set; } |
|
|
{ |
|
|
/// <summary>
|
|
|
get |
|
|
/// Whether the image is interlaced (progressive)
|
|
|
{ |
|
|
/// </summary>
|
|
|
return this.inputStream; |
|
|
public bool IsProgressive { get; private set; } |
|
|
} |
|
|
|
|
|
} |
|
|
/// <summary>
|
|
|
|
|
|
/// The restart interval
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int RestartInterval { get; private set; } |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Decodes the image from the specified this._stream and sets
|
|
|
/// Decodes the image from the specified this._stream and sets
|
|
|
@ -207,7 +177,7 @@ namespace ImageSharp.Formats |
|
|
public void Decode<TColor>(Image<TColor> image, Stream stream, bool configOnly) |
|
|
public void Decode<TColor>(Image<TColor> image, Stream stream, bool configOnly) |
|
|
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|
|
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|
|
{ |
|
|
{ |
|
|
this.inputStream = stream; |
|
|
this.InputStream = stream; |
|
|
|
|
|
|
|
|
// Check for the Start Of Image marker.
|
|
|
// Check for the Start Of Image marker.
|
|
|
this.ReadFull(this.Temp, 0, 2); |
|
|
this.ReadFull(this.Temp, 0, 2); |
|
|
@ -427,7 +397,7 @@ namespace ImageSharp.Formats |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.ycbcrImage?.Dispose(); |
|
|
this.ycbcrImage?.Dispose(); |
|
|
this.bytes.Dispose(); |
|
|
this.Bytes.Dispose(); |
|
|
this.grayImage.ReturnPooled(); |
|
|
this.grayImage.ReturnPooled(); |
|
|
this.blackImage.ReturnPooled(); |
|
|
this.blackImage.ReturnPooled(); |
|
|
} |
|
|
} |
|
|
@ -439,7 +409,7 @@ namespace ImageSharp.Formats |
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
internal byte ReadByte() |
|
|
internal byte ReadByte() |
|
|
{ |
|
|
{ |
|
|
return this.bytes.ReadByte(this.inputStream); |
|
|
return this.Bytes.ReadByte(this.InputStream); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
@ -451,32 +421,32 @@ namespace ImageSharp.Formats |
|
|
internal void ReadFull(byte[] data, int offset, int length) |
|
|
internal void ReadFull(byte[] data, int offset, int length) |
|
|
{ |
|
|
{ |
|
|
// Unread the overshot bytes, if any.
|
|
|
// Unread the overshot bytes, if any.
|
|
|
if (this.bytes.UnreadableBytes != 0) |
|
|
if (this.Bytes.UnreadableBytes != 0) |
|
|
{ |
|
|
{ |
|
|
if (this.Bits.UnreadBits >= 8) |
|
|
if (this.Bits.UnreadBits >= 8) |
|
|
{ |
|
|
{ |
|
|
this.UnreadByteStuffedByte(); |
|
|
this.UnreadByteStuffedByte(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.bytes.UnreadableBytes = 0; |
|
|
this.Bytes.UnreadableBytes = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
while (length > 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); |
|
|
Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, length); |
|
|
this.bytes.I += length; |
|
|
this.Bytes.I += length; |
|
|
length -= length; |
|
|
length -= length; |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
Array.Copy(this.bytes.Buffer, this.bytes.I, data, offset, 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; |
|
|
offset += this.Bytes.J - this.Bytes.I; |
|
|
length -= this.bytes.J - this.bytes.I; |
|
|
length -= this.Bytes.J - this.Bytes.I; |
|
|
this.bytes.I += 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); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -1415,32 +1385,32 @@ namespace ImageSharp.Formats |
|
|
private void Skip(int count) |
|
|
private void Skip(int count) |
|
|
{ |
|
|
{ |
|
|
// Unread the overshot bytes, if any.
|
|
|
// Unread the overshot bytes, if any.
|
|
|
if (this.bytes.UnreadableBytes != 0) |
|
|
if (this.Bytes.UnreadableBytes != 0) |
|
|
{ |
|
|
{ |
|
|
if (this.Bits.UnreadBits >= 8) |
|
|
if (this.Bits.UnreadBits >= 8) |
|
|
{ |
|
|
{ |
|
|
this.UnreadByteStuffedByte(); |
|
|
this.UnreadByteStuffedByte(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.bytes.UnreadableBytes = 0; |
|
|
this.Bytes.UnreadableBytes = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
while (true) |
|
|
while (true) |
|
|
{ |
|
|
{ |
|
|
int m = this.bytes.J - this.bytes.I; |
|
|
int m = this.Bytes.J - this.Bytes.I; |
|
|
if (m > count) |
|
|
if (m > count) |
|
|
{ |
|
|
{ |
|
|
m = count; |
|
|
m = count; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.bytes.I += m; |
|
|
this.Bytes.I += m; |
|
|
count -= m; |
|
|
count -= m; |
|
|
if (count == 0) |
|
|
if (count == 0) |
|
|
{ |
|
|
{ |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.bytes.Fill(this.inputStream); |
|
|
this.Bytes.Fill(this.InputStream); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -1453,8 +1423,8 @@ namespace ImageSharp.Formats |
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
private void UnreadByteStuffedByte() |
|
|
private void UnreadByteStuffedByte() |
|
|
{ |
|
|
{ |
|
|
this.bytes.I -= this.bytes.UnreadableBytes; |
|
|
this.Bytes.I -= this.Bytes.UnreadableBytes; |
|
|
this.bytes.UnreadableBytes = 0; |
|
|
this.Bytes.UnreadableBytes = 0; |
|
|
if (this.Bits.UnreadBits >= 8) |
|
|
if (this.Bits.UnreadBits >= 8) |
|
|
{ |
|
|
{ |
|
|
this.Bits.Accumulator >>= 8; |
|
|
this.Bits.Accumulator >>= 8; |
|
|
|