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. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Runtime.CompilerServices;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
@ -11,24 +12,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary> /// </summary>
internal sealed class FastACTables : IDisposable internal sealed class FastACTables : IDisposable
{ {
private Buffer2D<short> tables;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FastACTables"/> class. /// Initializes a new instance of the <see cref="FastACTables"/> class.
/// </summary> /// </summary>
/// <param name="memoryAllocator">The memory allocator used to allocate memory for image processing operations.</param> /// <param name="memoryAllocator">The memory allocator used to allocate memory for image processing operations.</param>
public FastACTables(MemoryAllocator memoryAllocator) public FastACTables(MemoryAllocator memoryAllocator)
{ {
this.Tables = memoryAllocator.AllocateClean2D<short>(512, 4); this.tables = memoryAllocator.AllocateClean2D<short>(512, 4);
} }
/// <summary> /// <summary>
/// Gets the collection of tables. /// Gets the <see cref="Span{Int16}"/> representing the table at the index in the collection.
/// </summary> /// </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 /> /// <inheritdoc />
public void Dispose() 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.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components 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 internal class ScanDecoder
{ {
// The number of bits that can be read via a LUT. // 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 short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; 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; int mcu = 0;
for (int j = 0; j < h; j++) for (int j = 0; j < h; j++)
@ -173,24 +177,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
int blockCol = mcu % w; int blockCol = mcu % w;
int offset = component.GetBlockBufferOffset(blockRow, blockCol); int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeBlock(component, ref Unsafe.Add(ref blockDataRef, offset), ref dcHuffmanTable, ref acHuffmanTable, ref fastACRef); 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 // Every data block is an MCU, so countdown the restart interval
if (--this.todo <= 0) mcu++;
if (!this.ContinueOnMcuComplete())
{ {
if (this.codeBits < 24) return;
{
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();
} }
} }
} }
@ -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 short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; 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 h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor; int v = component.VerticalSamplingFactor;
@ -240,21 +232,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
// After all interleaved components, that's an interleaved MCU, // After all interleaved components, that's an interleaved MCU,
// so now count down the restart interval // so now count down the restart interval
mcu++; mcu++;
if (--this.todo <= 0) if (!this.ContinueOnMcuComplete())
{ {
if (this.codeBits < 24) return;
{
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();
} }
} }
} }
@ -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 short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; 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; int mcu = 0;
for (int j = 0; j < h; j++) 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); this.DecodeBlockProgressiveAC(ref Unsafe.Add(ref blockDataRef, offset), ref acHuffmanTable, ref fastACRef);
} }
mcu++;
// Every data block is an MCU, so countdown the restart interval // Every data block is an MCU, so countdown the restart interval
if (--this.todo <= 0) mcu++;
if (!this.ContinueOnMcuComplete())
{ {
if (this.codeBits < 24) return;
{
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();
} }
} }
} }
@ -373,21 +340,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
// After all interleaved components, that's an interleaved MCU, // After all interleaved components, that's an interleaved MCU,
// so now count down the restart interval // so now count down the restart interval
mcu++; mcu++;
if (--this.todo <= 0) if (!this.ContinueOnMcuComplete())
{ {
if (this.codeBits < 24) return;
{
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();
} }
} }
} }
@ -887,14 +842,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
private int PeekBits() => (int)((this.codeBuffer >> (32 - FastBits)) & ((1 << FastBits) - 1)); private int PeekBits() => (int)((this.codeBuffer >> (32 - FastBits)) & ((1 << FastBits) - 1));
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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) if (this.badMarker)
{ {
this.stream.Position = this.markerPosition; this.stream.Position = this.markerPosition;
} }
return this.HasRestart(); if (!this.HasRestart())
{
return false;
}
this.Reset();
return true;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -904,7 +878,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
return m >= JpegConstants.Markers.RST0 && m <= JpegConstants.Markers.RST7; return m >= JpegConstants.Markers.RST0 && m <= JpegConstants.Markers.RST7;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
private void Reset() private void Reset()
{ {
this.codeBits = 0; 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); 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>() private Image<TPixel> PostProcessIntoImage<TPixel>()
where TPixel : struct, IPixel<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) private void BuildFastACTable(int index)
{ {
const int FastBits = ScanDecoder.FastBits; 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]; ref PdfJsHuffmanTable huffman = ref this.acHuffmanTables[index];
int i; int i;
for (i = 0; i < (1 << FastBits); i++) for (i = 0; i < (1 << FastBits); i++)
{ {
byte fast = huffman.Lookahead[i]; byte fast = huffman.Lookahead[i];
fastac[i] = 0; fastAC[i] = 0;
if (fast < 255) if (fast < byte.MaxValue)
{ {
int rs = huffman.Values[fast]; int rs = huffman.Values[fast];
int run = (rs >> 4) & 15; int run = (rs >> 4) & 15;
@ -881,10 +890,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
k += (int)((~0U << magbits) + 1); 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) 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