Browse Source

further Jpeg cleanup & refactor

af/merge-core
Anton Firszov 9 years ago
parent
commit
b98638ff98
  1. 6
      src/ImageSharp.Formats.Jpeg/Components/Decoder/ComponentScan.cs
  2. 94
      src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs
  3. 17
      src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs

6
src/ImageSharp.Formats.Jpeg/Components/Decoder/Scan.cs → src/ImageSharp.Formats.Jpeg/Components/Decoder/ComponentScan.cs

@ -1,4 +1,4 @@
// <copyright file="Scan.cs" company="James Jackson-South"> // <copyright file="ComponentScan.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -11,12 +11,12 @@ namespace ImageSharp.Formats.Jpg
/// Represents a component scan /// Represents a component scan
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct Scan internal struct ComponentScan
{ {
/// <summary> /// <summary>
/// Gets or sets the component index. /// Gets or sets the component index.
/// </summary> /// </summary>
public byte Index; public byte ComponentIndex;
/// <summary> /// <summary>
/// Gets or sets the DC table selector /// Gets or sets the DC table selector

94
src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs

@ -76,6 +76,16 @@ namespace ImageSharp.Formats.Jpg
/// </summary> /// </summary>
private int componentScanCount; private int componentScanCount;
/// <summary>
/// The current component index
/// </summary>
private int componentIndex;
/// <summary>
/// Horizontal sampling factor at the current component index
/// </summary>
private int hi;
/// <summary> /// <summary>
/// End-of-Band run, specified in section G.1.2.2. /// End-of-Band run, specified in section G.1.2.2.
/// </summary> /// </summary>
@ -120,11 +130,11 @@ namespace ImageSharp.Formats.Jpg
{ {
for (int i = 0; i < this.componentScanCount; i++) for (int i = 0; i < this.componentScanCount; i++)
{ {
int compIndex = this.pointers.Scan[i].Index; this.componentIndex = this.pointers.ComponentScan[i].ComponentIndex;
int hi = decoder.ComponentArray[compIndex].HorizontalFactor; this.hi = decoder.ComponentArray[this.componentIndex].HorizontalFactor;
int vi = decoder.ComponentArray[compIndex].VerticalFactor; int vi = decoder.ComponentArray[this.componentIndex].VerticalFactor;
for (int j = 0; j < hi * vi; j++) for (int j = 0; j < this.hi * vi; j++)
{ {
// The blocks are traversed one MCU at a time. For 4:2:0 chroma // The blocks are traversed one MCU at a time. For 4:2:0 chroma
// subsampling, there are four Y 8x8 blocks in every 16x16 MCU. // subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
@ -150,12 +160,12 @@ namespace ImageSharp.Formats.Jpg
// 3 4 5 // 3 4 5
if (this.componentScanCount != 1) if (this.componentScanCount != 1)
{ {
this.bx = (hi * mx) + (j % hi); this.bx = (this.hi * mx) + (j % this.hi);
this.by = (vi * my) + (j / hi); this.by = (vi * my) + (j / this.hi);
} }
else else
{ {
int q = decoder.MCUCountX * hi; int q = decoder.MCUCountX * this.hi;
this.bx = blockCount % q; this.bx = blockCount % q;
this.by = blockCount / q; this.by = blockCount / q;
blockCount++; blockCount++;
@ -165,7 +175,7 @@ namespace ImageSharp.Formats.Jpg
} }
} }
int qtIndex = decoder.ComponentArray[compIndex].Selector; int qtIndex = decoder.ComponentArray[this.componentIndex].Selector;
// TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async. // 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];
@ -173,15 +183,15 @@ namespace ImageSharp.Formats.Jpg
// Load the previous partially decoded coefficients, if applicable. // Load the previous partially decoded coefficients, if applicable.
if (decoder.IsProgressive) if (decoder.IsProgressive)
{ {
int blockIndex = ((this.by * decoder.MCUCountX) * hi) + this.bx; int blockIndex = this.GetBlockIndex(decoder);
this.data.Block = decoder.ProgCoeffs[compIndex][blockIndex]; this.data.Block = decoder.DecodedBlocks[this.componentIndex][blockIndex];
} }
else else
{ {
this.data.Block.Clear(); this.data.Block.Clear();
} }
this.ProcessBlockImpl(decoder, i, compIndex, hi); this.ProcessBlockImpl(decoder, i);
} }
// for j // for j
@ -256,7 +266,7 @@ namespace ImageSharp.Formats.Jpg
for (int i = 0; i < this.componentScanCount; i++) 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.ComponentScan[i], ref totalHv);
} }
// Section B.2.3 states that if there is more than one component then the // Section B.2.3 states that if there is more than one component then the
@ -291,22 +301,6 @@ namespace ImageSharp.Formats.Jpg
throw new ImageFormatException("Bad successive approximation values"); throw new ImageFormatException("Bad successive approximation values");
} }
} }
if (decoder.IsProgressive)
{
for (int i = 0; i < this.componentScanCount; i++)
{
int compIndex = this.pointers.Scan[i].Index;
if (decoder.ProgCoeffs[compIndex] == null)
{
int size = decoder.TotalMCUCount
* decoder.ComponentArray[compIndex].HorizontalFactor
* decoder.ComponentArray[compIndex].VerticalFactor;
decoder.ProgCoeffs[compIndex] = new Block8x8F[size];
}
}
}
} }
/// <summary> /// <summary>
@ -314,13 +308,11 @@ namespace ImageSharp.Formats.Jpg
/// </summary> /// </summary>
/// <param name="decoder">The decoder</param> /// <param name="decoder">The decoder</param>
/// <param name="i">The index of the scan</param> /// <param name="i">The index of the scan</param>
/// <param name="compIndex">The component index</param> private void ProcessBlockImpl(JpegDecoderCore decoder, int i)
/// <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 b = this.pointers.Block;
int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.Scan[i].AcTableSelector; int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[i].AcTableSelector;
if (this.ah != 0) if (this.ah != 0)
{ {
this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al);
@ -335,17 +327,17 @@ namespace ImageSharp.Formats.Jpg
// Decode the DC coefficient, as specified in section F.2.2.1. // Decode the DC coefficient, as specified in section F.2.2.1.
byte value = byte value =
decoder.DecodeHuffman( decoder.DecodeHuffman(
ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.Scan[i].DcTableSelector]); ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[i].DcTableSelector]);
if (value > 16) if (value > 16)
{ {
throw new ImageFormatException("Excessive DC component"); throw new ImageFormatException("Excessive DC component");
} }
int deltaDC = decoder.Bits.ReceiveExtend(value, decoder); int deltaDC = decoder.Bits.ReceiveExtend(value, decoder);
this.pointers.Dc[compIndex] += deltaDC; this.pointers.Dc[this.componentIndex] += deltaDC;
// b[0] = dc[compIndex] << al; // b[0] = dc[compIndex] << al;
Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[compIndex] << this.al); Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[this.componentIndex] << this.al);
} }
if (zig <= this.zigEnd && this.eobRun > 0) if (zig <= this.zigEnd && this.eobRun > 0)
@ -398,8 +390,7 @@ namespace ImageSharp.Formats.Jpg
if (this.zigEnd != Block8x8F.ScalarCount - 1 || this.al != 0) 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.DecodedBlocks[this.componentIndex][this.GetBlockIndex(decoder)] = *b;
decoder.ProgCoeffs[compIndex][((this.by * decoder.MCUCountX) * hi) + this.bx] = *b;
// At this point, we could execute the rest of the loop body to dequantize and // At this point, we could execute the rest of the loop body to dequantize and
// perform the inverse DCT, to save early stages of a progressive image to the // perform the inverse DCT, to save early stages of a progressive image to the
@ -415,12 +406,17 @@ namespace ImageSharp.Formats.Jpg
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 destChannel = decoder.GetDestinationChannel(this.componentIndex);
var destArea = destChannel.GetOffsetedSubAreaForBlock(this.bx, this.by); 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) private int GetBlockIndex(JpegDecoderCore decoder)
{
return ((this.@by * decoder.MCUCountX) * this.hi) + this.bx;
}
private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref ComponentScan currentComponentScan, ref int totalHv)
{ {
// Component selector. // Component selector.
int cs = decoder.Temp[1 + (2 * i)]; int cs = decoder.Temp[1 + (2 * i)];
@ -439,15 +435,15 @@ namespace ImageSharp.Formats.Jpg
throw new ImageFormatException("Unknown component selector"); throw new ImageFormatException("Unknown component selector");
} }
currentScan.Index = (byte)compIndex; currentComponentScan.ComponentIndex = (byte)compIndex;
this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); this.ProcessComponentImpl(decoder, i, ref currentComponentScan, ref totalHv, ref decoder.ComponentArray[compIndex]);
} }
private void ProcessComponentImpl( private void ProcessComponentImpl(
JpegDecoderCore decoder, JpegDecoderCore decoder,
int i, int i,
ref Scan currentScan, ref ComponentScan currentComponentScan,
ref int totalHv, ref int totalHv,
ref Component currentComponent) ref Component currentComponent)
{ {
@ -458,7 +454,7 @@ namespace ImageSharp.Formats.Jpg
// into comp are unique. // into comp are unique.
for (int j = 0; j < i; j++) for (int j = 0; j < i; j++)
{ {
if (currentScan.Index == this.pointers.Scan[j].Index) if (currentComponentScan.ComponentIndex == this.pointers.ComponentScan[j].ComponentIndex)
{ {
throw new ImageFormatException("Repeated component selector"); throw new ImageFormatException("Repeated component selector");
} }
@ -466,14 +462,14 @@ namespace ImageSharp.Formats.Jpg
totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor;
currentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); currentComponentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4);
if (currentScan.DcTableSelector > HuffmanTree.MaxTh) if (currentComponentScan.DcTableSelector > HuffmanTree.MaxTh)
{ {
throw new ImageFormatException("Bad DC table selector value"); throw new ImageFormatException("Bad DC table selector value");
} }
currentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); currentComponentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f);
if (currentScan.AcTableSelector > HuffmanTree.MaxTh) if (currentComponentScan.AcTableSelector > HuffmanTree.MaxTh)
{ {
throw new ImageFormatException("Bad AC table selector value"); throw new ImageFormatException("Bad AC table selector value");
} }
@ -714,7 +710,7 @@ namespace ImageSharp.Formats.Jpg
/// <summary> /// <summary>
/// Pointer to <see cref="ComputationData.ScanData"/> as Scan* /// Pointer to <see cref="ComputationData.ScanData"/> as Scan*
/// </summary> /// </summary>
public Scan* Scan; public ComponentScan* ComponentScan;
/// <summary> /// <summary>
/// Pointer to <see cref="ComputationData.Dc"/> /// Pointer to <see cref="ComputationData.Dc"/>
@ -732,7 +728,7 @@ namespace ImageSharp.Formats.Jpg
this.Temp2 = &basePtr->Temp2; this.Temp2 = &basePtr->Temp2;
this.QuantiazationTable = &basePtr->QuantiazationTable; this.QuantiazationTable = &basePtr->QuantiazationTable;
this.Unzig = basePtr->Unzig.Data; this.Unzig = basePtr->Unzig.Data;
this.Scan = (Scan*)basePtr->ScanData; this.ComponentScan = (ComponentScan*)basePtr->ScanData;
this.Dc = basePtr->Dc; this.Dc = basePtr->Dc;
} }
} }

17
src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs

@ -88,7 +88,7 @@ namespace ImageSharp.Formats
this.QuantizationTables = new Block8x8F[MaxTq + 1]; this.QuantizationTables = new Block8x8F[MaxTq + 1];
this.Temp = new byte[2 * Block8x8F.ScalarCount]; this.Temp = new byte[2 * Block8x8F.ScalarCount];
this.ComponentArray = new Component[MaxComponents]; this.ComponentArray = new Component[MaxComponents];
this.ProgCoeffs = new Block8x8F[MaxComponents][]; this.DecodedBlocks = new Block8x8F[MaxComponents][];
this.Bits = default(Bits); this.Bits = default(Bits);
this.Bytes = Bytes.Create(); this.Bytes = Bytes.Create();
} }
@ -107,6 +107,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// MissingFF00 /// MissingFF00
/// </summary> /// </summary>
// ReSharper disable once InconsistentNaming
MissingFF00 MissingFF00
} }
@ -122,8 +123,9 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Gets the saved state between progressive-mode scans. /// Gets the saved state between progressive-mode scans.
/// TODO: Also store non-progressive data here. (Helps splitting and parallelizing JpegScanDecoder-s loop)
/// </summary> /// </summary>
public Block8x8F[][] ProgCoeffs { get; } public Block8x8F[][] DecodedBlocks { get; }
/// <summary> /// <summary>
/// Gets the quantization tables, in zigzag order. /// Gets the quantization tables, in zigzag order.
@ -131,9 +133,9 @@ namespace ImageSharp.Formats
public Block8x8F[] QuantizationTables { get; } public Block8x8F[] QuantizationTables { get; }
/// <summary> /// <summary>
/// Gets the temporary buffer for holding pixel (and other?) data /// Gets the temporary buffer used to store bytes read from the stream.
/// TODO: Should be stack allocated, fixed sized buffer!
/// </summary> /// </summary>
// TODO: the usage rules of this buffer seem to be unclean + need to consider stack-allocating it for perf
public byte[] Temp { get; } public byte[] Temp { get; }
/// <summary> /// <summary>
@ -1408,6 +1410,13 @@ namespace ImageSharp.Formats
int v0 = this.ComponentArray[0].VerticalFactor; int v0 = this.ComponentArray[0].VerticalFactor;
this.MCUCountX = (this.ImageWidth + (8 * h0) - 1) / (8 * h0); this.MCUCountX = (this.ImageWidth + (8 * h0) - 1) / (8 * h0);
this.MCUCountY = (this.ImageHeight + (8 * v0) - 1) / (8 * v0); this.MCUCountY = (this.ImageHeight + (8 * v0) - 1) / (8 * v0);
for (int i = 0; i < this.ComponentCount; i++)
{
int size = this.TotalMCUCount * this.ComponentArray[i].HorizontalFactor
* this.ComponentArray[i].VerticalFactor;
this.DecodedBlocks[i] = new Block8x8F[size];
}
} }
/// <summary> /// <summary>

Loading…
Cancel
Save