diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/Component.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/Component.cs
index 2f167c251c..d49b1b4812 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/Component.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/Component.cs
@@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
///
/// Represents a component block
///
- internal struct Component : IDisposable
+ internal class Component : IDisposable
{
///
/// Gets or sets the output
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/Frame.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/Frame.cs
index e7eabcdc8b..4fdb5cd3b3 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/Frame.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/Frame.cs
@@ -5,6 +5,8 @@ using System;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
+ using SixLabors.ImageSharp.Memory;
+
///
/// Represent a single jpeg frame
///
@@ -83,5 +85,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
this.Components = null;
}
}
+
+ ///
+ /// Allocates the frame component blocks
+ ///
+ public void InitComponents()
+ {
+ this.McusPerLine = (int)MathF.Ceiling(this.SamplesPerLine / 8F / this.MaxHorizontalFactor);
+ this.McusPerColumn = (int)MathF.Ceiling(this.Scanlines / 8F / this.MaxVerticalFactor);
+
+ for (int i = 0; i < this.ComponentCount; i++)
+ {
+ FrameComponent component = this.Components[i];
+ component.Init();
+ }
+ }
+
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FrameComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FrameComponent.cs
index 4bc66406bb..bfc5c7c8b4 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FrameComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FrameComponent.cs
@@ -6,6 +6,8 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
+ using System.Runtime.CompilerServices;
+
///
/// Represents a single frame component
///
@@ -13,9 +15,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
#pragma warning disable SA1401 // Fields should be private
///
- /// Gets or sets the component Id
+ /// Gets the component Id
///
- public byte Id;
+ public byte Id { get; }
///
/// TODO: What does pred stand for?
@@ -23,19 +25,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
public int Pred;
///
- /// Gets or sets the horizontal sampling factor.
+ /// Gets the horizontal sampling factor.
///
- public int HorizontalFactor;
+ public int HorizontalFactor { get; }
///
- /// Gets or sets the vertical sampling factor.
+ /// Gets the vertical sampling factor.
///
- public int VerticalFactor;
+ public int VerticalFactor { get; }
///
- /// Gets or sets the identifier
+ /// Gets the identifier
///
- public byte QuantizationIdentifier;
+ public byte QuantizationIdentifier { get; }
///
/// Gets or sets the block data
@@ -62,11 +64,55 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
///
public int ACHuffmanTableId;
+ internal int BlocksPerLineForMcu;
+
+ internal int BlocksPerColumnForMcu;
+
+ public Frame Frame { get; }
+
+ public FrameComponent(Frame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationIdentifier)
+ {
+ this.Frame = frame;
+ this.Id = id;
+ this.HorizontalFactor = horizontalFactor;
+ this.VerticalFactor = verticalFactor;
+ this.QuantizationIdentifier = quantizationIdentifier;
+ }
+
///
public void Dispose()
{
this.BlockData?.Dispose();
this.BlockData = null;
}
+
+ public void Init()
+ {
+ this.BlocksPerLine = (int)MathF.Ceiling(
+ MathF.Ceiling(this.Frame.SamplesPerLine / 8F) * this.HorizontalFactor / this.Frame.MaxHorizontalFactor);
+
+ this.BlocksPerColumn = (int)MathF.Ceiling(
+ MathF.Ceiling(this.Frame.Scanlines / 8F) * this.VerticalFactor / this.Frame.MaxVerticalFactor);
+
+ this.BlocksPerLineForMcu = this.Frame.McusPerLine * this.HorizontalFactor;
+ this.BlocksPerColumnForMcu = this.Frame.McusPerColumn * this.VerticalFactor;
+
+ int blocksBufferSize = 64 * this.BlocksPerColumnForMcu * (this.BlocksPerLineForMcu + 1);
+
+ // Pooled. Disposed via frame disposal
+ this.BlockData = Buffer.CreateClean(blocksBufferSize);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int GetBlockBufferOffset(int row, int col)
+ {
+ return 64 * (((this.BlocksPerLine + 1) * row) + col);
+ }
+
+ public Span GetBlockBuffer(int row, int col)
+ {
+ int offset = this.GetBlockBufferOffset(row, col);
+ return this.BlockData.Span.Slice(offset, 64);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs
index c7f1513c8d..d8152c7b9b 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs
@@ -187,12 +187,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int GetBlockBufferOffset(FrameComponent component, int row, int col)
- {
- return 64 * (((component.BlocksPerLine + 1) * row) + col);
- }
-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeScanBaseline(
HuffmanTables dcHuffmanTables,
@@ -476,7 +470,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
int blockRow = mcu / component.BlocksPerLine;
int blockCol = mcu % component.BlocksPerLine;
- int offset = GetBlockBufferOffset(component, blockRow, blockCol);
+ int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeBaseline(component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream);
}
@@ -487,7 +481,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalFactor) + row;
int blockCol = (mcuCol * component.HorizontalFactor) + col;
- int offset = GetBlockBufferOffset(component, blockRow, blockCol);
+ int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeBaseline(component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream);
}
@@ -496,7 +490,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
int blockRow = mcu / component.BlocksPerLine;
int blockCol = mcu % component.BlocksPerLine;
- int offset = GetBlockBufferOffset(component, blockRow, blockCol);
+ int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCFirst(component, offset, ref dcHuffmanTable, stream);
}
@@ -507,7 +501,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalFactor) + row;
int blockCol = (mcuCol * component.HorizontalFactor) + col;
- int offset = GetBlockBufferOffset(component, blockRow, blockCol);
+ int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCFirst(component, offset, ref dcHuffmanTable, stream);
}
@@ -516,7 +510,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
int blockRow = mcu / component.BlocksPerLine;
int blockCol = mcu % component.BlocksPerLine;
- int offset = GetBlockBufferOffset(component, blockRow, blockCol);
+ int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCSuccessive(component, offset, stream);
}
@@ -527,7 +521,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalFactor) + row;
int blockCol = (mcuCol * component.HorizontalFactor) + col;
- int offset = GetBlockBufferOffset(component, blockRow, blockCol);
+ int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeDCSuccessive(component, offset, stream);
}
@@ -536,7 +530,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
int blockRow = mcu / component.BlocksPerLine;
int blockCol = mcu % component.BlocksPerLine;
- int offset = GetBlockBufferOffset(component, blockRow, blockCol);
+ int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACFirst(component, offset, ref acHuffmanTable, stream);
}
@@ -547,7 +541,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalFactor) + row;
int blockCol = (mcuCol * component.HorizontalFactor) + col;
- int offset = GetBlockBufferOffset(component, blockRow, blockCol);
+ int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACFirst(component, offset, ref acHuffmanTable, stream);
}
@@ -556,7 +550,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{
int blockRow = mcu / component.BlocksPerLine;
int blockCol = mcu % component.BlocksPerLine;
- int offset = GetBlockBufferOffset(component, blockRow, blockCol);
+ int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACSuccessive(component, offset, ref acHuffmanTable, stream);
}
@@ -567,7 +561,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * component.VerticalFactor) + row;
int blockCol = (mcuCol * component.HorizontalFactor) + col;
- int offset = GetBlockBufferOffset(component, blockRow, blockCol);
+ int offset = component.GetBlockBufferOffset(blockRow, blockCol);
this.DecodeACSuccessive(component, offset, ref acHuffmanTable, stream);
}
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/JpegDecoderCore.cs
index 6f01f6b442..dcdde1feb7 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/JpegDecoderCore.cs
@@ -140,6 +140,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
{
ImageMetaData metadata = this.ParseStream(stream);
+ this.QuantizeAndInverseAllComponents();
+
var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, metadata);
this.FillPixelData(image);
this.AssignResolution(image);
@@ -275,7 +277,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
for (int i = 0; i < this.components.Components.Length; i++)
{
- ref var frameComponent = ref this.Frame.Components[i];
+ FrameComponent frameComponent = this.Frame.Components[i];
var component = new Component
{
Scale = new System.Numerics.Vector2(
@@ -285,13 +287,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
BlocksPerColumn = frameComponent.BlocksPerColumn
};
- this.BuildComponentData(ref component, ref frameComponent);
+ // this.QuantizeAndInverseComponentData(ref component, frameComponent);
this.components.Components[i] = component;
}
this.NumberOfComponents = this.components.Components.Length;
}
+ internal void QuantizeAndInverseAllComponents()
+ {
+ for (int i = 0; i < this.components.Components.Length; i++)
+ {
+ FrameComponent frameComponent = this.Frame.Components[i];
+ Component component = this.components.Components[i];
+
+ this.QuantizeAndInverseComponentData(component, frameComponent);
+ }
+ }
+
///
/// Fills the given image with the color data
///
@@ -648,13 +661,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
maxV = v;
}
- var component = new FrameComponent();
- this.Frame.Components[i] = component;
- component.Id = this.temp[index];
- component.HorizontalFactor = h;
- component.VerticalFactor = v;
- component.QuantizationIdentifier = this.temp[index + 2];
+ var component = new FrameComponent(this.Frame, this.temp[index], h, v, this.temp[index + 2]);
+ this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id;
index += 3;
@@ -662,7 +671,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.Frame.MaxHorizontalFactor = maxH;
this.Frame.MaxVerticalFactor = maxV;
- this.PrepareComponents();
+ this.Frame.InitComponents();
}
///
@@ -784,7 +793,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
///
/// The component
/// The frame component
- private void BuildComponentData(ref Component component, ref FrameComponent frameComponent)
+ private void QuantizeAndInverseComponentData(Component component, FrameComponent frameComponent)
{
int blocksPerLine = component.BlocksPerLine;
int blocksPerColumn = component.BlocksPerColumn;
@@ -830,34 +839,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
tables[index] = new HuffmanTable(codeLengths, values);
}
- ///
- /// Allocates the frame component blocks
- ///
- private void PrepareComponents()
- {
- int mcusPerLine = (int)MathF.Ceiling(this.Frame.SamplesPerLine / 8F / this.Frame.MaxHorizontalFactor);
- int mcusPerColumn = (int)MathF.Ceiling(this.Frame.Scanlines / 8F / this.Frame.MaxVerticalFactor);
-
- for (int i = 0; i < this.Frame.ComponentCount; i++)
- {
- ref var component = ref this.Frame.Components[i];
- int blocksPerLine = (int)MathF.Ceiling(MathF.Ceiling(this.Frame.SamplesPerLine / 8F) * component.HorizontalFactor / this.Frame.MaxHorizontalFactor);
- int blocksPerColumn = (int)MathF.Ceiling(MathF.Ceiling(this.Frame.Scanlines / 8F) * component.VerticalFactor / this.Frame.MaxVerticalFactor);
- int blocksPerLineForMcu = mcusPerLine * component.HorizontalFactor;
- int blocksPerColumnForMcu = mcusPerColumn * component.VerticalFactor;
-
- int blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
-
- // Pooled. Disposed via frame disposal
- component.BlockData = Buffer.CreateClean(blocksBufferSize);
- component.BlocksPerLine = blocksPerLine;
- component.BlocksPerColumn = blocksPerColumn;
- }
-
- this.Frame.McusPerLine = mcusPerLine;
- this.Frame.McusPerColumn = mcusPerColumn;
- }
-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FillGrayScaleImage(Image image)
where TPixel : struct, IPixel
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/LibJpegTools.cs b/tests/ImageSharp.Tests/Formats/Jpg/LibJpegTools.cs
index a6efa3f2fb..04e755310f 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/LibJpegTools.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/LibJpegTools.cs
@@ -11,12 +11,13 @@ namespace SixLabors.ImageSharp.Tests
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort;
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components;
using SixLabors.ImageSharp.PixelFormats;
+ using SixLabors.Primitives;
using Xunit;
internal static class LibJpegTools
{
- public unsafe struct Block
+ public unsafe struct Block : IEquatable
{
public Block(short[] data)
{
@@ -24,28 +25,45 @@ namespace SixLabors.ImageSharp.Tests
}
public short[] Data { get; }
-
- //public fixed short Data[64];
-
- //public Block8x8(short[] data)
- //{
- // fixed (short* p = Data)
- // {
- // for (int i = 0; i < 64; i++)
- // {
- // p[i] = data[i];
- // }
- // }
- //}
-
+
public short this[int x, int y]
{
get => this.Data[y * 8 + x];
set => this.Data[y * 8 + x] = value;
}
+
+ public bool Equals(Block other)
+ {
+ for (int i = 0; i < 64; i++)
+ {
+ if (this.Data[i] != other.Data[i]) return false;
+ }
+ return true;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ return obj is Block && Equals((Block)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return (this.Data != null ? this.Data.GetHashCode() : 0);
+ }
+
+ public static bool operator ==(Block left, Block right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Block left, Block right)
+ {
+ return !left.Equals(right);
+ }
}
- public class SpectralData
+ public class SpectralData : IEquatable
{
public int ComponentCount { get; private set; }
@@ -132,13 +150,116 @@ namespace SixLabors.ImageSharp.Tests
public static SpectralData LoadFromImageSharpDecoder(JpegDecoderCore decoder)
{
FrameComponent[] srcComponents = decoder.Frame.Components;
-
- ComponentData[] destComponents = new ComponentData[srcComponents.Length];
- throw new NotImplementedException();
+
+ ComponentData[] destComponents = srcComponents.Select(ComponentData.Load).ToArray();
+
+ return new SpectralData(destComponents);
+ }
+
+ public Image TryCreateRGBSpectralImage()
+ {
+ if (this.ComponentCount != 3) return null;
+
+ ComponentData c0 = this.Components[0];
+ ComponentData c1 = this.Components[1];
+ ComponentData c2 = this.Components[2];
+
+ if (c0.Size != c1.Size || c1.Size != c2.Size)
+ {
+ return null;
+ }
+
+ Image result = new Image(c0.XCount * 8, c0.YCount * 8);
+
+ for (int by = 0; by < c0.YCount; by++)
+ {
+ for (int bx = 0; bx < c0.XCount; bx++)
+ {
+ this.WriteToImage(bx, by, result);
+ }
+ }
+ return result;
+ }
+
+ internal void WriteToImage(int bx, int by, Image image)
+ {
+ ComponentData c0 = this.Components[0];
+ ComponentData c1 = this.Components[1];
+ ComponentData c2 = this.Components[2];
+
+ Block block0 = c0.Blocks[by, bx];
+ Block block1 = c1.Blocks[by, bx];
+ Block block2 = c2.Blocks[by, bx];
+
+ float d0 = (c0.MaxVal - c0.MinVal);
+ float d1 = (c1.MaxVal - c1.MinVal);
+ float d2 = (c2.MaxVal - c2.MinVal);
+
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ float val0 = c0.GetBlockValue(block0, x, y);
+ float val1 = c0.GetBlockValue(block1, x, y);
+ float val2 = c0.GetBlockValue(block2, x, y);
+
+ Vector4 v = new Vector4(val0, val1, val2, 1);
+ Rgba32 color = default(Rgba32);
+ color.PackFromVector4(v);
+
+ int yy = by * 8 + y;
+ int xx = bx * 8 + x;
+ image[xx, yy] = color;
+ }
+ }
+ }
+
+ public bool Equals(SpectralData other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ if (this.ComponentCount != other.ComponentCount)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < this.ComponentCount; i++)
+ {
+ ComponentData a = this.Components[i];
+ ComponentData b = other.Components[i];
+ if (!a.Equals(b)) return false;
+ }
+ return true;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((SpectralData)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return (this.ComponentCount * 397) ^ (this.Components != null ? this.Components[0].GetHashCode() : 0);
+ }
+ }
+
+ public static bool operator ==(SpectralData left, SpectralData right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(SpectralData left, SpectralData right)
+ {
+ return !Equals(left, right);
}
}
- public class ComponentData
+ public class ComponentData : IEquatable
{
public ComponentData(int yCount, int xCount, int index)
{
@@ -148,6 +269,8 @@ namespace SixLabors.ImageSharp.Tests
this.Blocks = new Block[this.YCount, this.XCount];
}
+ public Size Size => new Size(this.XCount, this.YCount);
+
public int Index { get; }
public int YCount { get; }
@@ -172,16 +295,44 @@ namespace SixLabors.ImageSharp.Tests
private void Init(Array bloxSource)
{
- for (int i = 0; i < bloxSource.Length; i++)
+ for (int y = 0; y < bloxSource.Length; y++)
{
- Array row = (Array)bloxSource.GetValue(i);
- for (int j = 0; j < row.Length; j++)
+ Array row = (Array)bloxSource.GetValue(y);
+ for (int x = 0; x < row.Length; x++)
{
- object jBlock = row.GetValue(j);
+ object jBlock = row.GetValue(x);
short[] data = (short[])GetNonPublicMember(jBlock, "data");
- this.MinVal = Math.Min(this.MinVal, data.Min());
- this.MaxVal = Math.Max(this.MaxVal, data.Max());
- this.Blocks[i, j] = new Block(data);
+ this.MakeBlock(data, y, x);
+ }
+ }
+ }
+
+ private void MakeBlock(short[] data, int y, int x)
+ {
+ this.MinVal = Math.Min(this.MinVal, data.Min());
+ this.MaxVal = Math.Max(this.MaxVal, data.Max());
+ this.Blocks[y, x] = new Block(data);
+ }
+
+ public static ComponentData Load(FrameComponent sc, int index)
+ {
+ var result = new ComponentData(
+ sc.BlocksPerColumnForMcu,
+ sc.BlocksPerLineForMcu,
+ index
+ );
+ result.Init(sc);
+ return result;
+ }
+
+ private void Init(FrameComponent sc)
+ {
+ for (int y = 0; y < this.YCount; y++)
+ {
+ for (int x = 0; x < this.XCount; x++)
+ {
+ short[] data = sc.GetBlockBuffer(y, x).ToArray();
+ this.MakeBlock(data, y, x);
}
}
}
@@ -194,33 +345,93 @@ namespace SixLabors.ImageSharp.Tests
{
for (int bx = 0; bx < this.XCount; bx++)
{
- WriteToImage(this.Blocks[by, bx], bx, by, result);
+ this.WriteToImage(bx, by, result);
}
}
return result;
}
- private void WriteToImage(Block block, int bx, int by, Image image)
+ internal void WriteToImage(int bx, int by, Image image)
{
- float d = (this.MaxVal - this.MinVal);
+ Block block = this.Blocks[by, bx];
+
for (int y = 0; y < 8; y++)
{
for (int x = 0; x < 8; x++)
{
- int yy = by * 8 + y;
- int xx = bx * 8 + x;
- float val = block[x, y];
- val -= this.MinVal;
- val /= d;
+ var val = this.GetBlockValue(block, x, y);
Vector4 v = new Vector4(val, val, val, 1);
Rgba32 color = default(Rgba32);
color.PackFromVector4(v);
+ int yy = by * 8 + y;
+ int xx = bx * 8 + x;
image[xx, yy] = color;
}
}
}
+
+ internal float GetBlockValue(Block block, int x, int y)
+ {
+ float d = (this.MaxVal - this.MinVal);
+ float val = block[x, y];
+ val -= this.MinVal;
+ val /= d;
+ return val;
+ }
+
+ public bool Equals(ComponentData other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ bool ok = this.Index == other.Index && this.YCount == other.YCount && this.XCount == other.XCount
+ && this.MinVal == other.MinVal
+ && this.MaxVal == other.MaxVal;
+ if (!ok) return false;
+
+ for (int i = 0; i < this.YCount; i++)
+ {
+ for (int j = 0; j < this.XCount; j++)
+ {
+ Block a = this.Blocks[i, j];
+ Block b = other.Blocks[i, j];
+ if (!a.Equals(b)) return false;
+ }
+ }
+ return true;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((ComponentData)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = this.Index;
+ hashCode = (hashCode * 397) ^ this.YCount;
+ hashCode = (hashCode * 397) ^ this.XCount;
+ hashCode = (hashCode * 397) ^ this.MinVal.GetHashCode();
+ hashCode = (hashCode * 397) ^ this.MaxVal.GetHashCode();
+ return hashCode;
+ }
+ }
+
+ public static bool operator ==(ComponentData left, ComponentData right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(ComponentData left, ComponentData right)
+ {
+ return !Equals(left, right);
+ }
}
internal static FieldInfo GetNonPublicField(object obj, string fieldName)
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
index eeb879b8cc..51c996d85b 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
@@ -1,11 +1,14 @@
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
+ using System.Collections.Generic;
using System.IO;
+ using System.Linq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort;
using SixLabors.ImageSharp.PixelFormats;
+ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
using Xunit.Abstractions;
@@ -34,6 +37,8 @@ namespace SixLabors.ImageSharp.Tests
TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF
};
+ public static readonly string[] AllTestJpegs = BaselineTestJpegs.Concat(ProgressiveTestJpegs).ToArray();
+
[Theory]
[WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)]
public void BuildLibJpegSpectralResult(TestImageProvider provider)
@@ -68,14 +73,36 @@ namespace SixLabors.ImageSharp.Tests
this.SaveSpectralImage(provider, data);
}
}
-
+
+ [Theory]
+ [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)]
+ public void CompareSpectralResults(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ JpegDecoderCore decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder());
+
+ byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes;
+
+ using (var ms = new MemoryStream(sourceBytes))
+ {
+ decoder.ParseStream(ms);
+ var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder);
+
+ ms.Seek(0, SeekOrigin.Begin);
+ var libJpegData = LibJpegTools.SpectralData.Load(ms);
+
+ Assert.Equal(libJpegData, imageSharpData);
+ }
+ }
+
+
private void SaveSpectralImage(TestImageProvider provider, LibJpegTools.SpectralData data)
where TPixel : struct, IPixel
{
foreach (LibJpegTools.ComponentData comp in data.Components)
{
this.Output.WriteLine("Min: " + comp.MinVal);
- this.Output.WriteLine("MAx: " + comp.MaxVal);
+ this.Output.WriteLine("Max: " + comp.MaxVal);
using (Image image = comp.CreateGrayScaleImage())
{
@@ -83,6 +110,14 @@ namespace SixLabors.ImageSharp.Tests
image.DebugSave(provider, details, appendPixelTypeToFileName: false);
}
}
+
+ Image fullImage = data.TryCreateRGBSpectralImage();
+
+ if (fullImage != null)
+ {
+ fullImage.DebugSave(provider, "FULL", appendPixelTypeToFileName: false);
+ fullImage.Dispose();
+ }
}
}