diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs
deleted file mode 100644
index da97f9e2ae..0000000000
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
-{
- ///
- /// Various utilities for .
- ///
- internal static class ComponentUtils
- {
- ///
- /// Gets a reference to the at the given row and column index from
- ///
- public static ref Block8x8 GetBlockReference(this IJpegComponent component, int bx, int by)
- {
- return ref component.SpectralBlocks[bx, by];
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs
index 4109fc10e7..de9f75dc1f 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs
@@ -42,5 +42,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// We need to apply IDCT and dequantiazition to transform them into color-space blocks.
///
Buffer2D SpectralBlocks { get; }
+
+ ///
+ /// Gets a reference to the at the given row and column index from
+ ///
+ /// The column
+ /// The row
+ /// The
+ ref Block8x8 GetBlockReference(int column, int row);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs
index e83dd75a54..e2f21bd1c3 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.ImageSharp.Memory;
@@ -237,6 +238,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.SamplingFactors = new Size(h, v);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref Block8x8 GetBlockReference(int column, int row)
+ {
+ return ref this.SpectralBlocks[column, row];
+ }
+
public void Dispose()
{
this.SpectralBlocks.Dispose();
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs
index 2442c39981..e6ee4f16c9 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs
@@ -3,6 +3,7 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.ImageSharp.Memory;
@@ -25,6 +26,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.Id = id;
this.HorizontalSamplingFactor = horizontalFactor;
this.VerticalSamplingFactor = verticalFactor;
+ this.SamplingFactors = new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor);
this.QuantizationTableIndex = quantizationTableIndex;
this.Index = index;
}
@@ -49,25 +51,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
///
public int VerticalSamplingFactor { get; }
- Buffer2D IJpegComponent.SpectralBlocks => throw new NotImplementedException();
+ ///
+ public Buffer2D SpectralBlocks { get; private set; }
- // TODO: Should be derived from PdfJsComponent.Scale
- public Size SubSamplingDivisors => throw new NotImplementedException();
+ ///
+ public Size SubSamplingDivisors { get; private set; }
///
public int QuantizationTableIndex { get; }
- ///
- /// Gets the block data
- ///
- public IBuffer BlockData { get; private set; }
-
///
public int Index { get; }
- public Size SizeInBlocks => new Size(this.WidthInBlocks, this.HeightInBlocks);
+ ///
+ public Size SizeInBlocks { get; private set; }
- public Size SamplingFactors => new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor);
+ ///
+ public Size SamplingFactors { get; set; }
///
/// Gets the number of blocks per line
@@ -89,17 +89,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
///
public int ACHuffmanTableId { get; set; }
- internal int BlocksPerLineForMcu { get; private set; }
-
- internal int BlocksPerColumnForMcu { get; private set; }
-
public PdfJsFrame Frame { get; }
///
public void Dispose()
{
- this.BlockData?.Dispose();
- this.BlockData = null;
+ this.SpectralBlocks?.Dispose();
+ this.SpectralBlocks = null;
}
public void Init()
@@ -110,25 +106,43 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.HeightInBlocks = (int)MathF.Ceiling(
MathF.Ceiling(this.Frame.Scanlines / 8F) * this.VerticalSamplingFactor / this.Frame.MaxVerticalFactor);
- 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(blocksBufferSize, true);
+ int blocksPerLineForMcu = this.Frame.McusPerLine * this.HorizontalSamplingFactor;
+ int blocksPerColumnForMcu = this.Frame.McusPerColumn * this.VerticalSamplingFactor;
+ this.SizeInBlocks = new Size(blocksPerLineForMcu, blocksPerColumnForMcu);
+
+ // 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
+ {
+ PdfJsFrameComponent c0 = this.Frame.Components[0];
+ this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
+ }
+
+ this.SpectralBlocks = this.memoryManager.Allocate2D(blocksPerColumnForMcu, blocksPerLineForMcu + 1, true);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int GetBlockBufferOffset(int row, int col)
+ public ref Block8x8 GetBlockReference(int column, int row)
{
- return 64 * (((this.WidthInBlocks + 1) * row) + col);
+ int offset = ((this.WidthInBlocks + 1) * row) + column;
+ return ref Unsafe.Add(ref MemoryMarshal.GetReference(this.SpectralBlocks.Span), offset);
}
- public Span GetBlockBuffer(int row, int col)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int GetBlockBufferOffset(int row, int col)
{
- int offset = this.GetBlockBufferOffset(row, col);
- return this.BlockData.Span.Slice(offset, 64);
+ return 64 * (((this.WidthInBlocks + 1) * row) + col);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs
deleted file mode 100644
index 97c582dc00..0000000000
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Performs the inverse Descrete Cosine Transform on each frame component.
- ///
- 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;
-
- ///
- /// 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 & Signal Processing, 1989, 988-991.
- ///
- /// The frame component
- /// The block buffer offset
- /// The computational buffer for holding temp values ref
- /// The quantization table ref
- 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(ref Unsafe.AsRef(p0));
- Unsafe.Add(ref blockDataRef, c8) = Unsafe.As(ref Unsafe.AsRef(p1));
- Unsafe.Add(ref blockDataRef, c16) = Unsafe.As(ref Unsafe.AsRef(p2));
- Unsafe.Add(ref blockDataRef, c24) = Unsafe.As(ref Unsafe.AsRef(p3));
- Unsafe.Add(ref blockDataRef, c32) = Unsafe.As(ref Unsafe.AsRef(p4));
- Unsafe.Add(ref blockDataRef, c40) = Unsafe.As(ref Unsafe.AsRef(p5));
- Unsafe.Add(ref blockDataRef, c48) = Unsafe.As(ref Unsafe.AsRef(p6));
- Unsafe.Add(ref blockDataRef, c56) = Unsafe.As(ref Unsafe.AsRef(p7));
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
index fe80cbaf34..b2c80ce9a8 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs
+++ b/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(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(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(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(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(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(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(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(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(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(component.SpectralBlocks.Span));
ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId];
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
index 1cf904f558..87d58a6ba6 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
+++ b/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 with additional fixes to handle common encoding errors
///
- internal sealed class PdfJsJpegDecoderCore : IDisposable
+ internal sealed class PdfJsJpegDecoderCore : IRawJpegData
{
///
/// 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
///
public int ImageHeight { get; private set; }
- ///
- /// Gets the number of components
- ///
- public int NumberOfComponents { get; private set; }
-
///
/// Gets the color depth, in number of bits per pixel.
///
- public int BitsPerPixel => this.NumberOfComponents * SupportedPrecision;
+ public int BitsPerPixel => this.ComponentCount * SupportedPrecision;
///
/// Gets the input stream.
@@ -128,6 +125,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
///
public ImageMetaData MetaData { get; private set; }
+ ///
+ public Size ImageSizeInPixels => new Size(this.ImageWidth, this.ImageHeight);
+
+ ///
+ public int ComponentCount { get; private set; }
+
+ ///
+ public JpegColorSpace ColorSpace { get; private set; }
+
+ ///
+ public IEnumerable Components => this.Frame.Components;
+
+ public Block8x8F[] QuantizationTables { get; private set; }
+
///
/// Finds the next file marker within the byte stream.
///
@@ -174,10 +185,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
where TPixel : struct, IPixel
{
this.ParseStream(stream);
- this.QuantizeAndInverseAllComponents();
- var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
- this.FillPixelData(image.Frames.RootFrame);
+ Image image = this.PostProcessIntoImage();
+
+ // this.QuantizeAndInverseAllComponents();
+ // var image = new Image(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;
}
///
@@ -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(ImageFrame image)
where TPixel : struct, IPixel
{
- 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}");
+ }
+
///
/// Assigns the horizontal and vertical resolution to the image if it has a JFIF header or EXIF metadata.
///
@@ -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));
+ ref Block8x8F table = ref 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));
+ ref Block8x8F table = ref 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 computationBuffer = this.configuration.MemoryManager.Allocate(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;
}
///
@@ -973,5 +1024,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.InputStream.Read(this.markerBuffer, 0, 2);
return BinaryPrimitives.ReadUInt16BigEndian(this.markerBuffer);
}
+
+ private Image PostProcessIntoImage()
+ where TPixel : struct, IPixel
+ {
+ this.ColorSpace = this.DeduceJpegColorSpace();
+ using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryManager, this))
+ {
+ var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
+ postProcessor.PostProcess(image.Frames.RootFrame);
+ return image;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
index 30f0088861..a003f749e3 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
+++ b/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 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 CreateGrayScaleImage()
{
Image result = new Image(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 image)
{
Block8x8 block = this.SpectralBlocks[bx, by];
-
+
for (int y = 0; y < 8; y++)
{
for (int x = 0; x < 8; x++)
@@ -184,6 +184,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
}
}
+ public ref Block8x8 GetBlockReference(int column, int row)
+ {
+ throw new NotImplementedException();
+ }
+
public static bool operator ==(ComponentData left, ComponentData right)
{
return Object.Equals(left, right);