Browse Source

Refactor FastACTables and reduce trivial duplication.

pull/643/head
James Jackson-South 8 years ago
parent
commit
fcabcdd5d5
  1. 18
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs
  2. 106
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs
  3. 19
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

18
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
@ -11,24 +12,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary>
internal sealed class FastACTables : IDisposable
{
private Buffer2D<short> tables;
/// <summary>
/// Initializes a new instance of the <see cref="FastACTables"/> class.
/// </summary>
/// <param name="memoryAllocator">The memory allocator used to allocate memory for image processing operations.</param>
public FastACTables(MemoryAllocator memoryAllocator)
{
this.Tables = memoryAllocator.AllocateClean2D<short>(512, 4);
this.tables = memoryAllocator.AllocateClean2D<short>(512, 4);
}
/// <summary>
/// Gets the collection of tables.
/// Gets the <see cref="Span{Int16}"/> representing the table at the index in the collection.
/// </summary>
public Buffer2D<short> Tables { get; }
/// <param name="index">The table index.</param>
/// <returns><see cref="Span{Int16}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<short> GetTableSpan(int index)
{
return this.tables.GetRowSpan(index);
}
/// <inheritdoc />
public void Dispose()
{
this.Tables?.Dispose();
this.tables?.Dispose();
this.tables = null;
}
}
}

106
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs

@ -4,10 +4,14 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Decodes the Huffman encoded spectral scan.
/// Originally ported from <see href="https://github.com/rds1983/StbSharp"/>
/// with additional fixes for both performance and common encoding errors.
/// </summary>
internal class ScanDecoder
{
// The number of bits that can be read via a LUT.
@ -157,7 +161,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
ref short fastACRef = ref MemoryMarshal.GetReference(fastACTables.Tables.GetRowSpan(component.ACHuffmanTableId));
ref short fastACRef = ref MemoryMarshal.GetReference(fastACTables.GetTableSpan(component.ACHuffmanTableId));
int mcu = 0;
for (int j = 0; j < h; j++)
@ -173,24 +177,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
int blockCol = mcu % w;
int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeBlock(component, ref Unsafe.Add(ref blockDataRef, offset), ref dcHuffmanTable, ref acHuffmanTable, ref fastACRef);
mcu++;
// Every data block is an MCU, so countdown the restart interval
if (--this.todo <= 0)
mcu++;
if (!this.ContinueOnMcuComplete())
{
if (this.codeBits < 24)
{
this.FillBuffer();
}
// If it's NOT a restart, then just bail, so we get corrupt data
// rather than no data
if (!this.ContinueOnRestart())
{
return;
}
this.Reset();
return;
}
}
}
@ -212,7 +204,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
ref short fastACRef = ref MemoryMarshal.GetReference(fastACTables.Tables.GetRowSpan(component.ACHuffmanTableId));
ref short fastACRef = ref MemoryMarshal.GetReference(fastACTables.GetTableSpan(component.ACHuffmanTableId));
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
@ -240,21 +232,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
// After all interleaved components, that's an interleaved MCU,
// so now count down the restart interval
mcu++;
if (--this.todo <= 0)
if (!this.ContinueOnMcuComplete())
{
if (this.codeBits < 24)
{
this.FillBuffer();
}
// If it's NOT a restart, then just bail, so we get corrupt data
// rather than no data
if (!this.ContinueOnRestart())
{
return;
}
this.Reset();
return;
}
}
}
@ -283,7 +263,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
ref short fastACRef = ref MemoryMarshal.GetReference(fastACTables.Tables.GetRowSpan(component.ACHuffmanTableId));
ref short fastACRef = ref MemoryMarshal.GetReference(fastACTables.GetTableSpan(component.ACHuffmanTableId));
int mcu = 0;
for (int j = 0; j < h; j++)
@ -308,24 +288,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.DecodeBlockProgressiveAC(ref Unsafe.Add(ref blockDataRef, offset), ref acHuffmanTable, ref fastACRef);
}
mcu++;
// Every data block is an MCU, so countdown the restart interval
if (--this.todo <= 0)
mcu++;
if (!this.ContinueOnMcuComplete())
{
if (this.codeBits < 24)
{
this.FillBuffer();
}
// If it's NOT a restart, then just bail, so we get corrupt data
// rather than no data
if (!this.ContinueOnRestart())
{
return;
}
this.Reset();
return;
}
}
}
@ -373,21 +340,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
// After all interleaved components, that's an interleaved MCU,
// so now count down the restart interval
mcu++;
if (--this.todo <= 0)
if (!this.ContinueOnMcuComplete())
{
if (this.codeBits < 24)
{
this.FillBuffer();
}
// If it's NOT a restart, then just bail, so we get corrupt data
// rather than no data
if (!this.ContinueOnRestart())
{
return;
}
this.Reset();
return;
}
}
}
@ -887,14 +842,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
private int PeekBits() => (int)((this.codeBuffer >> (32 - FastBits)) & ((1 << FastBits) - 1));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool ContinueOnRestart()
private bool ContinueOnMcuComplete()
{
if (--this.todo > 0)
{
return true;
}
if (this.codeBits < 24)
{
this.FillBuffer();
}
// If it's NOT a restart, then just bail, so we get corrupt data rather than no data.
// Reset the stream to before any bad markers to ensure we can read sucessive segments.
if (this.badMarker)
{
this.stream.Position = this.markerPosition;
}
return this.HasRestart();
if (!this.HasRestart())
{
return false;
}
this.Reset();
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -904,7 +878,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
return m >= JpegConstants.Markers.RST0 && m <= JpegConstants.Markers.RST7;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
private void Reset()
{
this.codeBits = 0;

19
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

@ -842,6 +842,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
return BinaryPrimitives.ReadUInt16BigEndian(this.markerBuffer);
}
/// <summary>
/// Post processes the pixels into the destination image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
private Image<TPixel> PostProcessIntoImage<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
@ -853,18 +858,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
}
/// <summary>
/// Builds a lookup table for fast AC entropy scan decoding.
/// </summary>
/// <param name="index">The table index.</param>
private void BuildFastACTable(int index)
{
const int FastBits = ScanDecoder.FastBits;
Span<short> fastac = this.fastACTables.Tables.GetRowSpan(index);
Span<short> fastAC = this.fastACTables.GetTableSpan(index);
ref PdfJsHuffmanTable huffman = ref this.acHuffmanTables[index];
int i;
for (i = 0; i < (1 << FastBits); i++)
{
byte fast = huffman.Lookahead[i];
fastac[i] = 0;
if (fast < 255)
fastAC[i] = 0;
if (fast < byte.MaxValue)
{
int rs = huffman.Values[fast];
int run = (rs >> 4) & 15;
@ -881,10 +890,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
k += (int)((~0U << magbits) + 1);
}
// if the result is small enough, we can fit it in fastac table
// if the result is small enough, we can fit it in fastAC table
if (k >= -128 && k <= 127)
{
fastac[i] = (short)((k * 256) + (run * 16) + (len + magbits));
fastAC[i] = (short)((k * 256) + (run * 16) + (len + magbits));
}
}
}

Loading…
Cancel
Save