Browse Source

Feeble attempt to introduce postprocessor.

Component spectral data layout is incorrect here.
pull/525/head
James Jackson-South 8 years ago
parent
commit
d643b21f87
  1. 48
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs
  2. 243
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs
  3. 26
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
  4. 139
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  5. 12
      tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs

48
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs

@ -49,24 +49,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary>
public int VerticalSamplingFactor { get; }
Buffer2D<Block8x8> IJpegComponent.SpectralBlocks => throw new NotImplementedException();
/// <inheritdoc />
public Buffer2D<Block8x8> SpectralBlocks { get; private set; }
// TODO: Should be derived from PdfJsComponent.Scale
public Size SubSamplingDivisors => throw new NotImplementedException();
/// <inheritdoc />
public Size SubSamplingDivisors { get; private set; }
/// <inheritdoc />
public int QuantizationTableIndex { get; }
/// <summary>
/// Gets the block data
/// </summary>
public IBuffer<short> BlockData { get; private set; }
/// <inheritdoc />
public int Index { get; }
/// <inheritdoc />
public Size SizeInBlocks => new Size(this.WidthInBlocks, this.HeightInBlocks);
/// <inheritdoc />
public Size SamplingFactors => new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor);
/// <summary>
@ -98,8 +96,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// <inheritdoc/>
public void Dispose()
{
this.BlockData?.Dispose();
this.BlockData = null;
this.SpectralBlocks?.Dispose();
this.SpectralBlocks = null;
}
public void Init()
@ -113,10 +111,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.BlocksPerLineForMcu = this.Frame.McusPerLine * this.HorizontalSamplingFactor;
this.BlocksPerColumnForMcu = this.Frame.McusPerColumn * this.VerticalSamplingFactor;
int blocksBufferSize = 64 * this.BlocksPerColumnForMcu * (this.BlocksPerLineForMcu + 1);
// Pooled. Disposed via frame disposal
this.BlockData = this.memoryManager.Allocate<short>(blocksBufferSize, true);
// For 4-component images (either CMYK or YCbCrK), we only support two
// hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22].
// Theoretically, 4-component JPEG images could mix and match hv values
// but in practice, those two combinations are the only ones in use,
// and it simplifies the applyBlack code below if we can assume that:
// - for CMYK, the C and K channels have full samples, and if the M
// and Y channels subsample, they subsample both horizontally and
// vertically.
// - for YCbCrK, the Y and K channels have full samples.
if (this.Index == 0 || this.Index == 3)
{
this.SubSamplingDivisors = new Size(1, 1);
}
else
{
// TODO: Check division accuracy here. May need to divide by float
this.SubSamplingDivisors = this.SamplingFactors.DivideBy(new Size(this.Frame.MaxHorizontalFactor, this.Frame.MaxVerticalFactor));
}
this.SpectralBlocks = this.memoryManager.Allocate2D<Block8x8>(this.SizeInBlocks.Width, this.SizeInBlocks.Height, true);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -124,11 +138,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
return 64 * (((this.WidthInBlocks + 1) * row) + col);
}
public Span<short> GetBlockBuffer(int row, int col)
{
int offset = this.GetBlockBufferOffset(row, col);
return this.BlockData.Span.Slice(offset, 64);
}
}
}

243
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs

@ -1,243 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
/// <summary>
/// Performs the inverse Descrete Cosine Transform on each frame component.
/// </summary>
internal static class PdfJsIDCT
{
private const int DctCos1 = 4017; // cos(pi/16)
private const int DctSin1 = 799; // sin(pi/16)
private const int DctCos3 = 3406; // cos(3*pi/16)
private const int DctSin3 = 2276; // sin(3*pi/16)
private const int DctCos6 = 1567; // cos(6*pi/16)
private const int DctSin6 = 3784; // sin(6*pi/16)
private const int DctSqrt2 = 5793; // sqrt(2)
private const int DctSqrt1D2 = 2896; // sqrt(2) / 2
private const int MaxJSample = 255;
private const int CenterJSample = 128;
/// <summary>
/// A port of Poppler's IDCT method which in turn is taken from:
/// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
/// 'Practical Fast 1-D DCT Algorithms with 11 Multiplications',
/// IEEE Intl. Conf. on Acoustics, Speech &amp; Signal Processing, 1989, 988-991.
/// </summary>
/// <param name="component">The frame component</param>
/// <param name="blockBufferOffset">The block buffer offset</param>
/// <param name="computationBufferRef">The computational buffer for holding temp values ref</param>
/// <param name="quantizationTableRef">The quantization table ref</param>
public static void QuantizeAndInverse(PdfJsFrameComponent component, int blockBufferOffset, ref short computationBufferRef, ref short quantizationTableRef)
{
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Slice(blockBufferOffset));
int v0, v1, v2, v3, v4, v5, v6, v7;
int p0, p1, p2, p3, p4, p5, p6, p7;
int t;
// inverse DCT on rows
for (int row = 0; row < 64; row += 8)
{
int r1 = row + 1;
int r2 = row + 2;
int r3 = row + 3;
int r4 = row + 4;
int r5 = row + 5;
int r6 = row + 6;
int r7 = row + 7;
// gather block data
p0 = Unsafe.Add(ref blockDataRef, row);
p1 = Unsafe.Add(ref blockDataRef, r1);
p2 = Unsafe.Add(ref blockDataRef, r2);
p3 = Unsafe.Add(ref blockDataRef, r3);
p4 = Unsafe.Add(ref blockDataRef, r4);
p5 = Unsafe.Add(ref blockDataRef, r5);
p6 = Unsafe.Add(ref blockDataRef, r6);
p7 = Unsafe.Add(ref blockDataRef, r7);
// dequant p0
p0 *= Unsafe.Add(ref quantizationTableRef, row);
// check for all-zero AC coefficients
if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0)
{
t = ((DctSqrt2 * p0) + 512) >> 10;
short st = (short)t;
Unsafe.Add(ref computationBufferRef, row) = st;
Unsafe.Add(ref computationBufferRef, r1) = st;
Unsafe.Add(ref computationBufferRef, r2) = st;
Unsafe.Add(ref computationBufferRef, r3) = st;
Unsafe.Add(ref computationBufferRef, r4) = st;
Unsafe.Add(ref computationBufferRef, r5) = st;
Unsafe.Add(ref computationBufferRef, r6) = st;
Unsafe.Add(ref computationBufferRef, r7) = st;
continue;
}
// dequant p1 ... p7
p1 *= Unsafe.Add(ref quantizationTableRef, r1);
p2 *= Unsafe.Add(ref quantizationTableRef, r2);
p3 *= Unsafe.Add(ref quantizationTableRef, r3);
p4 *= Unsafe.Add(ref quantizationTableRef, r4);
p5 *= Unsafe.Add(ref quantizationTableRef, r5);
p6 *= Unsafe.Add(ref quantizationTableRef, r6);
p7 *= Unsafe.Add(ref quantizationTableRef, r7);
// stage 4
v0 = ((DctSqrt2 * p0) + CenterJSample) >> 8;
v1 = ((DctSqrt2 * p4) + CenterJSample) >> 8;
v2 = p2;
v3 = p6;
v4 = ((DctSqrt1D2 * (p1 - p7)) + CenterJSample) >> 8;
v7 = ((DctSqrt1D2 * (p1 + p7)) + CenterJSample) >> 8;
v5 = p3 << 4;
v6 = p5 << 4;
// stage 3
v0 = (v0 + v1 + 1) >> 1;
v1 = v0 - v1;
t = ((v2 * DctSin6) + (v3 * DctCos6) + CenterJSample) >> 8;
v2 = ((v2 * DctCos6) - (v3 * DctSin6) + CenterJSample) >> 8;
v3 = t;
v4 = (v4 + v6 + 1) >> 1;
v6 = v4 - v6;
v7 = (v7 + v5 + 1) >> 1;
v5 = v7 - v5;
// stage 2
v0 = (v0 + v3 + 1) >> 1;
v3 = v0 - v3;
v1 = (v1 + v2 + 1) >> 1;
v2 = v1 - v2;
t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12;
v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12;
v7 = t;
t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12;
v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12;
v6 = t;
// stage 1
Unsafe.Add(ref computationBufferRef, row) = (short)(v0 + v7);
Unsafe.Add(ref computationBufferRef, row + 7) = (short)(v0 - v7);
Unsafe.Add(ref computationBufferRef, row + 1) = (short)(v1 + v6);
Unsafe.Add(ref computationBufferRef, row + 6) = (short)(v1 - v6);
Unsafe.Add(ref computationBufferRef, row + 2) = (short)(v2 + v5);
Unsafe.Add(ref computationBufferRef, row + 5) = (short)(v2 - v5);
Unsafe.Add(ref computationBufferRef, row + 3) = (short)(v3 + v4);
Unsafe.Add(ref computationBufferRef, row + 4) = (short)(v3 - v4);
}
// inverse DCT on columns
for (int col = 0; col < 8; ++col)
{
int c8 = col + 8;
int c16 = col + 16;
int c24 = col + 24;
int c32 = col + 32;
int c40 = col + 40;
int c48 = col + 48;
int c56 = col + 56;
p0 = Unsafe.Add(ref computationBufferRef, col);
p1 = Unsafe.Add(ref computationBufferRef, c8);
p2 = Unsafe.Add(ref computationBufferRef, c16);
p3 = Unsafe.Add(ref computationBufferRef, c24);
p4 = Unsafe.Add(ref computationBufferRef, c32);
p5 = Unsafe.Add(ref computationBufferRef, c40);
p6 = Unsafe.Add(ref computationBufferRef, c48);
p7 = Unsafe.Add(ref computationBufferRef, c56);
// check for all-zero AC coefficients
if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0)
{
t = ((DctSqrt2 * p0) + 8192) >> 14;
// convert to 8 bit
t = (t < -2040) ? 0 : (t >= 2024) ? MaxJSample : (t + 2056) >> 4;
short st = (short)t;
Unsafe.Add(ref blockDataRef, col) = st;
Unsafe.Add(ref blockDataRef, c8) = st;
Unsafe.Add(ref blockDataRef, c16) = st;
Unsafe.Add(ref blockDataRef, c24) = st;
Unsafe.Add(ref blockDataRef, c32) = st;
Unsafe.Add(ref blockDataRef, c40) = st;
Unsafe.Add(ref blockDataRef, c48) = st;
Unsafe.Add(ref blockDataRef, c56) = st;
continue;
}
// stage 4
v0 = ((DctSqrt2 * p0) + 2048) >> 12;
v1 = ((DctSqrt2 * p4) + 2048) >> 12;
v2 = p2;
v3 = p6;
v4 = ((DctSqrt1D2 * (p1 - p7)) + 2048) >> 12;
v7 = ((DctSqrt1D2 * (p1 + p7)) + 2048) >> 12;
v5 = p3;
v6 = p5;
// stage 3
// Shift v0 by 128.5 << 5 here, so we don't need to shift p0...p7 when
// converting to UInt8 range later.
v0 = ((v0 + v1 + 1) >> 1) + 4112;
v1 = v0 - v1;
t = ((v2 * DctSin6) + (v3 * DctCos6) + 2048) >> 12;
v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 2048) >> 12;
v3 = t;
v4 = (v4 + v6 + 1) >> 1;
v6 = v4 - v6;
v7 = (v7 + v5 + 1) >> 1;
v5 = v7 - v5;
// stage 2
v0 = (v0 + v3 + 1) >> 1;
v3 = v0 - v3;
v1 = (v1 + v2 + 1) >> 1;
v2 = v1 - v2;
t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12;
v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12;
v7 = t;
t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12;
v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12;
v6 = t;
// stage 1
p0 = v0 + v7;
p7 = v0 - v7;
p1 = v1 + v6;
p6 = v1 - v6;
p2 = v2 + v5;
p5 = v2 - v5;
p3 = v3 + v4;
p4 = v3 - v4;
// convert to 8-bit integers
p0 = (p0 < 16) ? 0 : (p0 >= 4080) ? MaxJSample : p0 >> 4;
p1 = (p1 < 16) ? 0 : (p1 >= 4080) ? MaxJSample : p1 >> 4;
p2 = (p2 < 16) ? 0 : (p2 >= 4080) ? MaxJSample : p2 >> 4;
p3 = (p3 < 16) ? 0 : (p3 >= 4080) ? MaxJSample : p3 >> 4;
p4 = (p4 < 16) ? 0 : (p4 >= 4080) ? MaxJSample : p4 >> 4;
p5 = (p5 < 16) ? 0 : (p5 >= 4080) ? MaxJSample : p5 >> 4;
p6 = (p6 < 16) ? 0 : (p6 >= 4080) ? MaxJSample : p6 >> 4;
p7 = (p7 < 16) ? 0 : (p7 >= 4080) ? MaxJSample : p7 >> 4;
// store block data
Unsafe.Add(ref blockDataRef, col) = Unsafe.As<int, short>(ref Unsafe.AsRef(p0));
Unsafe.Add(ref blockDataRef, c8) = Unsafe.As<int, short>(ref Unsafe.AsRef(p1));
Unsafe.Add(ref blockDataRef, c16) = Unsafe.As<int, short>(ref Unsafe.AsRef(p2));
Unsafe.Add(ref blockDataRef, c24) = Unsafe.As<int, short>(ref Unsafe.AsRef(p3));
Unsafe.Add(ref blockDataRef, c32) = Unsafe.As<int, short>(ref Unsafe.AsRef(p4));
Unsafe.Add(ref blockDataRef, c40) = Unsafe.As<int, short>(ref Unsafe.AsRef(p5));
Unsafe.Add(ref blockDataRef, c48) = Unsafe.As<int, short>(ref Unsafe.AsRef(p6));
Unsafe.Add(ref blockDataRef, c56) = Unsafe.As<int, short>(ref Unsafe.AsRef(p7));
}
}
}
}

26
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs

@ -6,6 +6,7 @@ using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
@ -202,7 +203,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span);
// TODO: This is where our error is happening.
// We can't simply cast the span as I think the scan decoder expects data to be laid out in linear order
// rather than in the column major order expected by the Block8x8 struct and anything reading it down the pipeline.
// Ask Anton about this. It might be a lost cause.
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];
@ -224,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span);
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];
int h = component.HorizontalSamplingFactor;
@ -262,7 +268,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span);
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
for (int n = 0; n < mcuToRead; n++)
@ -283,7 +289,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span);
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
@ -319,7 +325,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span);
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
for (int n = 0; n < mcuToRead; n++)
{
@ -341,7 +347,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
PdfJsFrameComponent component = components[i];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span);
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
for (int j = 0; j < v; j++)
{
@ -375,7 +381,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span);
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
for (int n = 0; n < mcuToRead; n++)
@ -396,7 +402,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span);
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
@ -433,7 +439,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
if (componentsLength == 1)
{
PdfJsFrameComponent component = components[this.compIndex];
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span);
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
for (int n = 0; n < mcuToRead; n++)
@ -454,7 +460,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
for (int i = 0; i < componentsLength; i++)
{
PdfJsFrameComponent component = components[i];
ref short blockDataRef = ref MemoryMarshal.GetReference(component.BlockData.Span);
ref short blockDataRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<Block8x8, short>(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;

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

@ -3,10 +3,12 @@
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components;
using SixLabors.ImageSharp.Memory;
@ -15,6 +17,7 @@ using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
{
@ -22,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// Performs the jpeg decoding operation.
/// Ported from <see href="https://github.com/mozilla/pdf.js/blob/master/src/core/jpg.js"/> with additional fixes to handle common encoding errors
/// </summary>
internal sealed class PdfJsJpegDecoderCore : IDisposable
internal sealed class PdfJsJpegDecoderCore : IRawJpegData
{
/// <summary>
/// The only supported precision
@ -42,8 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
private readonly byte[] markerBuffer = new byte[2];
private PdfJsQuantizationTables quantizationTables;
// private PdfJsQuantizationTables quantizationTables;
private PdfJsHuffmanTables dcHuffmanTables;
private PdfJsHuffmanTables acHuffmanTables;
@ -103,15 +105,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </summary>
public int ImageHeight { get; private set; }
/// <summary>
/// Gets the number of components
/// </summary>
public int NumberOfComponents { get; private set; }
/// <summary>
/// Gets the color depth, in number of bits per pixel.
/// </summary>
public int BitsPerPixel => this.NumberOfComponents * SupportedPrecision;
public int BitsPerPixel => this.ComponentCount * SupportedPrecision;
/// <summary>
/// Gets the input stream.
@ -128,6 +125,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </summary>
public ImageMetaData MetaData { get; private set; }
/// <inheritdoc/>
public Size ImageSizeInPixels => new Size(this.ImageWidth, this.ImageHeight);
/// <inheritdoc/>
public int ComponentCount { get; private set; }
/// <inheritdoc/>
public JpegColorSpace ColorSpace { get; private set; }
/// <inheritdoc/>
public IEnumerable<IJpegComponent> Components => this.Frame.Components;
public Block8x8F[] QuantizationTables { get; private set; }
/// <summary>
/// Finds the next file marker within the byte stream.
/// </summary>
@ -174,10 +185,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
where TPixel : struct, IPixel<TPixel>
{
this.ParseStream(stream);
this.QuantizeAndInverseAllComponents();
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
this.FillPixelData(image.Frames.RootFrame);
Image<TPixel> image = this.PostProcessIntoImage<TPixel>();
// this.QuantizeAndInverseAllComponents();
// var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
// this.FillPixelData(image.Frames.RootFrame);
this.AssignResolution();
return image;
}
@ -213,7 +226,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
ushort marker = this.ReadUint16();
fileMarker = new PdfJsFileMarker(marker, (int)this.InputStream.Position - 2);
this.quantizationTables = new PdfJsQuantizationTables(this.configuration.MemoryManager);
this.QuantizationTables = new Block8x8F[4];
// this.quantizationTables = new PdfJsQuantizationTables(this.configuration.MemoryManager);
this.dcHuffmanTables = new PdfJsHuffmanTables();
this.acHuffmanTables = new PdfJsHuffmanTables();
@ -339,7 +354,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.components.Components[i] = component;
}
this.NumberOfComponents = this.components.Components.Length;
this.ComponentCount = this.components.Components.Length;
}
/// <inheritdoc/>
@ -347,13 +362,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
{
this.Frame?.Dispose();
this.components?.Dispose();
this.quantizationTables?.Dispose();
// this.quantizationTables?.Dispose();
this.pixelArea.Dispose();
// Set large fields to null.
this.Frame = null;
this.components = null;
this.quantizationTables = null;
// this.quantizationTables = null;
this.dcHuffmanTables = null;
this.acHuffmanTables = null;
}
@ -377,21 +394,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
private void FillPixelData<TPixel>(ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (this.NumberOfComponents > 4)
if (this.ComponentCount > 4)
{
throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.NumberOfComponents}");
throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}");
}
this.pixelArea = new PdfJsJpegPixelArea(this.configuration.MemoryManager, image.Width, image.Height, this.NumberOfComponents);
this.pixelArea = new PdfJsJpegPixelArea(this.configuration.MemoryManager, image.Width, image.Height, this.ComponentCount);
this.pixelArea.LinearizeBlockData(this.components);
if (this.NumberOfComponents == 1)
if (this.ComponentCount == 1)
{
this.FillGrayScaleImage(image);
return;
}
if (this.NumberOfComponents == 3)
if (this.ComponentCount == 3)
{
if (this.adobe.Equals(default) || this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYCbCr)
{
@ -403,7 +420,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
}
if (this.NumberOfComponents == 4)
if (this.ComponentCount == 4)
{
if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYcck)
{
@ -416,6 +433,40 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
}
private JpegColorSpace DeduceJpegColorSpace()
{
if (this.ComponentCount == 1)
{
return JpegColorSpace.Grayscale;
}
if (this.ComponentCount == 3)
{
if (this.adobe.Equals(default) || this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYCbCr)
{
return JpegColorSpace.YCbCr;
}
else if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformUnknown)
{
return JpegColorSpace.RGB;
}
}
if (this.ComponentCount == 4)
{
if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYcck)
{
return JpegColorSpace.Ycck;
}
else
{
return JpegColorSpace.Cmyk;
}
}
throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}");
}
/// <summary>
/// Assigns the horizontal and vertical resolution to the image if it has a JFIF header or EXIF metadata.
/// </summary>
@ -602,10 +653,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.InputStream.Read(this.temp, 0, 64);
remaining -= 64;
ref short tableRef = ref MemoryMarshal.GetReference(this.quantizationTables.Tables.GetRowSpan(quantizationTableSpec & 15));
Block8x8F table = this.QuantizationTables[quantizationTableSpec & 15];
for (int j = 0; j < 64; j++)
{
Unsafe.Add(ref tableRef, PdfJsQuantizationTables.DctZigZag[j]) = this.temp[j];
table[j] = this.temp[j];
}
}
@ -622,10 +673,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.InputStream.Read(this.temp, 0, 128);
remaining -= 128;
ref short tableRef = ref MemoryMarshal.GetReference(this.quantizationTables.Tables.GetRowSpan(quantizationTableSpec & 15));
Block8x8F table = this.QuantizationTables[quantizationTableSpec & 15];
for (int j = 0; j < 64; j++)
{
Unsafe.Add(ref tableRef, PdfJsQuantizationTables.DctZigZag[j]) = (short)((this.temp[2 * j] << 8) | this.temp[(2 * j) + 1]);
table[j] = (this.temp[2 * j] << 8) | this.temp[(2 * j) + 1];
}
}
@ -840,20 +891,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
int blocksPerColumn = component.BlocksPerColumn;
using (IBuffer<short> computationBuffer = this.configuration.MemoryManager.Allocate<short>(64, true))
{
ref short quantizationTableRef = ref MemoryMarshal.GetReference(this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex));
ref short computationBufferSpan = ref MemoryMarshal.GetReference(computationBuffer.Span);
for (int blockRow = 0; blockRow < blocksPerColumn; blockRow++)
{
for (int blockCol = 0; blockCol < blocksPerLine; blockCol++)
{
int offset = 64 * (((blocksPerLine + 1) * blockRow) + blockCol);
PdfJsIDCT.QuantizeAndInverse(frameComponent, offset, ref computationBufferSpan, ref quantizationTableRef);
}
}
// ref short quantizationTableRef = ref MemoryMarshal.GetReference(this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex));
// ref short computationBufferSpan = ref MemoryMarshal.GetReference(computationBuffer.Span);
//
// for (int blockRow = 0; blockRow < blocksPerColumn; blockRow++)
// {
// for (int blockCol = 0; blockCol < blocksPerLine; blockCol++)
// {
// int offset = 64 * (((blocksPerLine + 1) * blockRow) + blockCol);
// PdfJsIDCT.QuantizeAndInverse(frameComponent, offset, ref computationBufferSpan, ref quantizationTableRef);
// }
// }
}
component.Output = frameComponent.BlockData;
// component.Output = frameComponent.BlockData;
}
/// <summary>
@ -980,5 +1031,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.InputStream.Read(this.markerBuffer, 0, 2);
return BinaryPrimitives.ReadUInt16BigEndian(this.markerBuffer);
}
private Image<TPixel> PostProcessIntoImage<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
this.ColorSpace = this.DeduceJpegColorSpace();
using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryManager, this))
{
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
postProcessor.PostProcess(image.Frames.RootFrame);
return image;
}
}
}
}

12
tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs

@ -39,9 +39,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
public Size SubSamplingDivisors => throw new NotSupportedException();
public int HeightInBlocks { get; }
public int WidthInBlocks { get; }
public int QuantizationTableIndex => throw new NotSupportedException();
public Buffer2D<Block8x8> SpectralBlocks { get; private set; }
@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
public short MinVal { get; private set; } = short.MaxValue;
public short MaxVal { get; private set; } = short.MinValue;
internal void MakeBlock(short[] data, int y, int x)
{
this.MinVal = Math.Min((short)this.MinVal, data.Min());
@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
{
for (int x = 0; x < result.WidthInBlocks; x++)
{
short[] data = c.GetBlockBuffer(y, x).ToArray();
short[] data = c.GetBlockReference(x, y).ToArray();
result.MakeBlock(data, y, x);
}
}
@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
public Image<Rgba32> CreateGrayScaleImage()
{
Image<Rgba32> result = new Image<Rgba32>(this.WidthInBlocks * 8, this.HeightInBlocks * 8);
for (int by = 0; by < this.HeightInBlocks; by++)
{
for (int bx = 0; bx < this.WidthInBlocks; bx++)
@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
internal void WriteToImage(int bx, int by, Image<Rgba32> image)
{
Block8x8 block = this.SpectralBlocks[bx, by];
for (int y = 0; y < 8; y++)
{
for (int x = 0; x < 8; x++)

Loading…
Cancel
Save