📷 A modern, cross-platform, 2D Graphics library for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

947 lines
36 KiB

// <copyright file="ScanDecoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
using System;
#if DEBUG
using System.Diagnostics;
#endif
using System.IO;
using System.Runtime.CompilerServices;
/// <summary>
/// Provides the means to decode a spectral scan
/// </summary>
internal struct PdfJsScanDecoder
{
private byte[] markerBuffer;
private int bitsData;
private int bitsCount;
#pragma warning disable 414
private int bitsUnRead;
private int accumulator;
#pragma warning restore 414
private int specStart;
private int specEnd;
private int eobrun;
private int compIndex;
private int successiveState;
private int successiveACState;
private int successiveACNextValue;
private bool endOfStreamReached;
private bool unexpectedMarkerReached;
/// <summary>
/// Decodes the spectral scan
/// </summary>
/// <param name="frame">The image frame</param>
/// <param name="stream">The input stream</param>
/// <param name="dcHuffmanTables">The DC Huffman tables</param>
/// <param name="acHuffmanTables">The AC Huffman tables</param>
/// <param name="components">The scan components</param>
/// <param name="componentIndex">The component index within the array</param>
/// <param name="componentsLength">The length of the components. Different to the array length</param>
/// <param name="resetInterval">The reset interval</param>
/// <param name="spectralStart">The spectral selection start</param>
/// <param name="spectralEnd">The spectral selection end</param>
/// <param name="successivePrev">The successive approximation bit high end</param>
/// <param name="successive">The successive approximation bit low end</param>
public void DecodeScan(
PdfJsFrame frame,
Stream stream,
PdfJsHuffmanTables dcHuffmanTables,
PdfJsHuffmanTables acHuffmanTables,
PdfJsFrameComponent[] components,
int componentIndex,
int componentsLength,
ushort resetInterval,
int spectralStart,
int spectralEnd,
int successivePrev,
int successive)
{
this.markerBuffer = new byte[2];
this.compIndex = componentIndex;
this.specStart = spectralStart;
this.specEnd = spectralEnd;
this.successiveState = successive;
this.endOfStreamReached = false;
this.unexpectedMarkerReached = false;
bool progressive = frame.Progressive;
int mcusPerLine = frame.McusPerLine;
int mcu = 0;
int mcuExpected;
if (componentsLength == 1)
{
mcuExpected = components[this.compIndex].BlocksPerLine * components[this.compIndex].BlocksPerColumn;
}
else
{
mcuExpected = mcusPerLine * frame.McusPerColumn;
}
PdfJsFileMarker fileMarker;
while (mcu < mcuExpected)
{
// Reset interval stuff
int mcuToRead = resetInterval != 0 ? Math.Min(mcuExpected - mcu, resetInterval) : mcuExpected;
for (int i = 0; i < components.Length; i++)
{
ref PdfJsFrameComponent c = ref components[i];
c.Pred = 0;
}
this.eobrun = 0;
if (!progressive)
{
this.DecodeScanBaseline(dcHuffmanTables, acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream);
}
else
{
if (this.specStart == 0)
{
if (successivePrev == 0)
{
this.DecodeScanDCFirst(dcHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream);
}
else
{
this.DecodeScanDCSuccessive(components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream);
}
}
else
{
if (successivePrev == 0)
{
this.DecodeScanACFirst(acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream);
}
else
{
this.DecodeScanACSuccessive(acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream);
}
}
}
// Find marker
this.bitsCount = 0;
this.accumulator = 0;
this.bitsUnRead = 0;
fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream);
// Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past
// those to attempt to find a valid marker (fixes issue4090.pdf) in original code.
if (fileMarker.Invalid)
{
#if DEBUG
Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}");
#endif
}
ushort marker = fileMarker.Marker;
// RSTn - We've alread read the bytes and altered the position so no need to skip
if (marker >= PdfJsJpegConstants.Markers.RST0 && marker <= PdfJsJpegConstants.Markers.RST7)
{
continue;
}
if (!fileMarker.Invalid)
{
// We've found a valid marker.
// Rewind the stream to the position of the marker and break
stream.Position = fileMarker.Position;
break;
}
}
fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream);
// Some images include more Scan blocks than expected, skip past those and
// attempt to find the next valid marker (fixes issue8182.pdf) in original code.
if (fileMarker.Invalid)
{
#if DEBUG
Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}");
#endif
}
else
{
// We've found a valid marker.
// Rewind the stream to the position of the marker
stream.Position = fileMarker.Position;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetBlockBufferOffset(PdfJsFrameComponent component, int row, int col)
{
return 64 * (((component.BlocksPerLine + 1) * row) + col);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanBaseline(
PdfJsHuffmanTables dcHuffmanTables,
PdfJsHuffmanTables acHuffmanTables,
PdfJsFrameComponent[] components,
int componentsLength,
int mcusPerLine,
int mcuToRead,
ref int mcu,
Stream stream)
{
if (componentsLength == 1)
{
ref PdfJsFrameComponent component = ref components[this.compIndex];
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
for (int n = 0; n < mcuToRead; n++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeBlockBaseline(ref dcHuffmanTable, ref acHuffmanTable, ref component, mcu, stream);
mcu++;
}
}
else
{
for (int n = 0; n < mcuToRead; n++)
{
for (int i = 0; i < componentsLength; i++)
{
ref PdfJsFrameComponent component = ref components[i];
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
int h = component.HorizontalFactor;
int v = component.VerticalFactor;
for (int j = 0; j < v; j++)
{
for (int k = 0; k < h; k++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeMcuBaseline(ref dcHuffmanTable, ref acHuffmanTable, ref component, mcusPerLine, mcu, j, k, stream);
}
}
}
mcu++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanDCFirst(
PdfJsHuffmanTables dcHuffmanTables,
PdfJsFrameComponent[] components,
int componentsLength,
int mcusPerLine,
int mcuToRead,
ref int mcu,
Stream stream)
{
if (componentsLength == 1)
{
ref PdfJsFrameComponent component = ref components[this.compIndex];
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
for (int n = 0; n < mcuToRead; n++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeBlockDCFirst(ref dcHuffmanTable, ref component, mcu, stream);
mcu++;
}
}
else
{
for (int n = 0; n < mcuToRead; n++)
{
for (int i = 0; i < componentsLength; i++)
{
ref PdfJsFrameComponent component = ref components[i];
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
int h = component.HorizontalFactor;
int v = component.VerticalFactor;
for (int j = 0; j < v; j++)
{
for (int k = 0; k < h; k++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeMcuDCFirst(ref dcHuffmanTable, ref component, mcusPerLine, mcu, j, k, stream);
}
}
}
mcu++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanDCSuccessive(
PdfJsFrameComponent[] components,
int componentsLength,
int mcusPerLine,
int mcuToRead,
ref int mcu,
Stream stream)
{
if (componentsLength == 1)
{
ref PdfJsFrameComponent component = ref components[this.compIndex];
for (int n = 0; n < mcuToRead; n++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeBlockDCSuccessive(ref component, mcu, stream);
mcu++;
}
}
else
{
for (int n = 0; n < mcuToRead; n++)
{
for (int i = 0; i < componentsLength; i++)
{
ref PdfJsFrameComponent component = ref components[i];
int h = component.HorizontalFactor;
int v = component.VerticalFactor;
for (int j = 0; j < v; j++)
{
for (int k = 0; k < h; k++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeMcuDCSuccessive(ref component, mcusPerLine, mcu, j, k, stream);
}
}
}
mcu++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanACFirst(
PdfJsHuffmanTables acHuffmanTables,
PdfJsFrameComponent[] components,
int componentsLength,
int mcusPerLine,
int mcuToRead,
ref int mcu,
Stream stream)
{
if (componentsLength == 1)
{
ref PdfJsFrameComponent component = ref components[this.compIndex];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
for (int n = 0; n < mcuToRead; n++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeBlockACFirst(ref acHuffmanTable, ref component, mcu, stream);
mcu++;
}
}
else
{
for (int n = 0; n < mcuToRead; n++)
{
for (int i = 0; i < componentsLength; i++)
{
ref PdfJsFrameComponent component = ref components[i];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
int h = component.HorizontalFactor;
int v = component.VerticalFactor;
for (int j = 0; j < v; j++)
{
for (int k = 0; k < h; k++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeMcuACFirst(ref acHuffmanTable, ref component, mcusPerLine, mcu, j, k, stream);
}
}
}
mcu++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanACSuccessive(
PdfJsHuffmanTables acHuffmanTables,
PdfJsFrameComponent[] components,
int componentsLength,
int mcusPerLine,
int mcuToRead,
ref int mcu,
Stream stream)
{
if (componentsLength == 1)
{
ref PdfJsFrameComponent component = ref components[this.compIndex];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
for (int n = 0; n < mcuToRead; n++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeBlockACSuccessive(ref acHuffmanTable, ref component, mcu, stream);
mcu++;
}
}
else
{
for (int n = 0; n < mcuToRead; n++)
{
for (int i = 0; i < componentsLength; i++)
{
ref PdfJsFrameComponent component = ref components[i];
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
int h = component.HorizontalFactor;
int v = component.VerticalFactor;
for (int j = 0; j < v; j++)
{
for (int k = 0; k < h; k++)
{
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
continue;
}
this.DecodeMcuACSuccessive(ref acHuffmanTable, ref component, mcusPerLine, mcu, j, k, stream);
}
}
}
mcu++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcu, Stream stream)
{
int blockRow = mcu / component.BlocksPerLine;
int blockCol = mcu % component.BlocksPerLine;
int offset = GetBlockBufferOffset(component, blockRow, blockCol);
this.DecodeBaseline(ref component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalFactor) + row;
int blockCol = (mcuCol * component.HorizontalFactor) + col;
int offset = GetBlockBufferOffset(component, blockRow, blockCol);
this.DecodeBaseline(ref component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsFrameComponent component, int mcu, Stream stream)
{
int blockRow = mcu / component.BlocksPerLine;
int blockCol = mcu % component.BlocksPerLine;
int offset = GetBlockBufferOffset(component, blockRow, blockCol);
this.DecodeDCFirst(ref component, offset, ref dcHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalFactor) + row;
int blockCol = (mcuCol * component.HorizontalFactor) + col;
int offset = GetBlockBufferOffset(component, blockRow, blockCol);
this.DecodeDCFirst(ref component, offset, ref dcHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockDCSuccessive(ref PdfJsFrameComponent component, int mcu, Stream stream)
{
int blockRow = mcu / component.BlocksPerLine;
int blockCol = mcu % component.BlocksPerLine;
int offset = GetBlockBufferOffset(component, blockRow, blockCol);
this.DecodeDCSuccessive(ref component, offset, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuDCSuccessive(ref PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalFactor) + row;
int blockCol = (mcuCol * component.HorizontalFactor) + col;
int offset = GetBlockBufferOffset(component, blockRow, blockCol);
this.DecodeDCSuccessive(ref component, offset, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockACFirst(ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcu, Stream stream)
{
int blockRow = mcu / component.BlocksPerLine;
int blockCol = mcu % component.BlocksPerLine;
int offset = GetBlockBufferOffset(component, blockRow, blockCol);
this.DecodeACFirst(ref component, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuACFirst(ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalFactor) + row;
int blockCol = (mcuCol * component.HorizontalFactor) + col;
int offset = GetBlockBufferOffset(component, blockRow, blockCol);
this.DecodeACFirst(ref component, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBlockACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcu, Stream stream)
{
int blockRow = mcu / component.BlocksPerLine;
int blockCol = mcu % component.BlocksPerLine;
int offset = GetBlockBufferOffset(component, blockRow, blockCol);
this.DecodeACSuccessive(ref component, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeMcuACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, ref PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream)
{
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalFactor) + row;
int blockCol = (mcuCol * component.HorizontalFactor) + col;
int offset = GetBlockBufferOffset(component, blockRow, blockCol);
this.DecodeACSuccessive(ref component, offset, ref acHuffmanTable, stream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ReadBit(Stream stream)
{
// TODO: I wonder if we can do this two bytes at a time; libjpeg turbo seems to do that?
if (this.bitsCount > 0)
{
this.bitsCount--;
return (this.bitsData >> this.bitsCount) & 1;
}
this.bitsData = stream.ReadByte();
if (this.bitsData == -0x1)
{
// We've encountered the end of the file stream which means there's no EOI marker in the image
this.endOfStreamReached = true;
}
if (this.bitsData == PdfJsJpegConstants.Markers.Prefix)
{
int nextByte = stream.ReadByte();
if (nextByte != 0)
{
#if DEBUG
Debug.WriteLine($"DecodeScan - Unexpected marker {(this.bitsData << 8) | nextByte:X} at {stream.Position}");
#endif
// We've encountered an unexpected marker. Reverse the stream and exit.
this.unexpectedMarkerReached = true;
stream.Position -= 2;
}
// Unstuff 0
}
this.bitsCount = 7;
return this.bitsData >> 7;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private short DecodeHuffman(ref PdfJsHuffmanTable tree, Stream stream)
{
short code = -1;
// TODO: Adding this code introduces error into the decoder.
// NOTES # During investigation of the libjpeg implementation it appears that they pull 32bits at a time and operate on those bits
// using 3 methods: FillBits, PeekBits, and ReadBits. We should attempt to do the same.
// It doesn't appear to speed anything up either.
// if (this.bitsUnRead < 8)
// {
// if (this.bitsCount <= 0)
// {
// code = (short)this.ReadBit(stream);
// if (this.endOfStreamReached || this.unexpectedMarkerReached)
// {
// return -1;
// }
//
// this.bitsUnRead += 8;
// }
//
// this.accumulator = (this.accumulator << 8) | this.bitsData;
// int lutIndex = (this.accumulator >> (8 - this.bitsUnRead)) & 0xFF;
// int v = tree.Lookahead[lutIndex];
// if (v != 0)
// {
// int nb = (v & 0xFF) - 1;
// this.bitsCount -= nb - 1;
// this.bitsUnRead -= nb;
// v = v >> 8;
// return (short)v;
// }
// }
if (code == -1)
{
code = (short)this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return -1;
}
}
// "DECODE", section F.2.2.3, figure F.16, page 109 of T.81
int i = 1;
while (code > tree.MaxCode[i])
{
code <<= 1;
code |= (short)this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return -1;
}
i++;
}
int j = tree.ValOffset[i];
return tree.HuffVal[(j + code) & 0xFF];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int Receive(int length, Stream stream)
{
int n = 0;
while (length > 0)
{
int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return -1;
}
n = (n << 1) | bit;
length--;
}
return n;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ReceiveAndExtend(int length, Stream stream)
{
if (length == 1)
{
return this.ReadBit(stream) == 1 ? 1 : -1;
}
int n = this.Receive(length, stream);
if (n >= 1 << (length - 1))
{
return n;
}
return n + (-1 << length) + 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeBaseline(ref PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream)
{
int t = this.DecodeHuffman(ref dcHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream);
component.BlockData[offset] = (short)(component.Pred += diff);
int k = 1;
while (k < 64)
{
int rs = this.DecodeHuffman(ref acHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
int s = rs & 15;
int r = rs >> 4;
if (s == 0)
{
if (r < 15)
{
break;
}
k += 16;
continue;
}
k += r;
if (k > 63)
{
break;
}
byte z = PdfJsQuantizationTables.DctZigZag[k];
short re = (short)this.ReceiveAndExtend(s, stream);
component.BlockData[offset + z] = re;
k++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCFirst(ref PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream)
{
int t = this.DecodeHuffman(ref dcHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream) << this.successiveState;
component.BlockData[offset] = (short)(component.Pred += diff);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeDCSuccessive(ref PdfJsFrameComponent component, int offset, Stream stream)
{
int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
component.BlockData[offset] |= (short)(bit << this.successiveState);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeACFirst(ref PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream)
{
if (this.eobrun > 0)
{
this.eobrun--;
return;
}
Span<short> componentBlockDataSpan = component.BlockData.Span;
int k = this.specStart;
int e = this.specEnd;
while (k <= e)
{
short rs = this.DecodeHuffman(ref acHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
int s = rs & 15;
int r = rs >> 4;
if (s == 0)
{
if (r < 15)
{
this.eobrun = this.Receive(r, stream) + (1 << r) - 1;
break;
}
k += 16;
continue;
}
k += r;
byte z = PdfJsQuantizationTables.DctZigZag[k];
componentBlockDataSpan[offset + z] = (short)(this.ReceiveAndExtend(s, stream) * (1 << this.successiveState));
k++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeACSuccessive(ref PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream)
{
int k = this.specStart;
int e = this.specEnd;
int r = 0;
Span<short> componentBlockDataSpan = component.BlockData.Span;
while (k <= e)
{
byte z = PdfJsQuantizationTables.DctZigZag[k];
switch (this.successiveACState)
{
case 0: // Initial state
short rs = this.DecodeHuffman(ref acHuffmanTable, stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
int s = rs & 15;
r = rs >> 4;
if (s == 0)
{
if (r < 15)
{
this.eobrun = this.Receive(r, stream) + (1 << r);
this.successiveACState = 4;
}
else
{
r = 16;
this.successiveACState = 1;
}
}
else
{
if (s != 1)
{
throw new ImageFormatException("Invalid ACn encoding");
}
this.successiveACNextValue = this.ReceiveAndExtend(s, stream);
this.successiveACState = r > 0 ? 2 : 3;
}
continue;
case 1: // Skipping r zero items
case 2:
if (componentBlockDataSpan[offset + z] != 0)
{
int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState);
}
else
{
r--;
if (r == 0)
{
this.successiveACState = this.successiveACState == 2 ? 3 : 0;
}
}
break;
case 3: // Set value for a zero item
if (componentBlockDataSpan[offset + z] != 0)
{
int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState);
}
else
{
componentBlockDataSpan[offset + z] = (short)(this.successiveACNextValue << this.successiveState);
this.successiveACState = 0;
}
break;
case 4: // Eob
if (componentBlockDataSpan[offset + z] != 0)
{
int bit = this.ReadBit(stream);
if (this.endOfStreamReached || this.unexpectedMarkerReached)
{
return;
}
componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState);
}
break;
}
k++;
}
if (this.successiveACState == 4)
{
this.eobrun--;
if (this.eobrun == 0)
{
this.successiveACState = 0;
}
}
}
}
}