Browse Source

CalculateJpegChannelSize()

af/merge-core
Anton Firszov 9 years ago
parent
commit
352198db4d
  1. 65
      src/ImageSharp/Formats/Jpeg/Common/ComponentUtils.cs
  2. 5
      src/ImageSharp/Formats/Jpeg/Common/IJpegComponent.cs
  3. 41
      src/ImageSharp/Formats/Jpeg/Common/SubsampleRatio.cs
  4. 20
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs
  5. 15
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegPixelArea.cs
  6. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/YCbCrImage.cs
  7. 23
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
  8. 14
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs
  9. 2
      src/ImageSharp/Formats/Jpeg/PdfJsPort/JpegDecoderCore.cs
  10. 9
      src/ImageSharp/Memory/Buffer2D.cs
  11. 2
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  12. 2
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
  13. 124
      tests/ImageSharp.Tests/Formats/Jpg/ComponentUtilsTests.cs
  14. 16
      tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
  15. 7
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  16. 4
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs
  17. 10
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs
  18. 4
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs
  19. 2
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
  20. 67
      tests/ImageSharp.Tests/Formats/Jpg/SubsampleRatioTests.cs
  21. 18
      tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs

65
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/SubsampleRatio.cs → src/ImageSharp/Formats/Jpeg/Common/ComponentUtils.cs

@ -1,54 +1,18 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
/// <summary> using System;
/// Provides enumeration of the various available subsample ratios.
/// https://en.wikipedia.org/wiki/Chroma_subsampling
/// </summary>
internal enum SubsampleRatio
{
Undefined,
/// <summary>
/// 4:4:4
/// </summary>
Ratio444,
/// <summary>
/// 4:2:2
/// </summary>
Ratio422,
/// <summary>
/// 4:2:0
/// </summary>
Ratio420,
/// <summary>
/// 4:4:0
/// </summary>
Ratio440,
/// <summary>
/// 4:1:1
/// </summary>
Ratio411,
/// <summary>
/// 4:1:0
/// </summary>
Ratio410,
}
/// <summary> /// <summary>
/// Various utilities for <see cref="SubsampleRatio"/> /// Various utilities for <see cref="SubsampleRatio"/> and <see cref="IJpegComponent"/>.
/// </summary> /// </summary>
internal static class Subsampling internal static class ComponentUtils
{ {
public static Size SizeInBlocks(this IJpegComponent component) => new Size(component.WidthInBlocks, component.HeightInBlocks);
public static SubsampleRatio GetSubsampleRatio(int horizontalRatio, int verticalRatio) public static SubsampleRatio GetSubsampleRatio(int horizontalRatio, int verticalRatio)
{ {
switch ((horizontalRatio << 4) | verticalRatio) switch ((horizontalRatio << 4) | verticalRatio)
@ -113,5 +77,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
return new Size(width, height); return new Size(width, height);
} }
} }
public static bool IsChromaComponent(this IJpegComponent component) =>
component.Index > 0 && component.Index < 3;
public static Size CalculateJpegChannelSize(this IJpegComponent component, SubsampleRatio ratio = SubsampleRatio.Undefined)
{
Size size = new Size(component.WidthInBlocks, component.HeightInBlocks) * 8;
if (component.IsChromaComponent())
{
return ratio.CalculateChrominanceSize(size.Width, size.Height);
}
else
{
return size;
}
}
} }
} }

5
src/ImageSharp/Formats/Jpeg/Common/IJpegComponent.cs

@ -2,6 +2,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
internal interface IJpegComponent internal interface IJpegComponent
{ {
/// <summary>
/// Gets the component's position in the components array.
/// </summary>
int Index { get; }
/// <summary> /// <summary>
/// Gets the number of blocks per line /// Gets the number of blocks per line
/// </summary> /// </summary>

41
src/ImageSharp/Formats/Jpeg/Common/SubsampleRatio.cs

@ -0,0 +1,41 @@
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
/// <summary>
/// Provides enumeration of the various available subsample ratios.
/// https://en.wikipedia.org/wiki/Chroma_subsampling
/// </summary>
internal enum SubsampleRatio
{
Undefined,
/// <summary>
/// 4:4:4
/// </summary>
Ratio444,
/// <summary>
/// 4:2:2
/// </summary>
Ratio422,
/// <summary>
/// 4:2:0
/// </summary>
Ratio420,
/// <summary>
/// 4:4:0
/// </summary>
Ratio440,
/// <summary>
/// 4:1:1
/// </summary>
Ratio411,
/// <summary>
/// 4:1:0
/// </summary>
Ratio410,
}
}

20
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs

@ -25,9 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary> /// </summary>
public byte Identifier { get; } public byte Identifier { get; }
/// <summary> /// <inheritdoc />
/// Gets the component's position in <see cref="OrigJpegDecoderCore.Components"/>
/// </summary>
public int Index { get; } public int Index { get; }
/// <inheritdoc /> /// <inheritdoc />
@ -44,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// <summary> /// <summary>
/// Gets the <see cref="Buffer{T}"/> storing the "raw" frequency-domain decoded blocks. /// Gets the <see cref="Buffer{T}"/> storing the "raw" frequency-domain decoded blocks.
/// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks.
/// This is done by <see cref="OrigJpegDecoderCore.ProcessBlocksIntoJpegImageChannels{TPixel}"/>. /// This is done by <see cref="OrigJpegDecoderCore.ProcessBlocksIntoJpegImageChannels"/>.
/// When <see cref="OrigJpegDecoderCore.IsProgressive"/> us true, we are touching these blocks multiple times - each time we process a Scan. /// When <see cref="OrigJpegDecoderCore.IsProgressive"/> us true, we are touching these blocks multiple times - each time we process a Scan.
/// </summary> /// </summary>
public Buffer2D<Block8x8> SpectralBlocks { get; private set; } public Buffer2D<Block8x8> SpectralBlocks { get; private set; }
@ -224,20 +222,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.VerticalSamplingFactor = v; this.VerticalSamplingFactor = v;
} }
public Size CalculateJpegChannelSize(SubsampleRatio ratio)
{
Size size = new Size(this.WidthInBlocks, this.HeightInBlocks) * 8;
if (this.Index > 0 && this.Index < 3) // Chroma component:
{
return ratio.CalculateChrominanceSize(size.Width, size.Height);
}
else
{
return size;
}
}
public void Dispose() public void Dispose()
{ {
this.SpectralBlocks.Dispose(); this.SpectralBlocks.Dispose();

15
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegPixelArea.cs

@ -5,9 +5,11 @@ using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F; using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{ {
/// <summary> /// <summary>
/// Represents an area of a Jpeg subimage (channel) /// Represents an area of a Jpeg subimage (channel)
/// </summary> /// </summary>
@ -36,6 +38,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{ {
} }
public OrigJpegPixelArea(Size size)
: this(Buffer2D<byte>.CreateClean(size))
{
}
/// <summary> /// <summary>
/// Gets the pixels buffer. /// Gets the pixels buffer.
/// </summary> /// </summary>
@ -76,6 +83,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
} }
} }
public static OrigJpegPixelArea CreateForComponent(IJpegComponent component, SubsampleRatio ratio = SubsampleRatio.Undefined)
{
Size size = component.CalculateJpegChannelSize(ratio);
return new OrigJpegPixelArea(size);
}
/// <summary> /// <summary>
/// Gets the subarea that belongs to the Block8x8 defined by block indices /// Gets the subarea that belongs to the Block8x8 defined by block indices
/// </summary> /// </summary>

2
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/YCbCrImage.cs

@ -7,6 +7,8 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{ {
using SixLabors.ImageSharp.Formats.Jpeg.Common;
/// <summary> /// <summary>
/// Represents an image made up of three color components (luminance, blue chroma, red chroma) /// Represents an image made up of three color components (luminance, blue chroma, red chroma)
/// </summary> /// </summary>

23
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs

@ -17,6 +17,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{ {
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Performs the jpeg decoding operation. /// Performs the jpeg decoding operation.
/// </summary> /// </summary>
@ -112,7 +115,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
} }
/// <summary> /// <summary>
/// Gets the <see cref="GolangPort.Components.Decoder.SubsampleRatio"/> ratio. /// Gets the <see cref="Common.SubsampleRatio"/> ratio.
/// </summary> /// </summary>
public SubsampleRatio SubsampleRatio { get; private set; } public SubsampleRatio SubsampleRatio { get; private set; }
@ -775,29 +778,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
} }
/// <summary> /// <summary>
/// Makes the image from the buffer. /// Initializes the image channels.
/// </summary> /// </summary>
private void InitJpegImageChannels() private void InitJpegImageChannels()
{ {
if (this.ComponentCount == 1) if (this.ComponentCount == 1)
{ {
var buffer = Buffer2D<byte>.CreateClean(8 * this.MCUCountX, 8 * this.MCUCountY); this.grayImage = OrigJpegPixelArea.CreateForComponent(this.Components[0]);
this.grayImage = new OrigJpegPixelArea(buffer);
} }
else else
{ {
int h0 = this.Components[0].HorizontalSamplingFactor; Size size = this.Components[0].CalculateJpegChannelSize();
int v0 = this.Components[0].VerticalSamplingFactor;
this.ycbcrImage = new YCbCrImage(8 * h0 * this.MCUCountX, 8 * v0 * this.MCUCountY, this.SubsampleRatio); this.ycbcrImage = new YCbCrImage(size.Width, size.Height, this.SubsampleRatio);
if (this.ComponentCount == 4) if (this.ComponentCount == 4)
{ {
int h3 = this.Components[3].HorizontalSamplingFactor; this.blackImage = OrigJpegPixelArea.CreateForComponent(this.Components[3]);
int v3 = this.Components[3].VerticalSamplingFactor;
var buffer = Buffer2D<byte>.CreateClean(8 * h3 * this.MCUCountX, 8 * v3 * this.MCUCountY);
this.blackImage = new OrigJpegPixelArea(buffer);
} }
} }
} }
@ -1198,7 +1195,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.Components[i].InitializeBlocks(this); this.Components[i].InitializeBlocks(this);
} }
this.SubsampleRatio = Subsampling.GetSubsampleRatio(this.Components); this.SubsampleRatio = ComponentUtils.GetSubsampleRatio(this.Components);
} }
} }
} }

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

@ -16,13 +16,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
{ {
#pragma warning disable SA1401 // Fields should be private #pragma warning disable SA1401 // Fields should be private
public PdfJsFrameComponent(PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationIdentifier) public PdfJsFrameComponent(PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationIdentifier, int index)
{ {
this.Frame = frame; this.Frame = frame;
this.Id = id; this.Id = id;
this.HorizontalSamplingFactor = horizontalFactor; this.HorizontalSamplingFactor = horizontalFactor;
this.VerticalSamplingFactor = verticalFactor; this.VerticalSamplingFactor = verticalFactor;
this.QuantizationIdentifier = quantizationIdentifier; this.QuantizationIdentifier = quantizationIdentifier;
this.Index = index;
} }
/// <summary> /// <summary>
@ -35,14 +36,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary> /// </summary>
public int Pred { get; set; } public int Pred { get; set; }
/// <summary> /// <inheritdoc />
/// Gets the horizontal sampling factor.
/// </summary>
public int HorizontalSamplingFactor { get; } public int HorizontalSamplingFactor { get; }
/// <summary> /// <inheritdoc />
/// Gets the vertical sampling factor.
/// </summary>
public int VerticalSamplingFactor { get; } public int VerticalSamplingFactor { get; }
/// <summary> /// <summary>
@ -55,6 +52,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// </summary> /// </summary>
public Buffer<short> BlockData { get; private set; } public Buffer<short> BlockData { get; private set; }
/// <inheritdoc />
public int Index { get; }
/// <summary> /// <summary>
/// Gets the number of blocks per line /// Gets the number of blocks per line
/// </summary> /// </summary>

2
src/ImageSharp/Formats/Jpeg/PdfJsPort/JpegDecoderCore.cs

@ -676,7 +676,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
maxV = v; maxV = v;
} }
var component = new PdfJsFrameComponent(this.Frame, this.temp[index], h, v, this.temp[index + 2]); var component = new PdfJsFrameComponent(this.Frame, this.temp[index], h, v, this.temp[index + 2], i);
this.Frame.Components[i] = component; this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id; this.Frame.ComponentIds[i] = component.Id;

9
src/ImageSharp/Memory/Buffer2D.cs

@ -5,6 +5,8 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory namespace SixLabors.ImageSharp.Memory
{ {
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Represents a buffer of value type objects /// Represents a buffer of value type objects
/// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements. /// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements.
@ -71,5 +73,12 @@ namespace SixLabors.ImageSharp.Memory
buffer.Clear(); buffer.Clear();
return buffer; return buffer;
} }
/// <summary>
/// Creates a clean instance of <see cref="Buffer2D{T}"/> initializing it's elements with 'default(T)'.
/// </summary>
/// <param name="size">The size of the buffer</param>
/// <returns>The <see cref="Buffer2D{T}"/> instance</returns>
public static Buffer2D<T> CreateClean(Size size) => CreateClean(size.Width, size.Height);
} }
} }

2
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
public class Block8x8FTests : JpegUtilityTestFixture public class Block8x8FTests : JpegFixture
{ {
#if BENCHMARKING #if BENCHMARKING
public const int Times = 1000000; public const int Times = 1000000;

2
tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs

@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
public class Block8x8Tests : JpegUtilityTestFixture public class Block8x8Tests : JpegFixture
{ {
public Block8x8Tests(ITestOutputHelper output) public Block8x8Tests(ITestOutputHelper output)
: base(output) : base(output)

124
tests/ImageSharp.Tests/Formats/Jpg/ComponentUtilsTests.cs

@ -0,0 +1,124 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.Primitives;
using Xunit;
using Xunit.Abstractions;
public class ComponentUtilsTests
{
public ComponentUtilsTests(ITestOutputHelper output)
{
this.Output = output;
}
private ITestOutputHelper Output { get; }
[Theory]
[InlineData(SubsampleRatio.Ratio410, 4, 2)]
[InlineData(SubsampleRatio.Ratio411, 4, 1)]
[InlineData(SubsampleRatio.Ratio420, 2, 2)]
[InlineData(SubsampleRatio.Ratio422, 2, 1)]
[InlineData(SubsampleRatio.Ratio440, 1, 2)]
[InlineData(SubsampleRatio.Ratio444, 1, 1)]
internal void CalculateChrominanceSize(
SubsampleRatio ratio,
int expectedDivX,
int expectedDivY)
{
//this.Output.WriteLine($"RATIO: {ratio}");
Size size = ratio.CalculateChrominanceSize(400, 400);
//this.Output.WriteLine($"Ch Size: {size}");
Assert.Equal(new Size(400 / expectedDivX, 400 / expectedDivY), size);
}
[Theory]
[InlineData(SubsampleRatio.Ratio410, 4)]
[InlineData(SubsampleRatio.Ratio411, 4)]
[InlineData(SubsampleRatio.Ratio420, 2)]
[InlineData(SubsampleRatio.Ratio422, 2)]
[InlineData(SubsampleRatio.Ratio440, 1)]
[InlineData(SubsampleRatio.Ratio444, 1)]
internal void Create(SubsampleRatio ratio, int expectedCStrideDiv)
{
this.Output.WriteLine($"RATIO: {ratio}");
YCbCrImage img = new YCbCrImage(400, 400, ratio);
//this.PrintChannel("Y", img.YChannel);
//this.PrintChannel("Cb", img.CbChannel);
//this.PrintChannel("Cr", img.CrChannel);
Assert.Equal(400, img.YChannel.Width);
Assert.Equal(img.CbChannel.Width, 400 / expectedCStrideDiv);
Assert.Equal(img.CrChannel.Width, 400 / expectedCStrideDiv);
}
private void PrintChannel(string name, OrigJpegPixelArea channel)
{
this.Output.WriteLine($"{name}: Stride={channel.Stride}");
}
[Fact]
public void CalculateJpegChannelSize_Grayscale()
{
using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(TestImages.Jpeg.Baseline.Jpeg400))
{
Assert.Equal(1, decoder.ComponentCount);
Size expected = decoder.Components[0].SizeInBlocks() * 8;
Size actual = decoder.Components[0].CalculateJpegChannelSize(decoder.SubsampleRatio);
Assert.Equal(expected, actual);
}
}
[Theory]
[InlineData(TestImages.Jpeg.Baseline.Calliphora, 1)]
[InlineData(TestImages.Jpeg.Baseline.Jpeg444, 1)]
[InlineData(TestImages.Jpeg.Baseline.Jpeg420, 2)]
public void CalculateJpegChannelSize_YCbCr(
string imageFile,
int chromaDiv)
{
using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile))
{
Size ySize = decoder.Components[0].SizeInBlocks() * 8;
Size cSize = decoder.Components[1].SizeInBlocks() * 8 / chromaDiv;
Size s0 = decoder.Components[0].CalculateJpegChannelSize(decoder.SubsampleRatio);
Size s1 = decoder.Components[1].CalculateJpegChannelSize(decoder.SubsampleRatio);
Size s2 = decoder.Components[2].CalculateJpegChannelSize(decoder.SubsampleRatio);
Assert.Equal(ySize, s0);
Assert.Equal(cSize, s1);
Assert.Equal(cSize, s2);
}
}
[Theory]
[InlineData(TestImages.Jpeg.Baseline.Ycck)]
[InlineData(TestImages.Jpeg.Baseline.Cmyk)]
public void CalculateJpegChannelSize_4Chan(string imageFile)
{
using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile))
{
Size expected = decoder.Components[0].SizeInBlocks() * 8;
foreach (OrigComponent component in decoder.Components)
{
Size actual = component.CalculateJpegChannelSize(decoder.SubsampleRatio);
Assert.Equal(expected, actual);
}
}
}
}
}

16
tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public static class DCTTests public static class DCTTests
{ {
public class FastFloatingPoint : JpegUtilityTestFixture public class FastFloatingPoint : JpegFixture
{ {
public FastFloatingPoint(ITestOutputHelper output) public FastFloatingPoint(ITestOutputHelper output)
: base(output) : base(output)
@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Fact] [Fact]
public void iDCT2D8x4_LeftPart() public void iDCT2D8x4_LeftPart()
{ {
float[] sourceArray = JpegUtilityTestFixture.Create8x8FloatData(); float[] sourceArray = JpegFixture.Create8x8FloatData();
float[] expectedDestArray = new float[64]; float[] expectedDestArray = new float[64];
ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray, expectedDestArray); ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray, expectedDestArray);
@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Fact] [Fact]
public void iDCT2D8x4_RightPart() public void iDCT2D8x4_RightPart()
{ {
float[] sourceArray = JpegUtilityTestFixture.Create8x8FloatData(); float[] sourceArray = JpegFixture.Create8x8FloatData();
float[] expectedDestArray = new float[64]; float[] expectedDestArray = new float[64];
ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray.AsSpan().Slice(4), expectedDestArray.AsSpan().Slice(4)); ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray.AsSpan().Slice(4), expectedDestArray.AsSpan().Slice(4));
@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(3)] [InlineData(3)]
public void LLM_TransformIDCT_CompareToNonOptimized(int seed) public void LLM_TransformIDCT_CompareToNonOptimized(int seed)
{ {
float[] sourceArray = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed); float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed);
var source = Block8x8F.Load(sourceArray); var source = Block8x8F.Load(sourceArray);
@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(3)] [InlineData(3)]
public void LLM_TransformIDCT_CompareToAccurate(int seed) public void LLM_TransformIDCT_CompareToAccurate(int seed)
{ {
float[] sourceArray = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed); float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed);
var source = Block8x8F.Load(sourceArray); var source = Block8x8F.Load(sourceArray);
@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)] [InlineData(2)]
public void FDCT8x4_LeftPart(int seed) public void FDCT8x4_LeftPart(int seed)
{ {
Span<float> src = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); Span<float> src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F(); Block8x8F srcBlock = new Block8x8F();
srcBlock.LoadFrom(src); srcBlock.LoadFrom(src);
@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)] [InlineData(2)]
public void FDCT8x4_RightPart(int seed) public void FDCT8x4_RightPart(int seed)
{ {
Span<float> src = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); Span<float> src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F(); Block8x8F srcBlock = new Block8x8F();
srcBlock.LoadFrom(src); srcBlock.LoadFrom(src);
@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)] [InlineData(2)]
public void TransformFDCT(int seed) public void TransformFDCT(int seed)
{ {
Span<float> src = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); Span<float> src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F(); Block8x8F srcBlock = new Block8x8F();
srcBlock.LoadFrom(src); srcBlock.LoadFrom(src);

7
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -65,12 +65,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Fact(Skip = "Doesn't really matter")] [Fact(Skip = "Doesn't really matter")]
public void ParseStream_BasicPropertiesAreCorrect1_Orig() public void ParseStream_BasicPropertiesAreCorrect1_Orig()
{ {
byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(TestImages.Jpeg.Progressive.Progress))
using (var ms = new MemoryStream(bytes))
{ {
var decoder = new OrigJpegDecoderCore(Configuration.Default, new JpegDecoder());
decoder.ParseStream(ms);
VerifyJpeg.Components3(decoder.Components, 43, 61, 22, 31, 22, 31); VerifyJpeg.Components3(decoder.Components, 43, 61, 22, 31, 22, 31);
} }
} }
@ -95,7 +91,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void DecodeBaselineJpeg_PdfJs<TPixel>(TestImageProvider<TPixel> provider) public void DecodeBaselineJpeg_PdfJs<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(PdfJsJpegDecoder)) using (Image<TPixel> image = provider.GetImage(PdfJsJpegDecoder))
{ {
image.DebugSave(provider); image.DebugSave(provider);

4
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public partial class ReferenceImplementationsTests public partial class ReferenceImplementationsTests
{ {
public class AccurateDCT : JpegUtilityTestFixture public class AccurateDCT : JpegFixture
{ {
public AccurateDCT(ITestOutputHelper output) public AccurateDCT(ITestOutputHelper output)
: base(output) : base(output)
@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)] [InlineData(2)]
public void ForwardThenInverse(int seed) public void ForwardThenInverse(int seed)
{ {
float[] data = JpegUtilityTestFixture.Create8x8RandomFloatData(-1000, 1000, seed); float[] data = JpegFixture.Create8x8RandomFloatData(-1000, 1000, seed);
var b0 = default(Block8x8F); var b0 = default(Block8x8F);
b0.LoadFrom(data); b0.LoadFrom(data);

10
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public partial class ReferenceImplementationsTests public partial class ReferenceImplementationsTests
{ {
public class FastFloatingPointDCT : JpegUtilityTestFixture public class FastFloatingPointDCT : JpegFixture
{ {
public FastFloatingPointDCT(ITestOutputHelper output) public FastFloatingPointDCT(ITestOutputHelper output)
: base(output) : base(output)
@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2, 0)] [InlineData(2, 0)]
public void LLM_ForwardThenInverse(int seed, int startAt) public void LLM_ForwardThenInverse(int seed, int startAt)
{ {
int[] data = JpegUtilityTestFixture.Create8x8RandomIntData(-1000, 1000, seed); int[] data = JpegFixture.Create8x8RandomIntData(-1000, 1000, seed);
float[] original = data.ConvertAllToFloat(); float[] original = data.ConvertAllToFloat();
float[] src = data.ConvertAllToFloat(); float[] src = data.ConvertAllToFloat();
float[] dest = new float[64]; float[] dest = new float[64];
@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2, 200)] [InlineData(2, 200)]
public void LLM_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range) public void LLM_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range)
{ {
float[] sourceArray = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed); float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed);
var source = Block8x8F.Load(sourceArray); var source = Block8x8F.Load(sourceArray);
@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)] [InlineData(2)]
public void LLM_FDCT_IsEquivalentTo_AccurateImplementation(int seed) public void LLM_FDCT_IsEquivalentTo_AccurateImplementation(int seed)
{ {
float[] floatData = JpegUtilityTestFixture.Create8x8RandomFloatData(-1000, 1000); float[] floatData = JpegFixture.Create8x8RandomFloatData(-1000, 1000);
Block8x8F source = default(Block8x8F); Block8x8F source = default(Block8x8F);
source.LoadFrom(floatData); source.LoadFrom(floatData);
@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2, 200)] [InlineData(2, 200)]
public void GT_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range) public void GT_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range)
{ {
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-range, range, seed); int[] intData = JpegFixture.Create8x8RandomIntData(-range, range, seed);
float[] floatSrc = intData.ConvertAllToFloat(); float[] floatSrc = intData.ConvertAllToFloat();
ReferenceImplementations.AccurateDCT.TransformIDCTInplace(intData); ReferenceImplementations.AccurateDCT.TransformIDCTInplace(intData);

4
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public partial class ReferenceImplementationsTests public partial class ReferenceImplementationsTests
{ {
public class StandardIntegerDCT : JpegUtilityTestFixture public class StandardIntegerDCT : JpegFixture
{ {
public StandardIntegerDCT(ITestOutputHelper output) public StandardIntegerDCT(ITestOutputHelper output)
: base(output) : base(output)
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2, 0)] [InlineData(2, 0)]
public void ForwardThenInverse(int seed, int startAt) public void ForwardThenInverse(int seed, int startAt)
{ {
Span<int> original = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed); Span<int> original = JpegFixture.Create8x8RandomIntData(-200, 200, seed);
Span<int> block = original.AddScalarToAllValues(128); Span<int> block = original.AddScalarToAllValues(128);

2
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
public partial class ReferenceImplementationsTests : JpegUtilityTestFixture public partial class ReferenceImplementationsTests : JpegFixture
{ {
public ReferenceImplementationsTests(ITestOutputHelper output) public ReferenceImplementationsTests(ITestOutputHelper output)
: base(output) : base(output)

67
tests/ImageSharp.Tests/Formats/Jpg/SubsampleRatioTests.cs

@ -1,67 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder;
using SixLabors.Primitives;
using Xunit;
using Xunit.Abstractions;
public class SubsampleRatioTests
{
public SubsampleRatioTests(ITestOutputHelper output)
{
this.Output = output;
}
private ITestOutputHelper Output { get; }
[Theory]
[InlineData(SubsampleRatio.Ratio410, 4, 2)]
[InlineData(SubsampleRatio.Ratio411, 4, 1)]
[InlineData(SubsampleRatio.Ratio420, 2, 2)]
[InlineData(SubsampleRatio.Ratio422, 2, 1)]
[InlineData(SubsampleRatio.Ratio440, 1, 2)]
[InlineData(SubsampleRatio.Ratio444, 1, 1)]
internal void CalculateChrominanceSize(
SubsampleRatio ratio,
int expectedDivX,
int expectedDivY)
{
//this.Output.WriteLine($"RATIO: {ratio}");
Size size = ratio.CalculateChrominanceSize(400, 400);
//this.Output.WriteLine($"Ch Size: {size}");
Assert.Equal(new Size(400 / expectedDivX, 400 / expectedDivY), size);
}
[Theory]
[InlineData(SubsampleRatio.Ratio410, 4)]
[InlineData(SubsampleRatio.Ratio411, 4)]
[InlineData(SubsampleRatio.Ratio420, 2)]
[InlineData(SubsampleRatio.Ratio422, 2)]
[InlineData(SubsampleRatio.Ratio440, 1)]
[InlineData(SubsampleRatio.Ratio444, 1)]
internal void Create(SubsampleRatio ratio, int expectedCStrideDiv)
{
this.Output.WriteLine($"RATIO: {ratio}");
YCbCrImage img = new YCbCrImage(400, 400, ratio);
//this.PrintChannel("Y", img.YChannel);
//this.PrintChannel("Cb", img.CbChannel);
//this.PrintChannel("Cr", img.CrChannel);
Assert.Equal(400, img.YChannel.Width);
Assert.Equal(img.CbChannel.Width, 400 / expectedCStrideDiv);
Assert.Equal(img.CrChannel.Width, 400 / expectedCStrideDiv);
}
private void PrintChannel(string name, OrigJpegPixelArea channel)
{
this.Output.WriteLine($"{name}: Stride={channel.Stride}");
}
}
}

18
tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegUtilityTestFixture.cs → tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs

@ -9,16 +9,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
{ {
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Jpeg.Common; using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
public class JpegUtilityTestFixture : MeasureFixture public class JpegFixture : MeasureFixture
{ {
public JpegUtilityTestFixture(ITestOutputHelper output) : base(output) public JpegFixture(ITestOutputHelper output) : base(output)
{ {
} }
@ -166,5 +169,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
this.Output.WriteLine("TOTAL DIFF: "+totalDifference); this.Output.WriteLine("TOTAL DIFF: "+totalDifference);
Assert.False(failed); Assert.False(failed);
} }
internal static OrigJpegDecoderCore ParseStream(string testFileName)
{
byte[] bytes = TestFile.Create(testFileName).Bytes;
using (var ms = new MemoryStream(bytes))
{
var decoder = new OrigJpegDecoderCore(Configuration.Default, new JpegDecoder());
decoder.ParseStream(ms);
return decoder;
}
}
} }
} }
Loading…
Cancel
Save