Browse Source

cleanup & docs

pull/62/head
antonfirsov 10 years ago
parent
commit
83b4e56810
  1. 109
      src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs
  2. 124
      src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs
  3. 134
      src/ImageSharp/Formats/Jpg/Components/Decoder/YCbCrImage.cs
  4. 2093
      src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
  5. 19
      src/ImageSharp/Formats/Jpg/Utils/CleanPooler.cs

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

@ -8,69 +8,77 @@ namespace ImageSharp.Formats.Jpg
using System.Buffers;
/// <summary>
/// Represents a Huffman tree
/// Represents a Huffman tree
/// </summary>
internal struct HuffmanTree : IDisposable
{
public static HuffmanTree[] CreateHuffmanTrees()
{
HuffmanTree[] result = new HuffmanTree[(MaxTc + 1) * (MaxTh + 1)];
for (int i = 0; i < MaxTc + 1; i++)
{
for (int j = 0; j < MaxTh + 1; j++)
{
result[(i * ThRowSize) + j].Init();
}
}
return result;
}
/// <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.
/// </summary>
public const int MaxCodeLength = 16;
/// <summary>
/// The maximum number of Huffman table classes
/// </summary>
public const int MaxTc = 1;
/// <summary>
/// The maximum number of Huffman table identifiers
/// </summary>
public const int MaxTh = 3;
/// <summary>
/// Row size of the Huffman table
/// </summary>
internal const int MaxNCodes = 256;
public const int ThRowSize = MaxTh + 1;
/// <summary>
/// The maximum (inclusive) number of bits in a Huffman code.
/// Number of Hufman Trees in the Huffman table
/// </summary>
internal const int MaxCodeLength = 16;
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>
internal const int LutSize = 8;
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;
@ -81,7 +89,25 @@ namespace ImageSharp.Formats.Jpg
private static readonly ArrayPool<int> IntBuffer = ArrayPool<int>.Create(MaxCodeLength, 50);
/// <summary>
/// Initializes the Huffman tree
/// 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 table</returns>
public static HuffmanTree[] CreateHuffmanTrees()
{
HuffmanTree[] result = new HuffmanTree[NumberOfTrees];
for (int i = 0; i < MaxTc + 1; i++)
{
for (int j = 0; j < MaxTh + 1; j++)
{
result[(i * ThRowSize) + j].Init();
}
}
return result;
}
/// <summary>
/// Initializes the Huffman tree
/// </summary>
public void Init()
{
@ -93,7 +119,7 @@ namespace ImageSharp.Formats.Jpg
}
/// <summary>
/// Disposes the underlying buffers
/// Disposes the underlying buffers
/// </summary>
public void Dispose()
{
@ -105,12 +131,15 @@ 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 stream</param>
/// <param name="remaining">Remaining bits</param>
internal void ProcessDefineHuffmanTablesMarkerLoop(JpegDecoderCore decoder, byte[] defineHuffmanTablesData, ref int remaining)
internal void ProcessDefineHuffmanTablesMarkerLoop(
JpegDecoderCore decoder,
byte[] defineHuffmanTablesData,
ref int remaining)
{
// Read nCodes and huffman.Valuess (and derive h.Length).
// nCodes[i] is the number of codes with code length i.
@ -197,17 +226,5 @@ namespace ImageSharp.Formats.Jpg
c <<= 1;
}
}
/// <summary>
/// The maximum number of Huffman table classes
/// </summary>
internal const int MaxTc = 1;
/// <summary>
/// The maximum number of Huffman table identifiers
/// </summary>
internal const int MaxTh = 3;
internal const int ThRowSize = MaxTh + 1;
}
}

124
src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs

@ -1,89 +1,127 @@
// <copyright file="JpegChannelArea.cs" company="James Jackson-South">
// <copyright file="JpegPixelArea.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpg
{
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
/// <summary>
/// Represents a grayscale image
/// Represents an area of a Jpeg subimage (channel)
/// </summary>
internal struct JpegPixelArea
{
/// <summary>
/// Initializes a new instance of the <see cref="JpegPixelArea" /> class.
/// Initializes a new instance of the <see cref="JpegPixelArea" /> struct from existing data.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public static JpegPixelArea CreatePooled(int width, int height)
{
int size = width * height;
var pixels = CleanPooler<byte>.RentCleanArray(size);
return new JpegPixelArea(pixels, width, 0);
}
public JpegPixelArea(byte[] pixels, int widthOrStride, int offset)
/// <param name="pixels">The pixel array</param>
/// <param name="striede">The stride</param>
/// <param name="offset">The offset</param>
public JpegPixelArea(byte[] pixels, int striede, int offset)
{
this.Stride = widthOrStride;
this.Stride = striede;
this.Pixels = pixels;
this.Offset = offset;
}
public void ReturnPooled()
{
if (this.Pixels == null) return;
CleanPooler<byte>.ReturnArray(this.Pixels);
this.Pixels = null;
}
/// <summary>
/// Gets or sets the pixels.
/// Gets the pixels.
/// </summary>
public byte[] Pixels { get; private set; }
public bool Created => this.Pixels != null;
/// <summary>
/// Gets a value indicating whether the instance has been initalized. (Is not default(JpegPixelArea))
/// </summary>
public bool IsInitialized => this.Pixels != null;
/// <summary>
/// Gets or sets the width.
/// Gets or the stride.
/// </summary>
public int Stride { get; private set; }
public int Stride { get; }
/// <summary>
/// Gets or sets the offset
/// Gets or the offset.
/// </summary>
public int Offset { get; private set; }
public int Offset { get; }
/// <summary>
/// Get the subarea that belongs to the Block8x8 defined by block indices
/// Gets a <see cref="MutableSpan{T}" /> of bytes to the pixel area
/// </summary>
public MutableSpan<byte> Span => new MutableSpan<byte>(this.Pixels, this.Offset);
/// <summary>
/// Returns the pixel at (x, y)
/// </summary>
/// <param name="x">The x index</param>
/// <param name="y">The y index</param>
/// <returns>The pixel value</returns>
public byte this[int x, int y]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.Pixels[(y * this.Stride) + x];
}
}
/// <summary>
/// Creates a new instance of the <see cref="JpegPixelArea" /> struct.
/// Pixel array will be taken from a pool, this instance will be the owner of it's pixel data, therefore
/// <see cref="ReturnPooled" /> should be called when the instance is no longer needed.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns>A <see cref="JpegPixelArea" /> with pooled data</returns>
public static JpegPixelArea CreatePooled(int width, int height)
{
int size = width * height;
var pixels = CleanPooler<byte>.RentCleanArray(size);
return new JpegPixelArea(pixels, width, 0);
}
/// <summary>
/// Returns <see cref="Pixels" /> to the pool
/// </summary>
public void ReturnPooled()
{
if (this.Pixels == null)
{
return;
}
CleanPooler<byte>.ReturnArray(this.Pixels);
this.Pixels = null;
}
/// <summary>
/// Gets the subarea that belongs to the Block8x8 defined by block indices
/// </summary>
/// <param name="bx">The block X index</param>
/// <param name="by">The block Y index</param>
/// <returns></returns>
public JpegPixelArea GetOffsetedAreaForBlock(int bx, int by)
/// <returns>The subarea offseted by block indices</returns>
public JpegPixelArea GetOffsetedSubAreaForBlock(int bx, int by)
{
int offset = this.Offset + 8 * (by * this.Stride + bx);
int offset = this.Offset + (8 * ((by * this.Stride) + bx));
return new JpegPixelArea(this.Pixels, this.Stride, offset);
}
public byte this[int x, int y] => this.Pixels[y * this.Stride + x];
/// <summary>
/// Gets the row offset at the given position
/// Gets the row offset at the given position
/// </summary>
/// <param name="y">The y-coordinate of the image.</param>
/// <returns>The <see cref="int"/></returns>
/// <returns>The <see cref="int" /></returns>
public int GetRowOffset(int y)
{
return this.Offset + (y * this.Stride);
}
public MutableSpan<byte> Span => new MutableSpan<byte>(this.Pixels, this.Offset);
/// <summary>
/// Load values to the pixel area from the given <see cref="Block8x8F" />.
/// Level shift [-128.0, 128.0] floating point color values by +128, clip them to [0, 255], and convert them to
/// <see cref="byte" /> values
/// </summary>
/// <param name="block">The block holding the color values</param>
/// <param name="temp">Temporal block provided by the caller</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void LoadColorsFrom(Block8x8F* block, Block8x8F* temp)
{
@ -91,4 +129,4 @@ namespace ImageSharp.Formats.Jpg
block->CopyColorsTo(new MutableSpan<byte>(this.Pixels, this.Offset), this.Stride, temp);
}
}
}
}

134
src/ImageSharp/Formats/Jpg/Components/Decoder/YCbCrImage.cs

@ -2,24 +2,22 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpg
{
using System;
using System.Buffers;
/// <summary>
/// Represents an image made up of three color components (luminance, blue chroma, red chroma)
/// Represents an image made up of three color components (luminance, blue chroma, red chroma)
/// </summary>
internal class YCbCrImage : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="YCbCrImage"/> class.
/// Initializes a new instance of the <see cref="YCbCrImage" /> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="ratio">The ratio.</param>
public YCbCrImage(int width, int height, YCbCrSubsampleRatio ratio, int yOffset, int cOffset)
public YCbCrImage(int width, int height, YCbCrSubsampleRatio ratio)
{
int cw, ch;
YCbCrSize(width, height, ratio, out cw, out ch);
@ -27,134 +25,120 @@ namespace ImageSharp.Formats.Jpg
this.CbPixels = CleanPooler<byte>.RentCleanArray(cw * ch);
this.CrPixels = CleanPooler<byte>.RentCleanArray(cw * ch);
this.Ratio = ratio;
this.YOffset = yOffset;
this.COffset = cOffset;
this.YOffset = 0;
this.COffset = 0;
this.YStride = width;
this.CStride = cw;
this.X = 0;
this.Y = 0;
}
public JpegPixelArea YChannel => new JpegPixelArea(this.YPixels, this.YStride, this.YOffset);
public JpegPixelArea CbChannel => new JpegPixelArea(this.CbPixels, this.CStride, this.COffset);
public JpegPixelArea CrChannel => new JpegPixelArea(this.CrPixels, this.CStride, this.COffset);
/// <summary>
/// Prevents a default instance of the <see cref="YCbCrImage"/> class from being created.
/// </summary>
private YCbCrImage(int yOffset, int cOffset)
{
this.YOffset = yOffset;
this.COffset = cOffset;
}
/// <summary>
/// Provides enumeration of the various available subsample ratios.
/// Provides enumeration of the various available subsample ratios.
/// </summary>
public enum YCbCrSubsampleRatio
{
/// <summary>
/// YCbCrSubsampleRatio444
/// YCbCrSubsampleRatio444
/// </summary>
YCbCrSubsampleRatio444,
/// <summary>
/// YCbCrSubsampleRatio422
/// YCbCrSubsampleRatio422
/// </summary>
YCbCrSubsampleRatio422,
/// <summary>
/// YCbCrSubsampleRatio420
/// YCbCrSubsampleRatio420
/// </summary>
YCbCrSubsampleRatio420,
/// <summary>
/// YCbCrSubsampleRatio440
/// YCbCrSubsampleRatio440
/// </summary>
YCbCrSubsampleRatio440,
/// <summary>
/// YCbCrSubsampleRatio411
/// YCbCrSubsampleRatio411
/// </summary>
YCbCrSubsampleRatio411,
/// <summary>
/// YCbCrSubsampleRatio410
/// YCbCrSubsampleRatio410
/// </summary>
YCbCrSubsampleRatio410,
}
/// <summary>
/// Gets or sets the luminance components channel.
/// Gets an offseted <see cref="JpegPixelArea" /> to the Cb channel
/// </summary>
public byte[] YPixels { get; }
public JpegPixelArea CbChannel => new JpegPixelArea(this.CbPixels, this.CStride, this.COffset);
/// <summary>
/// Gets or sets the blue chroma components channel.
/// Gets the blue chroma components channel.
/// </summary>
public byte[] CbPixels { get; }
/// <summary>
/// Gets or sets the red chroma components channel.
/// Gets the index of the first element of red or blue chroma.
/// </summary>
public byte[] CrPixels { get; }
public int COffset { get; }
/// <summary>
/// Gets or sets the Y slice index delta between vertically adjacent pixels.
/// Gets an offseted <see cref="JpegPixelArea" /> to the Cr channel
/// </summary>
public int YStride { get; }
public JpegPixelArea CrChannel => new JpegPixelArea(this.CrPixels, this.CStride, this.COffset);
/// <summary>
/// Gets or sets the red and blue chroma slice index delta between vertically adjacent pixels
/// that map to separate chroma samples.
/// Gets the red chroma components channel.
/// </summary>
public byte[] CrPixels { get; }
/// <summary>
/// Gets the red and blue chroma slice index delta between vertically adjacent pixels
/// that map to separate chroma samples.
/// </summary>
public int CStride { get; }
/// <summary>
/// Gets or sets the index of the first luminance element.
/// Gets or sets the subsampling ratio.
/// </summary>
public int YOffset { get; }
public YCbCrSubsampleRatio Ratio { get; set; }
/// <summary>
/// Gets or sets the index of the first element of red or blue chroma.
/// Gets an offseted <see cref="JpegPixelArea" /> to the Y channel
/// </summary>
public int COffset { get; }
public JpegPixelArea YChannel => new JpegPixelArea(this.YPixels, this.YStride, this.YOffset);
/// <summary>
/// Gets or sets the horizontal position.
/// Gets the index of the first luminance element.
/// </summary>
public int X { get; set; }
public int YOffset { get; }
/// <summary>
/// Gets or sets the vertical position.
/// Gets the luminance components channel.
/// </summary>
public int Y { get; set; }
public byte[] YPixels { get; }
/// <summary>
/// Gets or sets the subsampling ratio.
/// Gets the Y slice index delta between vertically adjacent pixels.
/// </summary>
public YCbCrSubsampleRatio Ratio { get; set; }
public int YStride { get; }
/// <summary>
/// Returns the offset of the first luminance component at the given row
/// Disposes the <see cref="YCbCrImage" /> returning rented arrays to the pools.
/// </summary>
/// <param name="y">The row number.</param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
public int GetRowYOffset(int y)
public void Dispose()
{
return y * this.YStride;
CleanPooler<byte>.ReturnArray(this.YPixels);
CleanPooler<byte>.ReturnArray(this.CrPixels);
CleanPooler<byte>.ReturnArray(this.CbPixels);
}
/// <summary>
/// Returns the offset of the first chroma component at the given row
/// Returns the offset of the first chroma component at the given row
/// </summary>
/// <param name="y">The row number.</param>
/// <returns>
/// The <see cref="int"/>.
/// The <see cref="int" />.
/// </returns>
public int GetRowCOffset(int y)
{
@ -176,14 +160,31 @@ namespace ImageSharp.Formats.Jpg
}
/// <summary>
/// Returns the height and width of the chroma components
/// Returns the offset of the first luminance component at the given row
/// </summary>
/// <param name="y">The row number.</param>
/// <returns>
/// The <see cref="int" />.
/// </returns>
public int GetRowYOffset(int y)
{
return y * this.YStride;
}
/// <summary>
/// Returns the height and width of the chroma components
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="ratio">The subsampling ratio.</param>
/// <param name="chromaWidth">The chroma width.</param>
/// <param name="chromaHeight">The chroma height.</param>
private static void YCbCrSize(int width, int height, YCbCrSubsampleRatio ratio, out int chromaWidth, out int chromaHeight)
private static void YCbCrSize(
int width,
int height,
YCbCrSubsampleRatio ratio,
out int chromaWidth,
out int chromaHeight)
{
switch (ratio)
{
@ -215,12 +216,5 @@ namespace ImageSharp.Formats.Jpg
break;
}
}
public void Dispose()
{
CleanPooler<byte>.ReturnArray(this.YPixels);
CleanPooler<byte>.ReturnArray(this.CrPixels);
CleanPooler<byte>.ReturnArray(this.CbPixels);
}
}
}
}

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

File diff suppressed because it is too large

19
src/ImageSharp/Formats/Jpg/Utils/CleanPooler.cs

@ -1,13 +1,30 @@
namespace ImageSharp.Formats
// <copyright file="CleanPooler.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System.Buffers;
/// <summary>
/// Wraps <see cref="ArrayPool{T}"/> to always provide arrays initialized with default(T)
/// </summary>
/// <typeparam name="T">The element type</typeparam>
internal class CleanPooler<T>
{
private static readonly ArrayPool<T> Pool = ArrayPool<T>.Create();
/// <summary>
/// Rents a clean array
/// </summary>
/// <param name="minimumLength">The minimum array length</param>
/// <returns>A clean array of T</returns>
public static T[] RentCleanArray(int minimumLength) => Pool.Rent(minimumLength);
/// <summary>
/// Retursn array to the pool
/// </summary>
/// <param name="array">The array</param>
public static void ReturnArray(T[] array) => Pool.Return(array, true);
}
}
Loading…
Cancel
Save