Browse Source

Merge remote-tracking branch 'origin/jpeg-optimizations-experimental' into jpeg-optimizations

af/merge-core
Anton Firszov 9 years ago
parent
commit
13dd34d75f
  1. 2
      ImageSharp.sln
  2. 46
      src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs
  3. 24
      src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs
  4. 8
      src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs
  5. 452
      src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs

2
ImageSharp.sln

@ -32,8 +32,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C06
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Tests46", "tests\ImageSharp.Tests46\ImageSharp.Tests46.csproj", "{88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU

46
src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs

@ -8,77 +8,77 @@ namespace ImageSharp.Formats.Jpg
using System.Buffers; using System.Buffers;
/// <summary> /// <summary>
/// Represents a Huffman tree /// Represents a Huffman tree
/// </summary> /// </summary>
internal struct HuffmanTree : IDisposable internal struct HuffmanTree : IDisposable
{ {
/// <summary> /// <summary>
/// The maximum (inclusive) number of codes in a Huffman tree. /// The maximum (inclusive) number of codes in a Huffman tree.
/// </summary> /// </summary>
public const int MaxNCodes = 256; public const int MaxNCodes = 256;
/// <summary> /// <summary>
/// The maximum (inclusive) number of bits in a Huffman code. /// The maximum (inclusive) number of bits in a Huffman code.
/// </summary> /// </summary>
public const int MaxCodeLength = 16; public const int MaxCodeLength = 16;
/// <summary> /// <summary>
/// The maximum number of Huffman table classes /// The maximum number of Huffman table classes
/// </summary> /// </summary>
public const int MaxTc = 1; public const int MaxTc = 1;
/// <summary> /// <summary>
/// The maximum number of Huffman table identifiers /// The maximum number of Huffman table identifiers
/// </summary> /// </summary>
public const int MaxTh = 3; public const int MaxTh = 3;
/// <summary> /// <summary>
/// Row size of the Huffman table /// Row size of the Huffman table
/// </summary> /// </summary>
public const int ThRowSize = MaxTh + 1; public const int ThRowSize = MaxTh + 1;
/// <summary> /// <summary>
/// Number of Hufman Trees in the Huffman table /// Number of Hufman Trees in the Huffman table
/// </summary> /// </summary>
public const int NumberOfTrees = (MaxTc + 1) * (MaxTh + 1); public const int NumberOfTrees = (MaxTc + 1) * (MaxTh + 1);
/// <summary> /// <summary>
/// The log-2 size of the Huffman decoder's look-up table. /// The log-2 size of the Huffman decoder's look-up table.
/// </summary> /// </summary>
public const int LutSize = 8; public const int LutSize = 8;
/// <summary> /// <summary>
/// Gets or sets the number of codes in the tree. /// Gets or sets the number of codes in the tree.
/// </summary> /// </summary>
public int Length; public int Length;
/// <summary> /// <summary>
/// Gets the look-up table for the next LutSize bits in the bit-stream. /// Gets the look-up table for the next LutSize bits in the bit-stream.
/// The high 8 bits of the uint16 are the encoded value. The low 8 bits /// The high 8 bits of the uint16 are the encoded value. The low 8 bits
/// are 1 plus the code length, or 0 if the value is too large to fit in /// are 1 plus the code length, or 0 if the value is too large to fit in
/// lutSize bits. /// lutSize bits.
/// </summary> /// </summary>
public ushort[] Lut; public ushort[] Lut;
/// <summary> /// <summary>
/// Gets the the decoded values, sorted by their encoding. /// Gets the the decoded values, sorted by their encoding.
/// </summary> /// </summary>
public byte[] Values; public byte[] Values;
/// <summary> /// <summary>
/// Gets the array of minimum codes. /// Gets the array of minimum codes.
/// MinCodes[i] is the minimum code of length i, or -1 if there are no codes of that length. /// MinCodes[i] is the minimum code of length i, or -1 if there are no codes of that length.
/// </summary> /// </summary>
public int[] MinCodes; public int[] MinCodes;
/// <summary> /// <summary>
/// Gets the array of maximum codes. /// Gets the array of maximum codes.
/// MaxCodes[i] is the maximum code of length i, or -1 if there are no codes of that length. /// MaxCodes[i] is the maximum code of length i, or -1 if there are no codes of that length.
/// </summary> /// </summary>
public int[] MaxCodes; public int[] MaxCodes;
/// <summary> /// <summary>
/// Gets the array of indices. Indices[i] is the index into Values of MinCodes[i]. /// Gets the array of indices. Indices[i] is the index into Values of MinCodes[i].
/// </summary> /// </summary>
public int[] Indices; public int[] Indices;
@ -89,7 +89,7 @@ namespace ImageSharp.Formats.Jpg
private static readonly ArrayPool<int> IntBuffer = ArrayPool<int>.Create(MaxCodeLength, 50); private static readonly ArrayPool<int> IntBuffer = ArrayPool<int>.Create(MaxCodeLength, 50);
/// <summary> /// <summary>
/// Creates and initializes an array of <see cref="HuffmanTree" /> instances of size <see cref="NumberOfTrees" /> /// Creates and initializes an array of <see cref="HuffmanTree" /> instances of size <see cref="NumberOfTrees" />
/// </summary> /// </summary>
/// <returns>An array of <see cref="HuffmanTree" /> instances representing the Huffman tables</returns> /// <returns>An array of <see cref="HuffmanTree" /> instances representing the Huffman tables</returns>
public static HuffmanTree[] CreateHuffmanTrees() public static HuffmanTree[] CreateHuffmanTrees()
@ -107,7 +107,7 @@ namespace ImageSharp.Formats.Jpg
} }
/// <summary> /// <summary>
/// Initializes the Huffman tree /// Initializes the Huffman tree
/// </summary> /// </summary>
private void Init() private void Init()
{ {
@ -119,7 +119,7 @@ namespace ImageSharp.Formats.Jpg
} }
/// <summary> /// <summary>
/// Disposes the underlying buffers /// Disposes the underlying buffers
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
@ -131,7 +131,7 @@ namespace ImageSharp.Formats.Jpg
} }
/// <summary> /// <summary>
/// Internal part of the DHT processor, whatever does it mean /// Internal part of the DHT processor, whatever does it mean
/// </summary> /// </summary>
/// <param name="decoder">The decoder instance</param> /// <param name="decoder">The decoder instance</param>
/// <param name="defineHuffmanTablesData">The temporal buffer that holds the data that has been read from the Jpeg stream</param> /// <param name="defineHuffmanTablesData">The temporal buffer that holds the data that has been read from the Jpeg stream</param>

24
src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs

@ -12,12 +12,12 @@ namespace ImageSharp.Formats.Jpg
internal unsafe struct JpegScanDecoder internal unsafe struct JpegScanDecoder
{ {
/// <summary> /// <summary>
/// The AC table index /// The AC table index
/// </summary> /// </summary>
internal const int AcTableIndex = 1; internal const int AcTableIndex = 1;
/// <summary> /// <summary>
/// The DC table index /// The DC table index
/// </summary> /// </summary>
internal const int DcTableIndex = 0; internal const int DcTableIndex = 0;
@ -37,9 +37,9 @@ namespace ImageSharp.Formats.Jpg
public UnzigData Unzig; 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() public static ComponentData Create()
{ {
@ -133,7 +133,7 @@ namespace ImageSharp.Formats.Jpg
private ComponentData Data; private ComponentData Data;
private ComponentPointers Pointers; private ComponentPointers Pointers;
public static void Init(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining) public static void Init(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining)
{ {
p->Data = ComponentData.Create(); p->Data = ComponentData.Create();
@ -387,7 +387,7 @@ namespace ImageSharp.Formats.Jpg
// Reset the DC components, as per section F.2.1.3.1. // Reset the DC components, as per section F.2.1.3.1.
this.ResetDc(); this.ResetDc();
// Reset the progressive decoder state, as per section G.1.2.2. // Reset the progressive decoder state, as per section G.1.2.2.
decoder.EobRun = 0; decoder.EobRun = 0;
} }
@ -398,7 +398,7 @@ namespace ImageSharp.Formats.Jpg
} }
/// <summary> /// <summary>
/// Decodes a successive approximation refinement block, as specified in section G.1.2. /// Decodes a successive approximation refinement block, as specified in section G.1.2.
/// </summary> /// </summary>
/// <param name="h">The Huffman tree</param> /// <param name="h">The Huffman tree</param>
/// <param name="delta">The low transform offset</param> /// <param name="delta">The low transform offset</param>
@ -473,7 +473,7 @@ namespace ImageSharp.Formats.Jpg
{ {
break; break;
} }
zig = this.RefineNonZeroes(decoder, zig, val0, delta); zig = this.RefineNonZeroes(decoder, zig, val0, delta);
if (zig > this.zigEnd) if (zig > this.zigEnd)
{ {
@ -491,13 +491,13 @@ namespace ImageSharp.Formats.Jpg
if (decoder.EobRun > 0) if (decoder.EobRun > 0)
{ {
decoder.EobRun--; decoder.EobRun--;
this.RefineNonZeroes(decoder, zig,-1, delta); this.RefineNonZeroes(decoder, zig, -1, delta);
} }
} }
/// <summary> /// <summary>
/// Refines non-zero entries of b in zig-zag order. /// Refines non-zero entries of b in zig-zag order.
/// If <paramref name="nz" /> >= 0, the first <paramref name="nz" /> zero entries are skipped over. /// If <paramref name="nz" /> >= 0, the first <paramref name="nz" /> zero entries are skipped over.
/// </summary> /// </summary>
/// <param name="decoder">The decoder</param> /// <param name="decoder">The decoder</param>
/// <param name="zig">The zig-zag start index</param> /// <param name="zig">The zig-zag start index</param>
@ -649,5 +649,5 @@ namespace ImageSharp.Formats.Jpg
destArea.LoadColorsFrom(this.Pointers.Temp1, this.Pointers.Temp2); destArea.LoadColorsFrom(this.Pointers.Temp1, this.Pointers.Temp2);
} }
} }
} }

8
src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs

@ -3,23 +3,23 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
/// <summary> /// <summary>
/// Represents a component scan /// Represents a component scan
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct Scan internal struct Scan
{ {
/// <summary> /// <summary>
/// Gets or sets the component index. /// Gets or sets the component index.
/// </summary> /// </summary>
public byte Index; public byte Index;
/// <summary> /// <summary>
/// Gets or sets the DC table selector /// Gets or sets the DC table selector
/// </summary> /// </summary>
public byte DcTableSelector; public byte DcTableSelector;
/// <summary> /// <summary>
/// Gets or sets the AC table selector /// Gets or sets the AC table selector
/// </summary> /// </summary>
public byte AcTableSelector; public byte AcTableSelector;
} }

452
src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs

@ -7,139 +7,82 @@ 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;
/// <summary> /// <summary>
/// Performs the jpeg decoding operation. /// Performs the jpeg decoding operation.
/// </summary> /// </summary>
internal unsafe class JpegDecoderCore : IDisposable internal unsafe class JpegDecoderCore : IDisposable
{ {
/// <summary> /// <summary>
/// The maximum number of color components /// The maximum number of color components
/// </summary>
internal 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> /// </summary>
internal Block8x8F[][] ProgCoeffs { get; } public const int MaxComponents = 4;
/// <summary> /// <summary>
/// Quantization tables, in zigzag order. /// The App14 marker color-space
/// </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>
/// The App14 marker color-space
/// </summary> /// </summary>
private byte adobeTransform; private byte adobeTransform;
/// <summary> /// <summary>
/// Whether the image is in CMYK format with an App14 marker /// Whether the image is in CMYK format with an App14 marker
/// </summary> /// </summary>
private bool adobeTransformValid; private bool adobeTransformValid;
/// <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.
/// </summary> /// </summary>
private JpegPixelArea grayImage; private JpegPixelArea grayImage;
/// <summary> /// <summary>
/// The horizontal resolution. Calculated if the image has a JFIF header. /// The horizontal resolution. Calculated if the image has a JFIF header.
/// </summary> /// </summary>
private short horizontalResolution; private short horizontalResolution;
/// <summary>
/// The image height
/// </summary>
internal int ImageHeight { get; private set; }
/// <summary>
/// The image width
/// </summary>
internal int ImageWidth { get; private set; }
/// <summary> /// <summary>
/// The byte buffer. /// The maximum number of quantization tables
/// </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>
private short verticalResolution; private short verticalResolution;
/// <summary> /// <summary>
/// The full color image to decode to. /// The full color image to decode to.
/// </summary> /// </summary>
private YCbCrImage ycbcrImage; private YCbCrImage ycbcrImage;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegDecoderCore" /> class. /// Initializes a new instance of the <see cref="JpegDecoderCore" /> class.
/// </summary> /// </summary>
public JpegDecoderCore() public JpegDecoderCore()
{ {
@ -149,56 +92,83 @@ 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>
/// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent) /// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent)
/// It's better tho have an error code for this! /// It's better tho have an error code for this!
/// </summary> /// </summary>
internal enum ErrorCodes internal enum ErrorCodes
{ {
/// <summary> /// <summary>
/// NoError /// NoError
/// </summary> /// </summary>
NoError, NoError,
/// <summary> /// <summary>
/// MissingFF00 /// MissingFF00
/// </summary> /// </summary>
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> /// <summary>
/// Gets the input stream. /// Saved state between progressive-mode scans.
/// </summary> /// </summary>
public Stream InputStream public Block8x8F[][] ProgCoeffs { get; }
{
get /// <summary>
{ /// Quantization tables, in zigzag order.
return this.inputStream; /// </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>
/// Gets the input stream.
/// </summary>
public Stream InputStream { get; private set; }
/// <summary>
/// Whether the image is interlaced (progressive)
/// </summary>
public bool IsProgressive { get; private set; }
/// <summary> /// <summary>
/// Decodes the image from the specified this._stream and sets /// The restart interval
/// the data to image. /// </summary>
public int RestartInterval { get; private set; }
/// <summary>
/// Decodes the image from the specified this._stream and sets
/// the data to image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image, where the data should be set to.</param> /// <param name="image">The image, where the data should be set to.</param>
@ -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);
@ -417,7 +387,7 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Dispose /// Dispose
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
@ -427,23 +397,23 @@ 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();
} }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
/// <returns>The <see cref="byte" /></returns> /// <returns>The <see cref="byte" /></returns>
[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>
/// 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.
/// </summary> /// </summary>
/// <param name="data">The data to write to.</param> /// <param name="data">The data to write to.</param>
/// <param name="offset">The offset in the source buffer</param> /// <param name="offset">The offset in the source buffer</param>
@ -451,39 +421,39 @@ 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);
} }
} }
} }
/// <summary> /// <summary>
/// Optimized method to pack bytes to the image from the YCbCr color space. /// Optimized method to pack bytes to the image from the YCbCr color space.
/// This is faster than implicit casting as it avoids double packing. /// This is faster than implicit casting as it avoids double packing.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="packed">The packed pixel.</param> /// <param name="packed">The packed pixel.</param>
@ -505,7 +475,7 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image to assign the resolution to.</param> /// <param name="image">The image to assign the resolution to.</param>
@ -520,7 +490,7 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Converts the image from the original CMYK image pixels. /// Converts the image from the original CMYK image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param> /// <param name="width">The image width.</param>
@ -539,28 +509,28 @@ namespace ImageSharp.Formats
0, 0,
height, height,
y => 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); byte cyan = this.ycbcrImage.YPixels[yo + x];
int co = this.ycbcrImage.GetRowCOffset(y); byte magenta = this.ycbcrImage.CbPixels[co + (x / scale)];
byte yellow = this.ycbcrImage.CrPixels[co + (x / scale)];
for (int x = 0; x < width; x++)
{ TColor packed = default(TColor);
byte cyan = this.ycbcrImage.YPixels[yo + x]; this.PackCmyk<TColor>(ref packed, cyan, magenta, yellow, x, y);
byte magenta = this.ycbcrImage.CbPixels[co + (x / scale)]; pixels[x, y] = packed;
byte yellow = this.ycbcrImage.CrPixels[co + (x / scale)]; }
});
TColor packed = default(TColor);
this.PackCmyk<TColor>(ref packed, cyan, magenta, yellow, x, y);
pixels[x, y] = packed;
}
});
} }
this.AssignResolution(image); this.AssignResolution(image);
} }
/// <summary> /// <summary>
/// Converts the image from the original grayscale image pixels. /// Converts the image from the original grayscale image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param> /// <param name="width">The image width.</param>
@ -578,24 +548,24 @@ namespace ImageSharp.Formats
height, height,
Bootstrapper.ParallelOptions, Bootstrapper.ParallelOptions,
y => y =>
{
int yoff = this.grayImage.GetRowOffset(y);
for (int x = 0; x < width; x++)
{ {
int yoff = this.grayImage.GetRowOffset(y); byte rgb = this.grayImage.Pixels[yoff + x];
for (int x = 0; x < width; x++)
{ TColor packed = default(TColor);
byte rgb = this.grayImage.Pixels[yoff + x]; packed.PackFromBytes(rgb, rgb, rgb, 255);
pixels[x, y] = packed;
TColor packed = default(TColor); }
packed.PackFromBytes(rgb, rgb, rgb, 255); });
pixels[x, y] = packed;
}
});
} }
this.AssignResolution(image); this.AssignResolution(image);
} }
/// <summary> /// <summary>
/// Converts the image from the original RBG image pixels. /// Converts the image from the original RBG image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param> /// <param name="width">The image width.</param>
@ -614,28 +584,28 @@ namespace ImageSharp.Formats
height, height,
Bootstrapper.ParallelOptions, Bootstrapper.ParallelOptions,
y => 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); byte red = this.ycbcrImage.YPixels[yo + x];
int co = this.ycbcrImage.GetRowCOffset(y); byte green = this.ycbcrImage.CbPixels[co + (x / scale)];
byte blue = this.ycbcrImage.CrPixels[co + (x / scale)];
for (int x = 0; x < width; x++)
{ TColor packed = default(TColor);
byte red = this.ycbcrImage.YPixels[yo + x]; packed.PackFromBytes(red, green, blue, 255);
byte green = this.ycbcrImage.CbPixels[co + (x / scale)]; pixels[x, y] = packed;
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); this.AssignResolution(image);
} }
/// <summary> /// <summary>
/// Converts the image from the original YCbCr image pixels. /// Converts the image from the original YCbCr image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param> /// <param name="width">The image width.</param>
@ -654,28 +624,28 @@ namespace ImageSharp.Formats
height, height,
Bootstrapper.ParallelOptions, Bootstrapper.ParallelOptions,
y => 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); byte yy = this.ycbcrImage.YPixels[yo + x];
int co = this.ycbcrImage.GetRowCOffset(y); byte cb = this.ycbcrImage.CbPixels[co + (x / scale)];
byte cr = this.ycbcrImage.CrPixels[co + (x / scale)];
for (int x = 0; x < width; x++)
{ TColor packed = default(TColor);
byte yy = this.ycbcrImage.YPixels[yo + x]; PackYcbCr<TColor>(ref packed, yy, cb, cr);
byte cb = this.ycbcrImage.CbPixels[co + (x / scale)]; pixels[x, y] = packed;
byte cr = this.ycbcrImage.CrPixels[co + (x / scale)]; }
});
TColor packed = default(TColor);
PackYcbCr<TColor>(ref packed, yy, cb, cr);
pixels[x, y] = packed;
}
});
} }
this.AssignResolution(image); this.AssignResolution(image);
} }
/// <summary> /// <summary>
/// Converts the image from the original YCCK image pixels. /// Converts the image from the original YCCK image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param> /// <param name="width">The image width.</param>
@ -694,28 +664,28 @@ namespace ImageSharp.Formats
0, 0,
height, height,
y => 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); byte yy = this.ycbcrImage.YPixels[yo + x];
int co = this.ycbcrImage.GetRowCOffset(y); byte cb = this.ycbcrImage.CbPixels[co + (x / scale)];
byte cr = this.ycbcrImage.CrPixels[co + (x / scale)];
for (int x = 0; x < width; x++)
{ TColor packed = default(TColor);
byte yy = this.ycbcrImage.YPixels[yo + x]; this.PackYcck<TColor>(ref packed, yy, cb, cr, x, y);
byte cb = this.ycbcrImage.CbPixels[co + (x / scale)]; pixels[x, y] = packed;
byte cr = this.ycbcrImage.CrPixels[co + (x / scale)]; }
});
TColor packed = default(TColor);
this.PackYcck<TColor>(ref packed, yy, cb, cr, x, y);
pixels[x, y] = packed;
}
});
} }
this.AssignResolution(image); this.AssignResolution(image);
} }
/// <summary> /// <summary>
/// Decodes a single bit /// Decodes a single bit
/// </summary> /// </summary>
/// <returns>The <see cref="bool" /></returns> /// <returns>The <see cref="bool" /></returns>
internal bool DecodeBit() internal bool DecodeBit()
@ -736,7 +706,7 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Decodes the given number of bits /// Decodes the given number of bits
/// </summary> /// </summary>
/// <param name="count">The number of bits to decode.</param> /// <param name="count">The number of bits to decode.</param>
/// <returns>The <see cref="uint" /></returns> /// <returns>The <see cref="uint" /></returns>
@ -759,7 +729,7 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
/// <param name="huffmanTree">The huffman value</param> /// <param name="huffmanTree">The huffman value</param>
/// <returns>The <see cref="byte" /></returns> /// <returns>The <see cref="byte" /></returns>
@ -850,10 +820,10 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Returns a value indicating whether the image in an RGB image. /// Returns a value indicating whether the image in an RGB image.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="bool" />. /// The <see cref="bool" />.
/// </returns> /// </returns>
private bool IsRGB() private bool IsRGB()
{ {
@ -874,7 +844,7 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Makes the image from the buffer. /// Makes the image from the buffer.
/// </summary> /// </summary>
/// <param name="mxx">The horizontal MCU count</param> /// <param name="mxx">The horizontal MCU count</param>
/// <param name="myy">The vertical MCU count</param> /// <param name="myy">The vertical MCU count</param>
@ -932,8 +902,8 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Optimized method to pack bytes to the image from the CMYK color space. /// Optimized method to pack bytes to the image from the CMYK color space.
/// This is faster than implicit casting as it avoids double packing. /// This is faster than implicit casting as it avoids double packing.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="packed">The packed pixel.</param> /// <param name="packed">The packed pixel.</param>
@ -957,8 +927,8 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Optimized method to pack bytes to the image from the YCCK color space. /// Optimized method to pack bytes to the image from the YCCK color space.
/// This is faster than implicit casting as it avoids double packing. /// This is faster than implicit casting as it avoids double packing.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="packed">The packed pixel.</param> /// <param name="packed">The packed pixel.</param>
@ -995,9 +965,9 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Processes the "Adobe" APP14 segment stores image encoding information for DCT filters. /// 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 /// 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. /// deleted by default when deleting all metadata because it may affect the appearance of the image.
/// </summary> /// </summary>
/// <param name="remaining">The remaining number of bytes in the stream.</param> /// <param name="remaining">The remaining number of bytes in the stream.</param>
private void ProcessApp14Marker(int remaining) private void ProcessApp14Marker(int remaining)
@ -1025,7 +995,7 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Processes the App1 marker retrieving any stored metadata /// Processes the App1 marker retrieving any stored metadata
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
@ -1050,7 +1020,7 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Processes the application header containing the JFIF identifier plus extra data. /// Processes the application header containing the JFIF identifier plus extra data.
/// </summary> /// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessApplicationHeader(int remaining) private void ProcessApplicationHeader(int remaining)
@ -1081,8 +1051,8 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Processes a Define Huffman Table marker, and initializes a huffman /// Processes a Define Huffman Table marker, and initializes a huffman
/// struct from its contents. Specified in section B.2.4.2. /// struct from its contents. Specified in section B.2.4.2.
/// </summary> /// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessDefineHuffmanTablesMarker(int remaining) private void ProcessDefineHuffmanTablesMarker(int remaining)
@ -1114,8 +1084,8 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in
/// macroblocks /// macroblocks
/// </summary> /// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessDefineRestartIntervalMarker(int remaining) private void ProcessDefineRestartIntervalMarker(int remaining)
@ -1130,11 +1100,11 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
/// <exception cref="ImageFormatException"> /// <exception cref="ImageFormatException">
/// Thrown if the tables do not match the header /// Thrown if the tables do not match the header
/// </exception> /// </exception>
private void ProcessDqt(int remaining) private void ProcessDqt(int remaining)
{ {
@ -1201,7 +1171,7 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Processes the Start of Frame marker. Specified in section B.2.2. /// Processes the Start of Frame marker. Specified in section B.2.2.
/// </summary> /// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessStartOfFrameMarker(int remaining) private void ProcessStartOfFrameMarker(int remaining)
@ -1409,52 +1379,52 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// Skips the next n bytes. /// Skips the next n bytes.
/// </summary> /// </summary>
/// <param name="count">The number of bytes to ignore.</param> /// <param name="count">The number of bytes to ignore.</param>
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);
} }
} }
/// <summary> /// <summary>
/// Undoes the most recent ReadByteStuffedByte call, /// Undoes the most recent ReadByteStuffedByte call,
/// giving a byte of data back from bits to bytes. The Huffman look-up table /// 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 /// 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 /// 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. /// can happen when expecting to read a 0xff 0x00 byte-stuffed byte.
/// </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;
@ -1464,22 +1434,22 @@ namespace ImageSharp.Formats
} }
/// <summary> /// <summary>
/// The EOF (End of File exception). /// The EOF (End of File exception).
/// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker
/// </summary> /// </summary>
internal class EOFException : Exception internal class EOFException : Exception
{ {
} }
/// <summary> /// <summary>
/// The missing ff00 exception. /// The missing ff00 exception.
/// </summary> /// </summary>
internal class MissingFF00Exception : Exception internal class MissingFF00Exception : Exception
{ {
} }
/// <summary> /// <summary>
/// The short huffman data exception. /// The short huffman data exception.
/// </summary> /// </summary>
private class ShortHuffmanDataException : Exception private class ShortHuffmanDataException : Exception
{ {

Loading…
Cancel
Save