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
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Tests46", "tests\ImageSharp.Tests46\ImageSharp.Tests46.csproj", "{88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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;
/// <summary>
/// Represents a Huffman tree
/// Represents a Huffman tree
/// </summary>
internal struct HuffmanTree : IDisposable
{
/// <summary>
/// The maximum (inclusive) number of codes in a Huffman tree.
/// The maximum (inclusive) number of codes in a Huffman tree.
/// </summary>
public const int MaxNCodes = 256;
/// <summary>
/// The maximum (inclusive) number of bits in a Huffman code.
/// The maximum (inclusive) number of bits in a Huffman code.
/// </summary>
public const int MaxCodeLength = 16;
/// <summary>
/// The maximum number of Huffman table classes
/// The maximum number of Huffman table classes
/// </summary>
public const int MaxTc = 1;
/// <summary>
/// The maximum number of Huffman table identifiers
/// The maximum number of Huffman table identifiers
/// </summary>
public const int MaxTh = 3;
/// <summary>
/// Row size of the Huffman table
/// Row size of the Huffman table
/// </summary>
public const int ThRowSize = MaxTh + 1;
/// <summary>
/// Number of Hufman Trees in the Huffman table
/// Number of Hufman Trees in the Huffman table
/// </summary>
public const int NumberOfTrees = (MaxTc + 1) * (MaxTh + 1);
/// <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>
public const int LutSize = 8;
/// <summary>
/// Gets or sets the number of codes in the tree.
/// Gets or sets the number of codes in the tree.
/// </summary>
public int Length;
/// <summary>
/// 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
/// are 1 plus the code length, or 0 if the value is too large to fit in
/// lutSize bits.
/// 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
/// are 1 plus the code length, or 0 if the value is too large to fit in
/// lutSize bits.
/// </summary>
public ushort[] Lut;
/// <summary>
/// Gets the the decoded values, sorted by their encoding.
/// Gets the the decoded values, sorted by their encoding.
/// </summary>
public byte[] Values;
/// <summary>
/// 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.
/// 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.
/// </summary>
public int[] MinCodes;
/// <summary>
/// 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.
/// 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.
/// </summary>
public int[] MaxCodes;
/// <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>
public int[] Indices;
@ -89,7 +89,7 @@ namespace ImageSharp.Formats.Jpg
private static readonly ArrayPool<int> IntBuffer = ArrayPool<int>.Create(MaxCodeLength, 50);
/// <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>
/// <returns>An array of <see cref="HuffmanTree" /> instances representing the Huffman tables</returns>
public static HuffmanTree[] CreateHuffmanTrees()
@ -107,7 +107,7 @@ namespace ImageSharp.Formats.Jpg
}
/// <summary>
/// Initializes the Huffman tree
/// Initializes the Huffman tree
/// </summary>
private void Init()
{
@ -119,7 +119,7 @@ namespace ImageSharp.Formats.Jpg
}
/// <summary>
/// Disposes the underlying buffers
/// Disposes the underlying buffers
/// </summary>
public void Dispose()
{
@ -131,7 +131,7 @@ namespace ImageSharp.Formats.Jpg
}
/// <summary>
/// Internal part of the DHT processor, whatever does it mean
/// Internal part of the DHT processor, whatever does it mean
/// </summary>
/// <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>

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

@ -12,12 +12,12 @@ namespace ImageSharp.Formats.Jpg
internal unsafe struct JpegScanDecoder
{
/// <summary>
/// The AC table index
/// The AC table index
/// </summary>
internal const int AcTableIndex = 1;
/// <summary>
/// The DC table index
/// The DC table index
/// </summary>
internal const int DcTableIndex = 0;
@ -37,9 +37,9 @@ namespace ImageSharp.Formats.Jpg
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()
{
@ -133,7 +133,7 @@ namespace ImageSharp.Formats.Jpg
private ComponentData Data;
private ComponentPointers Pointers;
public static void Init(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining)
{
p->Data = ComponentData.Create();
@ -387,7 +387,7 @@ namespace ImageSharp.Formats.Jpg
// Reset the DC components, as per section F.2.1.3.1.
this.ResetDc();
// Reset the progressive decoder state, as per section G.1.2.2.
decoder.EobRun = 0;
}
@ -398,7 +398,7 @@ namespace ImageSharp.Formats.Jpg
}
/// <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>
/// <param name="h">The Huffman tree</param>
/// <param name="delta">The low transform offset</param>
@ -473,7 +473,7 @@ namespace ImageSharp.Formats.Jpg
{
break;
}
zig = this.RefineNonZeroes(decoder, zig, val0, delta);
if (zig > this.zigEnd)
{
@ -491,13 +491,13 @@ namespace ImageSharp.Formats.Jpg
if (decoder.EobRun > 0)
{
decoder.EobRun--;
this.RefineNonZeroes(decoder, zig,-1, delta);
this.RefineNonZeroes(decoder, zig, -1, delta);
}
}
/// <summary>
/// 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.
/// 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.
/// </summary>
/// <param name="decoder">The decoder</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);
}
}
}

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

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

452
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;
/// <summary>
/// Performs the jpeg decoding operation.
/// Performs the jpeg decoding operation.
/// </summary>
internal unsafe class JpegDecoderCore : IDisposable
{
/// <summary>
/// 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.
/// The maximum number of color components
/// </summary>
internal Block8x8F[][] ProgCoeffs { get; }
public const int MaxComponents = 4;
/// <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>
/// The App14 marker color-space
/// The App14 marker color-space
/// </summary>
private byte adobeTransform;
/// <summary>
/// Whether the image is in CMYK format with an App14 marker
/// Whether the image is in CMYK format with an App14 marker
/// </summary>
private bool adobeTransformValid;
/// <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>
internal Bits Bits;
private JpegPixelArea blackImage;
//private int blockIndex;
public Bits Bits;
/// <summary>
/// The byte buffer.
/// The byte buffer.
/// </summary>
private Bytes bytes;
public Bytes Bytes;
/// <summary>
/// The number of color components within the image.
/// End-of-Band run, specified in section G.1.2.2.
/// </summary>
internal int ComponentCount { get; private set; }
public ushort EobRun;
/// <summary>
/// End-of-Band run, specified in section G.1.2.2.
/// The black image to decode to.
/// </summary>
internal ushort EobRun;
private JpegPixelArea blackImage;
/// <summary>
/// A grayscale image to decode to.
/// A grayscale image to decode to.
/// </summary>
private JpegPixelArea grayImage;
/// <summary>
/// The horizontal resolution. Calculated if the image has a JFIF header.
/// The horizontal resolution. Calculated if the image has a JFIF header.
/// </summary>
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>
/// The byte buffer.
/// The maximum number of quantization tables
/// </summary>
private Stream inputStream;
private const int MaxTq = 3;
/// <summary>
/// Whether the image has a JFIF header
/// Whether the image has a JFIF header
/// </summary>
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>
/// The vertical resolution. Calculated if the image has a JFIF header.
/// The vertical resolution. Calculated if the image has a JFIF header.
/// </summary>
private short verticalResolution;
/// <summary>
/// The full color image to decode to.
/// The full color image to decode to.
/// </summary>
private YCbCrImage ycbcrImage;
/// <summary>
/// Initializes a new instance of the <see cref="JpegDecoderCore" /> class.
/// Initializes a new instance of the <see cref="JpegDecoderCore" /> class.
/// </summary>
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();
}
/// <summary>
/// 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!
/// </summary>
internal enum ErrorCodes
{
/// <summary>
/// NoError
/// NoError
/// </summary>
NoError,
/// <summary>
/// MissingFF00
/// MissingFF00
/// </summary>
MissingFF00
}
/// <summary>
/// Gets or sets the byte buffer.
/// The component array
/// </summary>
public Bytes Bytes
{
get
{
return this.bytes;
}
public Component[] ComponentArray { get; }
set
{
this.bytes = value;
}
}
/// <summary>
/// The huffman trees
/// </summary>
public HuffmanTree[] HuffmanTrees { get; }
/// <summary>
/// Gets the input stream.
/// Saved state between progressive-mode scans.
/// </summary>
public Stream InputStream
{
get
{
return this.inputStream;
}
}
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>
/// 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>
/// Decodes the image from the specified this._stream and sets
/// the data to image.
/// The restart interval
/// </summary>
public int RestartInterval { get; private set; }
/// <summary>
/// Decodes the image from the specified this._stream and sets
/// the data to image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <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)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
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
}
/// <summary>
/// Dispose
/// Dispose
/// </summary>
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();
}
/// <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>
/// <returns>The <see cref="byte" /></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByte()
{
return this.bytes.ReadByte(this.inputStream);
return this.Bytes.ReadByte(this.InputStream);
}
/// <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>
/// <param name="data">The data to write to.</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)
{
// 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);
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="packed">The packed pixel.</param>
@ -505,7 +475,7 @@ namespace ImageSharp.Formats
}
/// <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>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image to assign the resolution to.</param>
@ -520,7 +490,7 @@ namespace ImageSharp.Formats
}
/// <summary>
/// Converts the image from the original CMYK image pixels.
/// Converts the image from the original CMYK image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
@ -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<TColor>(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<TColor>(ref packed, cyan, magenta, yellow, x, y);
pixels[x, y] = packed;
}
});
}
this.AssignResolution(image);
}
/// <summary>
/// Converts the image from the original grayscale image pixels.
/// Converts the image from the original grayscale image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
@ -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);
}
/// <summary>
/// Converts the image from the original RBG image pixels.
/// Converts the image from the original RBG image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
@ -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);
}
/// <summary>
/// Converts the image from the original YCbCr image pixels.
/// Converts the image from the original YCbCr image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
@ -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<TColor>(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<TColor>(ref packed, yy, cb, cr);
pixels[x, y] = packed;
}
});
}
this.AssignResolution(image);
}
/// <summary>
/// Converts the image from the original YCCK image pixels.
/// Converts the image from the original YCCK image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
@ -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<TColor>(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<TColor>(ref packed, yy, cb, cr, x, y);
pixels[x, y] = packed;
}
});
}
this.AssignResolution(image);
}
/// <summary>
/// Decodes a single bit
/// Decodes a single bit
/// </summary>
/// <returns>The <see cref="bool" /></returns>
internal bool DecodeBit()
@ -736,7 +706,7 @@ namespace ImageSharp.Formats
}
/// <summary>
/// Decodes the given number of bits
/// Decodes the given number of bits
/// </summary>
/// <param name="count">The number of bits to decode.</param>
/// <returns>The <see cref="uint" /></returns>
@ -759,7 +729,7 @@ namespace ImageSharp.Formats
}
/// <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>
/// <param name="huffmanTree">The huffman value</param>
/// <returns>The <see cref="byte" /></returns>
@ -850,10 +820,10 @@ namespace ImageSharp.Formats
}
/// <summary>
/// Returns a value indicating whether the image in an RGB image.
/// Returns a value indicating whether the image in an RGB image.
/// </summary>
/// <returns>
/// The <see cref="bool" />.
/// The <see cref="bool" />.
/// </returns>
private bool IsRGB()
{
@ -874,7 +844,7 @@ namespace ImageSharp.Formats
}
/// <summary>
/// Makes the image from the buffer.
/// Makes the image from the buffer.
/// </summary>
/// <param name="mxx">The horizontal MCU count</param>
/// <param name="myy">The vertical MCU count</param>
@ -932,8 +902,8 @@ namespace ImageSharp.Formats
}
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="packed">The packed pixel.</param>
@ -957,8 +927,8 @@ namespace ImageSharp.Formats
}
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="packed">The packed pixel.</param>
@ -995,9 +965,9 @@ namespace ImageSharp.Formats
}
/// <summary>
/// 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.
/// </summary>
/// <param name="remaining">The remaining number of bytes in the stream.</param>
private void ProcessApp14Marker(int remaining)
@ -1025,7 +995,7 @@ namespace ImageSharp.Formats
}
/// <summary>
/// Processes the App1 marker retrieving any stored metadata
/// Processes the App1 marker retrieving any stored metadata
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="remaining">The remaining bytes in the segment block.</param>
@ -1050,7 +1020,7 @@ namespace ImageSharp.Formats
}
/// <summary>
/// Processes the application header containing the JFIF identifier plus extra data.
/// Processes the application header containing the JFIF identifier plus extra data.
/// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessApplicationHeader(int remaining)
@ -1081,8 +1051,8 @@ namespace ImageSharp.Formats
}
/// <summary>
/// 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.
/// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessDefineHuffmanTablesMarker(int remaining)
@ -1114,8 +1084,8 @@ namespace ImageSharp.Formats
}
/// <summary>
/// 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
/// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessDefineRestartIntervalMarker(int remaining)
@ -1130,11 +1100,11 @@ namespace ImageSharp.Formats
}
/// <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>
/// <param name="remaining">The remaining bytes in the segment block.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the tables do not match the header
/// Thrown if the tables do not match the header
/// </exception>
private void ProcessDqt(int remaining)
{
@ -1201,7 +1171,7 @@ namespace ImageSharp.Formats
}
/// <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>
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessStartOfFrameMarker(int remaining)
@ -1409,52 +1379,52 @@ namespace ImageSharp.Formats
}
/// <summary>
/// Skips the next n bytes.
/// Skips the next n bytes.
/// </summary>
/// <param name="count">The number of bytes to ignore.</param>
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);
}
}
/// <summary>
/// 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.
/// </summary>
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
}
/// <summary>
/// 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
/// </summary>
internal class EOFException : Exception
{
}
/// <summary>
/// The missing ff00 exception.
/// The missing ff00 exception.
/// </summary>
internal class MissingFF00Exception : Exception
{
}
/// <summary>
/// The short huffman data exception.
/// The short huffman data exception.
/// </summary>
private class ShortHuffmanDataException : Exception
{

Loading…
Cancel
Save