Browse Source

ok StyleCop, this time I won!

af/merge-core
Anton Firszov 9 years ago
parent
commit
3a728e3589
  1. 7
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
  2. 26
      src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs
  3. 341
      src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs
  4. 7
      src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs
  5. 339
      src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
  6. 6
      src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
  7. 16
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

7
src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs

@ -373,12 +373,5 @@ namespace ImageSharp.Formats.Jpg
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy(Block8x8F* dest, Block8x8F* source)
{
*dest = *source;
//Unsafe.CopyBlock(dest, source, (uint)sizeof(Block8x8F));
}
}
}

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

@ -106,18 +106,6 @@ namespace ImageSharp.Formats.Jpg
return result;
}
/// <summary>
/// Initializes the Huffman tree
/// </summary>
private void Init()
{
this.Lut = UshortBuffer.Rent(1 << LutSize);
this.Values = ByteBuffer.Rent(MaxNCodes);
this.MinCodes = IntBuffer.Rent(MaxCodeLength);
this.MaxCodes = IntBuffer.Rent(MaxCodeLength);
this.Indices = IntBuffer.Rent(MaxCodeLength);
}
/// <summary>
/// Disposes the underlying buffers
/// </summary>
@ -136,7 +124,7 @@ namespace ImageSharp.Formats.Jpg
/// <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="remaining">Remaining bits</param>
internal void ProcessDefineHuffmanTablesMarkerLoop(
public void ProcessDefineHuffmanTablesMarkerLoop(
JpegDecoderCore decoder,
byte[] defineHuffmanTablesData,
ref int remaining)
@ -226,5 +214,17 @@ namespace ImageSharp.Formats.Jpg
c <<= 1;
}
}
/// <summary>
/// Initializes the Huffman tree
/// </summary>
private void Init()
{
this.Lut = UshortBuffer.Rent(1 << LutSize);
this.Values = ByteBuffer.Rent(MaxNCodes);
this.MinCodes = IntBuffer.Rent(MaxCodeLength);
this.MaxCodes = IntBuffer.Rent(MaxCodeLength);
this.Indices = IntBuffer.Rent(MaxCodeLength);
}
}
}

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

@ -1,4 +1,9 @@
// ReSharper disable InconsistentNaming
// <copyright file="JpegScanDecoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// ReSharper disable InconsistentNaming
namespace ImageSharp.Formats.Jpg
{
using System;
@ -12,128 +17,24 @@ namespace ImageSharp.Formats.Jpg
internal unsafe struct JpegScanDecoder
{
/// <summary>
/// The AC table index
/// Number of MCU-s (Minimum Coded Units) in the image along the X axis
/// </summary>
internal const int AcTableIndex = 1;
public int XNumberOfMCUs;
/// <summary>
/// The DC table index
/// Number of MCU-s (Minimum Coded Units) in the image along the Y axis
/// </summary>
internal const int DcTableIndex = 0;
public int YNumberOfMCUs;
/// <summary>
/// Holds the "large" data blocks needed for computations
/// The AC table index
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ComputationData
{
/// <summary>
/// The main input block
/// </summary>
public Block8x8F Block;
/// <summary>
/// Temporal block 1 to store intermediate and/or final computation results
/// </summary>
public Block8x8F Temp1;
/// <summary>
/// Temporal block 2 to store intermediate and/or final computation results
/// </summary>
public Block8x8F Temp2;
/// <summary>
/// The quantization table as <see cref="Block8x8F"/>
/// </summary>
public Block8x8F QuantiazationTable;
/// <summary>
/// The jpeg unzig data
/// </summary>
public UnzigData Unzig;
/// <summary>
/// The no-idea-what's this data
/// </summary>
public fixed byte ScanData[3 * JpegDecoderCore.MaxComponents];
/// <summary>
/// The DC component values
/// </summary>
public fixed int Dc[JpegDecoderCore.MaxComponents];
/// <summary>
/// Creates and initializes a new <see cref="ComputationData"/> instance
/// </summary>
/// <returns></returns>
public static ComputationData Create()
{
ComputationData data = default(ComputationData);
data.Unzig = UnzigData.Create();
return data;
}
}
private const int AcTableIndex = 1;
/// <summary>
/// Contains pointers to the memory regions of <see cref="ComputationData"/> so they can be easily passed around to pointer based utility methods of <see cref="Block8x8F"/>
/// The DC table index
/// </summary>
public struct DataPointers
{
/// <summary>
/// Pointer to <see cref="ComputationData.Block"/>
/// </summary>
public Block8x8F* Block;
/// <summary>
/// Pointer to <see cref="ComputationData.Temp1"/>
/// </summary>
public Block8x8F* Temp1;
/// <summary>
/// Pointer to <see cref="ComputationData.Temp2"/>
/// </summary>
public Block8x8F* Temp2;
/// <summary>
/// Pointer to <see cref="ComputationData.QuantiazationTable"/>
/// </summary>
public Block8x8F* QuantiazationTable;
/// <summary>
/// Pointer to <see cref="ComputationData.Unzig"/> as int*
/// </summary>
public int* Unzig;
/// <summary>
/// Pointer to <see cref="ComputationData.ScanData"/> as Scan*
/// </summary>
public Scan* Scan;
/// <summary>
/// Pointer to <see cref="ComputationData.Dc"/>
/// </summary>
public int* Dc;
public DataPointers(ComputationData* basePtr)
{
this.Block = &basePtr->Block;
this.Temp1 = &basePtr->Temp1;
this.Temp2 = &basePtr->Temp2;
this.QuantiazationTable = &basePtr->QuantiazationTable;
this.Unzig = basePtr->Unzig.Data;
this.Scan = (Scan*)basePtr->ScanData;
this.Dc = basePtr->Dc;
}
}
private void ResetDc()
{
Unsafe.InitBlock(this.Pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents);
}
// bx and by are the
// blocks: the third block in the first row has (bx, by) = (2, 0).
private const int DcTableIndex = 0;
/// <summary>
/// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0))
@ -180,32 +81,25 @@ namespace ImageSharp.Formats.Jpg
/// </summary>
private int al;
// XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image.
/// <summary>
/// Number of MCU-s (Minimum Coded Units) on X axis
/// </summary>
public int XNumberOfMCUs;
/// <summary>
/// Number of MCU-s (Minimum Coded Units) in Y axis
/// The number of component scans
/// </summary>
public int YNumberOfMCUs;
private int componentScanCount;
/// <summary>
/// The number of component scans
/// End-of-Band run, specified in section G.1.2.2.
/// </summary>
private int componentScanCount;
private ushort eobRun;
/// <summary>
/// The <see cref="ComputationData"/> buffer
/// </summary>
private ComputationData Data;
private ComputationData data;
/// <summary>
/// Pointers to elements of <see cref="Data"/>
/// Pointers to elements of <see cref="data"/>
/// </summary>
private DataPointers Pointers;
private DataPointers pointers;
/// <summary>
/// Initializes the default instance after creation.
@ -215,8 +109,8 @@ namespace ImageSharp.Formats.Jpg
/// <param name="remaining">The remaining bytes in the segment block.</param>
public static void Init(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining)
{
p->Data = ComputationData.Create();
p->Pointers = new DataPointers(&p->Data);
p->data = ComputationData.Create();
p->pointers = new DataPointers(&p->data);
p->InitImpl(decoder, remaining);
}
@ -236,7 +130,7 @@ namespace ImageSharp.Formats.Jpg
{
for (int i = 0; i < this.componentScanCount; i++)
{
int compIndex = this.Pointers.Scan[i].Index;
int compIndex = this.pointers.Scan[i].Index;
int hi = decoder.ComponentArray[compIndex].HorizontalFactor;
int vi = decoder.ComponentArray[compIndex].VerticalFactor;
@ -284,18 +178,17 @@ namespace ImageSharp.Formats.Jpg
int qtIndex = decoder.ComponentArray[compIndex].Selector;
// TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async.
this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex];
this.Data.QuantiazationTable = decoder.QuantizationTables[qtIndex];
//Load the previous partially decoded coefficients, if applicable.
// Load the previous partially decoded coefficients, if applicable.
if (decoder.IsProgressive)
{
int blockIndex = ((this.by * this.XNumberOfMCUs) * hi) + this.bx;
this.Data.Block = decoder.ProgCoeffs[compIndex][blockIndex];
this.data.Block = decoder.ProgCoeffs[compIndex][blockIndex];
}
else
{
this.Data.Block.Clear();
this.data.Block.Clear();
}
this.ProcessBlockImpl(decoder, i, compIndex, hi);
@ -330,14 +223,19 @@ namespace ImageSharp.Formats.Jpg
this.ResetDc();
// Reset the progressive decoder state, as per section G.1.2.2.
decoder.EobRun = 0;
this.eobRun = 0;
}
}
// for mx
}
}
private void ResetDc()
{
Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents);
}
/// <summary>
/// The implementation part of <see cref="Init"/> as an instance method.
/// </summary>
@ -368,7 +266,7 @@ namespace ImageSharp.Formats.Jpg
for (int i = 0; i < this.componentScanCount; i++)
{
this.ProcessScanImpl(decoder, i, ref this.Pointers.Scan[i], ref totalHv);
this.ProcessScanImpl(decoder, i, ref this.pointers.Scan[i], ref totalHv);
}
// Section B.2.3 states that if there is more than one component then the
@ -414,7 +312,7 @@ namespace ImageSharp.Formats.Jpg
{
for (int i = 0; i < this.componentScanCount; i++)
{
int compIndex = this.Pointers.Scan[i].Index;
int compIndex = this.pointers.Scan[i].Index;
if (decoder.ProgCoeffs[compIndex] == null)
{
int size = this.XNumberOfMCUs * this.YNumberOfMCUs * decoder.ComponentArray[compIndex].HorizontalFactor
@ -435,9 +333,9 @@ namespace ImageSharp.Formats.Jpg
/// <param name="hi">Horizontal sampling factor at the given component index</param>
private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi)
{
var b = this.Pointers.Block;
//var dc = this.Pointers.Dc;
int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].AcTableSelector;
var b = this.pointers.Block;
int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.Scan[i].AcTableSelector;
if (this.ah != 0)
{
this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al);
@ -452,22 +350,22 @@ namespace ImageSharp.Formats.Jpg
// Decode the DC coefficient, as specified in section F.2.2.1.
byte value =
decoder.DecodeHuffman(
ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].DcTableSelector]);
ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.Scan[i].DcTableSelector]);
if (value > 16)
{
throw new ImageFormatException("Excessive DC component");
}
int deltaDC = decoder.Bits.ReceiveExtend(value, decoder);
this.Pointers.Dc[compIndex] += deltaDC;
this.pointers.Dc[compIndex] += deltaDC;
// b[0] = dc[compIndex] << al;
Block8x8F.SetScalarAt(b, 0, this.Pointers.Dc[compIndex] << al);
Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[compIndex] << this.al);
}
if (zig <= this.zigEnd && decoder.EobRun > 0)
if (zig <= this.zigEnd && this.eobRun > 0)
{
decoder.EobRun--;
this.eobRun--;
}
else
{
@ -488,19 +386,19 @@ namespace ImageSharp.Formats.Jpg
int ac = decoder.Bits.ReceiveExtend(val1, decoder);
// b[Unzig[zig]] = ac << al;
Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], ac << this.al);
Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al);
}
else
{
if (val0 != 0x0f)
{
decoder.EobRun = (ushort)(1 << val0);
this.eobRun = (ushort)(1 << val0);
if (val0 != 0)
{
decoder.EobRun |= (ushort)decoder.DecodeBits(val0);
this.eobRun |= (ushort)decoder.DecodeBits(val0);
}
decoder.EobRun--;
this.eobRun--;
break;
}
@ -514,7 +412,7 @@ namespace ImageSharp.Formats.Jpg
{
if (this.zigEnd != Block8x8F.ScalarCount - 1 || this.al != 0)
{
// We haven't completely decoded this 8x8 block. Save the coefficients.
// We haven't completely decoded this 8x8 block. Save the coefficients.
// this.ProgCoeffs[compIndex][((@by * XNumberOfMCUs) * hi) + bx] = b.Clone();
decoder.ProgCoeffs[compIndex][((this.by * this.XNumberOfMCUs) * hi) + this.bx] = *b;
@ -528,13 +426,13 @@ namespace ImageSharp.Formats.Jpg
}
// Dequantize, perform the inverse DCT and store the block to the image.
Block8x8F.UnZig(b, this.Pointers.QuantiazationTable, this.Pointers.Unzig);
Block8x8F.UnZig(b, this.pointers.QuantiazationTable, this.pointers.Unzig);
DCT.TransformIDCT(ref *b, ref *this.Pointers.Temp1, ref *this.Pointers.Temp2);
DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2);
var destChannel = decoder.GetDestinationChannel(compIndex);
var destArea = destChannel.GetOffsetedSubAreaForBlock(this.bx, this.by);
destArea.LoadColorsFrom(this.Pointers.Temp1, this.Pointers.Temp2);
destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2);
}
private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv)
@ -561,7 +459,6 @@ namespace ImageSharp.Formats.Jpg
this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]);
}
private void ProcessComponentImpl(
JpegDecoderCore decoder,
int i,
@ -576,7 +473,7 @@ namespace ImageSharp.Formats.Jpg
// into comp are unique.
for (int j = 0; j < i; j++)
{
if (currentScan.Index == this.Pointers.Scan[j].Index)
if (currentScan.Index == this.pointers.Scan[j].Index)
{
throw new ImageFormatException("Repeated component selector");
}
@ -600,12 +497,13 @@ namespace ImageSharp.Formats.Jpg
/// <summary>
/// Decodes a successive approximation refinement block, as specified in section G.1.2.
/// </summary>
/// <param name="decoder">The decoder instance</param>
/// <param name="h">The Huffman tree</param>
/// <param name="delta">The low transform offset</param>
/// <param name="decoder">The decoder instance</param>
private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta)
{
Block8x8F* b = this.Pointers.Block;
Block8x8F* b = this.pointers.Block;
// Refining a DC component is trivial.
if (this.zigStart == 0)
{
@ -631,7 +529,7 @@ namespace ImageSharp.Formats.Jpg
// Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3.
int zig = this.zigStart;
if (decoder.EobRun == 0)
if (this.eobRun == 0)
{
for (; zig <= this.zigEnd; zig++)
{
@ -646,10 +544,10 @@ namespace ImageSharp.Formats.Jpg
case 0:
if (val0 != 0x0f)
{
decoder.EobRun = (ushort)(1 << val0);
this.eobRun = (ushort)(1 << val0);
if (val0 != 0)
{
decoder.EobRun |= (ushort)decoder.DecodeBits(val0);
this.eobRun |= (ushort)decoder.DecodeBits(val0);
}
done = true;
@ -683,14 +581,14 @@ namespace ImageSharp.Formats.Jpg
if (z != 0)
{
// b[Unzig[zig]] = z;
Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], z);
Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], z);
}
}
}
if (decoder.EobRun > 0)
if (this.eobRun > 0)
{
decoder.EobRun--;
this.eobRun--;
this.RefineNonZeroes(decoder, zig, -1, delta);
}
}
@ -706,10 +604,10 @@ namespace ImageSharp.Formats.Jpg
/// <returns>The <see cref="int" /></returns>
private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta)
{
var b = this.Pointers.Block;
var b = this.pointers.Block;
for (; zig <= this.zigEnd; zig++)
{
int u = this.Pointers.Unzig[zig];
int u = this.pointers.Unzig[zig];
float bu = Block8x8F.GetScalarAt(b, u);
// TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary?
@ -744,5 +642,114 @@ namespace ImageSharp.Formats.Jpg
return zig;
}
/// <summary>
/// Holds the "large" data blocks needed for computations
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ComputationData
{
/// <summary>
/// The main input block
/// </summary>
public Block8x8F Block;
/// <summary>
/// Temporal block 1 to store intermediate and/or final computation results
/// </summary>
public Block8x8F Temp1;
/// <summary>
/// Temporal block 2 to store intermediate and/or final computation results
/// </summary>
public Block8x8F Temp2;
/// <summary>
/// The quantization table as <see cref="Block8x8F"/>
/// </summary>
public Block8x8F QuantiazationTable;
/// <summary>
/// The jpeg unzig data
/// </summary>
public UnzigData Unzig;
/// <summary>
/// The no-idea-what's this data
/// </summary>
public fixed byte ScanData[3 * JpegDecoderCore.MaxComponents];
/// <summary>
/// The DC component values
/// </summary>
public fixed int Dc[JpegDecoderCore.MaxComponents];
/// <summary>
/// Creates and initializes a new <see cref="ComputationData"/> instance
/// </summary>
/// <returns>The <see cref="ComputationData"/></returns>
public static ComputationData Create()
{
ComputationData data = default(ComputationData);
data.Unzig = UnzigData.Create();
return data;
}
}
/// <summary>
/// Contains pointers to the memory regions of <see cref="ComputationData"/> so they can be easily passed around to pointer based utility methods of <see cref="Block8x8F"/>
/// </summary>
public struct DataPointers
{
/// <summary>
/// Pointer to <see cref="ComputationData.Block"/>
/// </summary>
public Block8x8F* Block;
/// <summary>
/// Pointer to <see cref="ComputationData.Temp1"/>
/// </summary>
public Block8x8F* Temp1;
/// <summary>
/// Pointer to <see cref="ComputationData.Temp2"/>
/// </summary>
public Block8x8F* Temp2;
/// <summary>
/// Pointer to <see cref="ComputationData.QuantiazationTable"/>
/// </summary>
public Block8x8F* QuantiazationTable;
/// <summary>
/// Pointer to <see cref="ComputationData.Unzig"/> as int*
/// </summary>
public int* Unzig;
/// <summary>
/// Pointer to <see cref="ComputationData.ScanData"/> as Scan*
/// </summary>
public Scan* Scan;
/// <summary>
/// Pointer to <see cref="ComputationData.Dc"/>
/// </summary>
public int* Dc;
/// <summary>
/// Initializes a new instance of the <see cref="DataPointers" /> struct.
/// </summary>
/// <param name="basePtr">The pointer pointing to <see cref="ComputationData"/></param>
public DataPointers(ComputationData* basePtr)
{
this.Block = &basePtr->Block;
this.Temp1 = &basePtr->Temp1;
this.Temp2 = &basePtr->Temp2;
this.QuantiazationTable = &basePtr->QuantiazationTable;
this.Unzig = basePtr->Unzig.Data;
this.Scan = (Scan*)basePtr->ScanData;
this.Dc = basePtr->Dc;
}
}
}
}

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

@ -1,4 +1,9 @@
namespace ImageSharp.Formats.Jpg
// <copyright file="Scan.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.Runtime.InteropServices;

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

@ -21,30 +21,33 @@ namespace ImageSharp.Formats
/// </summary>
public const int MaxComponents = 4;
// Complex value type field + mutable + available to other classes == the field MUST NOT be private :P
#pragma warning disable SA1401 // FieldsMustBePrivate
/// <summary>
/// The App14 marker color-space
/// Holds the unprocessed bits that have been taken from the byte-stream.
/// </summary>
private byte adobeTransform;
public Bits Bits;
/// <summary>
/// Whether the image is in CMYK format with an App14 marker
/// The byte buffer.
/// </summary>
private bool adobeTransformValid;
public Bytes Bytes;
#pragma warning restore SA401
/// <summary>
/// Holds the unprocessed bits that have been taken from the byte-stream.
/// The maximum number of quantization tables
/// </summary>
public Bits Bits;
private const int MaxTq = 3;
/// <summary>
/// The byte buffer.
/// The App14 marker color-space
/// </summary>
public Bytes Bytes;
private byte adobeTransform;
/// <summary>
/// End-of-Band run, specified in section G.1.2.2.
/// Whether the image is in CMYK format with an App14 marker
/// </summary>
public ushort EobRun;
private bool adobeTransformValid;
/// <summary>
/// The black image to decode to.
@ -60,17 +63,12 @@ namespace ImageSharp.Formats
/// The horizontal resolution. Calculated if the image has a JFIF header.
/// </summary>
private short horizontalResolution;
/// <summary>
/// The maximum number of quantization tables
/// </summary>
private const int MaxTq = 3;
/// <summary>
/// Whether the image has a JFIF header
/// </summary>
private bool isJfif;
/// <summary>
/// The vertical resolution. Calculated if the image has a JFIF header.
/// </summary>
@ -112,43 +110,44 @@ namespace ImageSharp.Formats
MissingFF00
}
/// <summary>
/// The component array
/// Gets the component array
/// </summary>
public Component[] ComponentArray { get; }
/// <summary>
/// The huffman trees
/// Gets the huffman trees
/// </summary>
public HuffmanTree[] HuffmanTrees { get; }
/// <summary>
/// Saved state between progressive-mode scans.
/// Gets the saved state between progressive-mode scans.
/// </summary>
public Block8x8F[][] ProgCoeffs { get; }
/// <summary>
/// Quantization tables, in zigzag order.
/// Gets the quantization tables, in zigzag order.
/// </summary>
public Block8x8F[] QuantizationTables { get; }
/// <summary>
/// A temporary buffer for holding pixels
/// Gets the temporary buffer for holding pixel (and other?) data
/// </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.
/// Gets the number of color components within the image.
/// </summary>
public int ComponentCount { get; private set; }
/// <summary>
/// The image height
/// Gets the image height
/// </summary>
public int ImageHeight { get; private set; }
/// <summary>
/// The image width
/// Gets the image width
/// </summary>
public int ImageWidth { get; private set; }
@ -156,16 +155,17 @@ namespace ImageSharp.Formats
/// Gets the input stream.
/// </summary>
public Stream InputStream { get; private set; }
/// <summary>
/// Whether the image is interlaced (progressive)
/// Gets a value indicating whether the image is interlaced (progressive)
/// </summary>
public bool IsProgressive { get; private set; }
/// <summary>
/// The restart interval
/// Gets 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.
@ -407,18 +407,39 @@ namespace ImageSharp.Formats
/// </summary>
/// <returns>The <see cref="byte" /></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByte()
public byte ReadByte()
{
return this.Bytes.ReadByte(this.InputStream);
}
/// <summary>
/// Decodes a single bit
/// </summary>
/// <returns>The <see cref="bool" /></returns>
public bool DecodeBit()
{
if (this.Bits.UnreadBits == 0)
{
ErrorCodes errorCode = this.Bits.EnsureNBits(1, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
}
bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0;
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
return ret;
}
/// <summary>
/// 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>
/// <param name="length">The number of bytes to read</param>
internal void ReadFull(byte[] data, int offset, int length)
public void ReadFull(byte[] data, int offset, int length)
{
// Unread the overshot bytes, if any.
if (this.Bytes.UnreadableBytes != 0)
@ -451,6 +472,125 @@ namespace ImageSharp.Formats
}
}
/// <summary>
/// Decodes the given number of bits
/// </summary>
/// <param name="count">The number of bits to decode.</param>
/// <returns>The <see cref="uint" /></returns>
public uint DecodeBits(int count)
{
if (this.Bits.UnreadBits < count)
{
ErrorCodes errorCode = this.Bits.EnsureNBits(count, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
}
uint ret = this.Bits.Accumulator >> (this.Bits.UnreadBits - count);
ret = (uint)(ret & ((1 << count) - 1));
this.Bits.UnreadBits -= count;
this.Bits.Mask >>= count;
return ret;
}
/// <summary>
/// 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>
public byte DecodeHuffman(ref HuffmanTree huffmanTree)
{
// Copy stuff to the stack:
if (huffmanTree.Length == 0)
{
throw new ImageFormatException("Uninitialized Huffman table");
}
if (this.Bits.UnreadBits < 8)
{
ErrorCodes errorCode = this.Bits.EnsureNBits(8, this);
if (errorCode == ErrorCodes.NoError)
{
ushort v =
huffmanTree.Lut[(this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSize)) & 0xff];
if (v != 0)
{
byte n = (byte)((v & 0xff) - 1);
this.Bits.UnreadBits -= n;
this.Bits.Mask >>= n;
return (byte)(v >> 8);
}
}
else
{
this.UnreadByteStuffedByte();
}
}
int code = 0;
for (int i = 0; i < HuffmanTree.MaxCodeLength; i++)
{
if (this.Bits.UnreadBits == 0)
{
ErrorCodes errorCode = this.Bits.EnsureNBits(1, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
}
if ((this.Bits.Accumulator & this.Bits.Mask) != 0)
{
code |= 1;
}
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
if (code <= huffmanTree.MaxCodes[i])
{
return huffmanTree.Values[huffmanTree.Indices[i] + code - huffmanTree.MinCodes[i]];
}
code <<= 1;
}
throw new ImageFormatException("Bad Huffman code");
}
/// <summary>
/// Gets the <see cref="JpegPixelArea"/> representing the channel at a given component index
/// </summary>
/// <param name="compIndex">The component index</param>
/// <returns>The <see cref="JpegPixelArea"/> of the channel</returns>
public JpegPixelArea GetDestinationChannel(int compIndex)
{
if (this.ComponentCount == 1)
{
return this.grayImage;
}
else
{
switch (compIndex)
{
case 0:
return this.ycbcrImage.YChannel;
case 1:
return this.ycbcrImage.CbChannel;
case 2:
return this.ycbcrImage.CrChannel;
case 3:
return this.blackImage;
default:
throw new ImageFormatException("Too many components");
}
}
}
/// <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.
@ -684,141 +824,6 @@ namespace ImageSharp.Formats
this.AssignResolution(image);
}
/// <summary>
/// Decodes a single bit
/// </summary>
/// <returns>The <see cref="bool" /></returns>
internal bool DecodeBit()
{
if (this.Bits.UnreadBits == 0)
{
ErrorCodes errorCode = this.Bits.EnsureNBits(1, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
}
bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0;
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
return ret;
}
/// <summary>
/// Decodes the given number of bits
/// </summary>
/// <param name="count">The number of bits to decode.</param>
/// <returns>The <see cref="uint" /></returns>
internal uint DecodeBits(int count)
{
if (this.Bits.UnreadBits < count)
{
ErrorCodes errorCode = this.Bits.EnsureNBits(count, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
}
uint ret = this.Bits.Accumulator >> (this.Bits.UnreadBits - count);
ret = (uint)(ret & ((1 << count) - 1));
this.Bits.UnreadBits -= count;
this.Bits.Mask >>= count;
return ret;
}
/// <summary>
/// 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>
internal byte DecodeHuffman(ref HuffmanTree huffmanTree)
{
// Copy stuff to the stack:
if (huffmanTree.Length == 0)
{
throw new ImageFormatException("Uninitialized Huffman table");
}
if (this.Bits.UnreadBits < 8)
{
ErrorCodes errorCode = this.Bits.EnsureNBits(8, this);
if (errorCode == ErrorCodes.NoError)
{
ushort v =
huffmanTree.Lut[(this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSize)) & 0xff];
if (v != 0)
{
byte n = (byte)((v & 0xff) - 1);
this.Bits.UnreadBits -= n;
this.Bits.Mask >>= n;
return (byte)(v >> 8);
}
}
else
{
this.UnreadByteStuffedByte();
}
}
int code = 0;
for (int i = 0; i < HuffmanTree.MaxCodeLength; i++)
{
if (this.Bits.UnreadBits == 0)
{
ErrorCodes errorCode = this.Bits.EnsureNBits(1, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
}
if ((this.Bits.Accumulator & this.Bits.Mask) != 0)
{
code |= 1;
}
this.Bits.UnreadBits--;
this.Bits.Mask >>= 1;
if (code <= huffmanTree.MaxCodes[i])
{
return huffmanTree.Values[huffmanTree.Indices[i] + code - huffmanTree.MinCodes[i]];
}
code <<= 1;
}
throw new ImageFormatException("Bad Huffman code");
}
internal JpegPixelArea GetDestinationChannel(int compIndex)
{
if (this.ComponentCount == 1)
{
return this.grayImage;
}
else
{
switch (compIndex)
{
case 0:
return this.ycbcrImage.YChannel;
case 1:
return this.ycbcrImage.CbChannel;
case 2:
return this.ycbcrImage.CrChannel;
case 3:
return this.blackImage;
default:
throw new ImageFormatException("Too many components");
}
}
}
/// <summary>
/// Returns a value indicating whether the image in an RGB image.
/// </summary>

6
src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs

@ -420,7 +420,7 @@ namespace ImageSharp.Formats
private void Encode444<TColor>(PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
// TODO: Need a JpegEncoderScanProcessor<TColor> struct to encapsulate all this mess:
// TODO: Need a JpegScanEncoder<TColor> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
Block8x8F b = default(Block8x8F);
Block8x8F cb = default(Block8x8F);
Block8x8F cr = default(Block8x8F);
@ -759,7 +759,7 @@ namespace ImageSharp.Formats
private void WriteStartOfScan<TColor>(PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
// TODO: This method should be the entry point for a JpegEncoderScanProcessor<TColor> struct
// TODO: Need a JpegScanEncoder<TColor> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
// TODO: We should allow grayscale writing.
this.outputStream.Write(SosHeaderYCbCr, 0, SosHeaderYCbCr.Length);
@ -786,7 +786,7 @@ namespace ImageSharp.Formats
private void Encode420<TColor>(PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
// TODO: Need a JpegEncoderScanProcessor<TColor> struct to encapsulate all this mess:
// TODO: Need a JpegScanEncoder<TColor> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.)
Block8x8F b = default(Block8x8F);
BlockQuad cb = default(BlockQuad);

16
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -433,21 +433,5 @@ namespace ImageSharp.Tests
Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f));
}
[Fact]
public unsafe void Copy_FromHeap()
{
Block8x8F[] blox = new Block8x8F[1];
blox[0].LoadFrom(Create8x8FloatData());
Block8x8F clone = default(Block8x8F);
fixed (Block8x8F* p = &blox[0])
{
Block8x8F.Copy(&clone, p);
}
Assert.Equal(blox[0], clone);
}
}
}
Loading…
Cancel
Save