Browse Source

Merge branch 'main' into js/parallel-cleanup

pull/3113/head
James Jackson-South 3 weeks ago
committed by GitHub
parent
commit
7b28e25caa
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 326
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs
  2. 40
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Vector128.cs
  3. 26
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Vector256.cs
  4. 79
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  5. 8
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DirectComponentProcessor.cs
  6. 339
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs
  7. 281
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs
  8. 164
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs
  9. 14
      src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs
  10. 71
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  11. 10
      tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
  12. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
  13. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs
  14. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  15. 14
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  16. 4
      tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SeparateChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png
  17. 4
      tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SynchronizedChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png
  18. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Calliphora.png
  19. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Issue394-MultiHuffmanBaseline-Speakers.png
  20. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_MultiScanBaselineCMYK.png
  21. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badeof.png
  22. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badrst.png
  23. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_cmyk.png
  24. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg420small.png
  25. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg422.png
  26. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg444.png
  27. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig.png
  28. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig12.png
  29. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck-subsample-1222.png
  30. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck.png
  31. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_BadEofProgressive.png
  32. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_ExifUndefType.png
  33. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_fb.png
  34. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_progress.png
  35. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_CMYK_ICC_Jpeg_Rgba32_issue-129.png
  36. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-AdobeRGB-yes.png
  37. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-AppleRGB-yes.png
  38. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-ColorMatch-yes.png
  39. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-ProPhoto-yes.png
  40. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-WideRGB-yes.png
  41. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-sRGB-yes.png
  42. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Perceptual-cLUT-only.png
  43. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Perceptual.png
  44. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_sRGB_Gray.png
  45. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_YCCK_ICC_Jpeg_Rgba32_issue_2723.png
  46. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Bicubic_Calliphora_150_150.png
  47. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Calliphora_150_150.png
  48. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Combined_Resize_Calliphora_150_150.png
  49. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_IDCT_Resize_Calliphora_150_150.png
  50. 4
      tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Scale_Resize_Calliphora_150_150.png
  51. 4
      tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_Cmyk_Rgba32_Cmyk-jpeg.png
  52. 4
      tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_JpegCompressedWithIssue2679_Issue2679.png
  53. 4
      tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_ICC_Rgba32_Issue2454_A.png
  54. 4
      tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_ICC_Rgba32_Issue2454_B.png
  55. 4
      tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_Rgba32_Issue2454_A.png
  56. 4
      tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_Rgba32_Issue2454_B.png
  57. 4
      tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.25.png
  58. 4
      tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.75.png

326
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs

@ -14,7 +14,7 @@ internal partial struct Block8x8F
public void ScaledCopyFrom(ref float areaOrigin, int areaStride) =>
CopyFrom1x1Scale(ref Unsafe.As<float, byte>(ref areaOrigin), ref Unsafe.As<Block8x8F, byte>(ref this), areaStride);
[MethodImpl(InliningOptions.ShortMethod)]
[MethodImpl(InliningOptions.ColdPath)]
public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
{
if (horizontalScale == 1 && verticalScale == 1)
@ -29,7 +29,50 @@ internal partial struct Block8x8F
return;
}
// TODO: Optimize: implement all cases with scale-specific, loopless code!
if (horizontalScale == 2 && verticalScale == 1)
{
this.CopyTo2x1Scale(ref areaOrigin, (uint)areaStride);
return;
}
if (horizontalScale == 1 && verticalScale == 2)
{
this.CopyTo1x2Scale(ref areaOrigin, (uint)areaStride);
return;
}
if (horizontalScale == 4 && verticalScale == 1)
{
this.CopyTo4x1Scale(ref areaOrigin, (uint)areaStride);
return;
}
if (horizontalScale == 4 && verticalScale == 2)
{
this.CopyTo4x2Scale(ref areaOrigin, (uint)areaStride);
return;
}
if (horizontalScale == 1 && verticalScale == 4)
{
this.CopyTo1x4Scale(ref areaOrigin, (uint)areaStride);
return;
}
if (horizontalScale == 2 && verticalScale == 4)
{
this.CopyTo2x4Scale(ref areaOrigin, (uint)areaStride);
return;
}
if (horizontalScale == 4 && verticalScale == 4)
{
this.CopyTo4x4Scale(ref areaOrigin, (uint)areaStride);
return;
}
// The common 1x, 2x, and 4x integral scales are specialized above.
// Uncommon legal factor-3 scales use the generic fallback.
this.CopyArbitraryScale(ref areaOrigin, (uint)areaStride, (uint)horizontalScale, (uint)verticalScale);
}
@ -85,6 +128,285 @@ internal partial struct Block8x8F
}
}
/// <summary>
/// Copies the full 8x8 block into the destination buffer while doubling only the horizontal axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private void CopyTo2x1Scale(ref float areaOrigin, uint areaStride)
{
ref Vector4 sourceBase = ref this.V0L;
WidenRow8(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 2u, 2u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 3u, 3u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 4u, 4u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 5u, 5u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 6u, 6u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 7u, 7u, areaStride);
}
/// <summary>
/// Copies the full 8x8 block into the destination buffer while doubling only the vertical axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private void CopyTo1x2Scale(ref float areaOrigin, uint areaStride)
{
ref Vector4 sourceBase = ref this.V0L;
CopyRow8(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 2u, 4u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 2u, 5u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 3u, 6u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 3u, 7u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 4u, 8u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 4u, 9u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 5u, 10u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 5u, 11u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 6u, 12u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 6u, 13u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 7u, 14u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 7u, 15u, areaStride);
}
/// <summary>
/// Copies the full 8x8 block into the destination buffer while quadrupling only the horizontal axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private void CopyTo4x1Scale(ref float areaOrigin, uint areaStride)
{
ref Vector4 sourceBase = ref this.V0L;
ExpandRow8(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 2u, 2u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 3u, 3u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 4u, 4u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 5u, 5u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 6u, 6u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 7u, 7u, areaStride);
}
/// <summary>
/// Copies the full 8x8 block into the destination buffer while quadrupling horizontally and doubling vertically.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private void CopyTo4x2Scale(ref float areaOrigin, uint areaStride)
{
ref Vector4 sourceBase = ref this.V0L;
ExpandRow8(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 2u, 4u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 2u, 5u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 3u, 6u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 3u, 7u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 4u, 8u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 4u, 9u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 5u, 10u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 5u, 11u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 6u, 12u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 6u, 13u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 7u, 14u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 7u, 15u, areaStride);
}
/// <summary>
/// Copies the full 8x8 block into the destination buffer while quadrupling only the vertical axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private void CopyTo1x4Scale(ref float areaOrigin, uint areaStride)
{
ref Vector4 sourceBase = ref this.V0L;
CopyRow8(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 2u, 8u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 2u, 9u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 2u, 10u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 2u, 11u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 3u, 12u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 3u, 13u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 3u, 14u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 3u, 15u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 4u, 16u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 4u, 17u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 4u, 18u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 4u, 19u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 5u, 20u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 5u, 21u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 5u, 22u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 5u, 23u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 6u, 24u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 6u, 25u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 6u, 26u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 6u, 27u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 7u, 28u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 7u, 29u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 7u, 30u, areaStride);
CopyRow8(ref sourceBase, ref areaOrigin, 7u, 31u, areaStride);
}
/// <summary>
/// Copies the full 8x8 block into the destination buffer while doubling horizontally and quadrupling vertically.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private void CopyTo2x4Scale(ref float areaOrigin, uint areaStride)
{
ref Vector4 sourceBase = ref this.V0L;
WidenRow8(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 2u, 8u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 2u, 9u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 2u, 10u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 2u, 11u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 3u, 12u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 3u, 13u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 3u, 14u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 3u, 15u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 4u, 16u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 4u, 17u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 4u, 18u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 4u, 19u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 5u, 20u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 5u, 21u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 5u, 22u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 5u, 23u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 6u, 24u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 6u, 25u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 6u, 26u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 6u, 27u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 7u, 28u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 7u, 29u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 7u, 30u, areaStride);
WidenRow8(ref sourceBase, ref areaOrigin, 7u, 31u, areaStride);
}
/// <summary>
/// Copies the full 8x8 block into the destination buffer while quadrupling both axes.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private void CopyTo4x4Scale(ref float areaOrigin, uint areaStride)
{
ref Vector4 sourceBase = ref this.V0L;
ExpandRow8(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 2u, 8u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 2u, 9u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 2u, 10u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 2u, 11u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 3u, 12u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 3u, 13u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 3u, 14u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 3u, 15u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 4u, 16u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 4u, 17u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 4u, 18u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 4u, 19u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 5u, 20u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 5u, 21u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 5u, 22u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 5u, 23u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 6u, 24u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 6u, 25u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 6u, 26u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 6u, 27u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 7u, 28u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 7u, 29u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 7u, 30u, areaStride);
ExpandRow8(ref sourceBase, ref areaOrigin, 7u, 31u, areaStride);
}
/// <summary>
/// Copies one eight-sample row from the full block to the destination row.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyRow8(ref Vector4 sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
{
ref Vector4 source = ref Unsafe.Add(ref sourceBase, sourceRow * 2u);
ref Vector4 dest = ref Unsafe.As<float, Vector4>(ref Unsafe.Add(ref areaOrigin, destRow * areaStride));
dest = source;
Unsafe.Add(ref dest, 1u) = Unsafe.Add(ref source, 1u);
}
/// <summary>
/// Expands one eight-sample row to sixteen samples by duplicating each source value horizontally.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void WidenRow8(ref Vector4 sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
{
ref Vector4 sourceLeft = ref Unsafe.Add(ref sourceBase, sourceRow * 2u);
ref Vector4 sourceRight = ref Unsafe.Add(ref sourceLeft, 1u);
ref Vector4 dest = ref Unsafe.As<float, Vector4>(ref Unsafe.Add(ref areaOrigin, destRow * areaStride));
Vector4 xyLeft = new(sourceLeft.X);
xyLeft.Z = sourceLeft.Y;
xyLeft.W = sourceLeft.Y;
Vector4 zwLeft = new(sourceLeft.Z);
zwLeft.Z = sourceLeft.W;
zwLeft.W = sourceLeft.W;
Vector4 xyRight = new(sourceRight.X);
xyRight.Z = sourceRight.Y;
xyRight.W = sourceRight.Y;
Vector4 zwRight = new(sourceRight.Z);
zwRight.Z = sourceRight.W;
zwRight.W = sourceRight.W;
dest = xyLeft;
Unsafe.Add(ref dest, 1u) = zwLeft;
Unsafe.Add(ref dest, 2u) = xyRight;
Unsafe.Add(ref dest, 3u) = zwRight;
}
/// <summary>
/// Expands one eight-sample row to thirty-two samples by duplicating each source value four times horizontally.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void ExpandRow8(ref Vector4 sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
{
ref Vector4 sourceLeft = ref Unsafe.Add(ref sourceBase, sourceRow * 2u);
ref Vector4 sourceRight = ref Unsafe.Add(ref sourceLeft, 1u);
ref Vector4 dest = ref Unsafe.As<float, Vector4>(ref Unsafe.Add(ref areaOrigin, destRow * areaStride));
dest = new Vector4(sourceLeft.X);
Unsafe.Add(ref dest, 1u) = new Vector4(sourceLeft.Y);
Unsafe.Add(ref dest, 2u) = new Vector4(sourceLeft.Z);
Unsafe.Add(ref dest, 3u) = new Vector4(sourceLeft.W);
Unsafe.Add(ref dest, 4u) = new Vector4(sourceRight.X);
Unsafe.Add(ref dest, 5u) = new Vector4(sourceRight.Y);
Unsafe.Add(ref dest, 6u) = new Vector4(sourceRight.Z);
Unsafe.Add(ref dest, 7u) = new Vector4(sourceRight.W);
}
[MethodImpl(InliningOptions.ColdPath)]
private void CopyArbitraryScale(ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale)
{

40
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Vector128.cs

@ -13,31 +13,31 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components;
internal partial struct Block8x8F
{
/// <summary>
/// <see cref="Vector128{Single}"/> version of <see cref="NormalizeColorsInPlace(float)"/> and <see cref="RoundInPlace()"/>.
/// <see cref="Vector128{Single}"/> version of <see cref="NormalizeColorsInPlace(float)"/>.
/// </summary>
/// <param name="maximum">The maximum value to normalize to.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInPlaceVector128(float maximum)
public void NormalizeColorsInPlaceVector128(float maximum)
{
Vector128<float> max = Vector128.Create(maximum);
Vector128<float> off = Vector128.Ceiling(max * .5F);
this.V0L = NormalizeAndRoundVector128(this.V0L.AsVector128(), off, max).AsVector4();
this.V0R = NormalizeAndRoundVector128(this.V0R.AsVector128(), off, max).AsVector4();
this.V1L = NormalizeAndRoundVector128(this.V1L.AsVector128(), off, max).AsVector4();
this.V1R = NormalizeAndRoundVector128(this.V1R.AsVector128(), off, max).AsVector4();
this.V2L = NormalizeAndRoundVector128(this.V2L.AsVector128(), off, max).AsVector4();
this.V2R = NormalizeAndRoundVector128(this.V2R.AsVector128(), off, max).AsVector4();
this.V3L = NormalizeAndRoundVector128(this.V3L.AsVector128(), off, max).AsVector4();
this.V3R = NormalizeAndRoundVector128(this.V3R.AsVector128(), off, max).AsVector4();
this.V4L = NormalizeAndRoundVector128(this.V4L.AsVector128(), off, max).AsVector4();
this.V4R = NormalizeAndRoundVector128(this.V4R.AsVector128(), off, max).AsVector4();
this.V5L = NormalizeAndRoundVector128(this.V5L.AsVector128(), off, max).AsVector4();
this.V5R = NormalizeAndRoundVector128(this.V5R.AsVector128(), off, max).AsVector4();
this.V6L = NormalizeAndRoundVector128(this.V6L.AsVector128(), off, max).AsVector4();
this.V6R = NormalizeAndRoundVector128(this.V6R.AsVector128(), off, max).AsVector4();
this.V7L = NormalizeAndRoundVector128(this.V7L.AsVector128(), off, max).AsVector4();
this.V7R = NormalizeAndRoundVector128(this.V7R.AsVector128(), off, max).AsVector4();
this.V0L = NormalizeVector128(this.V0L.AsVector128(), off, max).AsVector4();
this.V0R = NormalizeVector128(this.V0R.AsVector128(), off, max).AsVector4();
this.V1L = NormalizeVector128(this.V1L.AsVector128(), off, max).AsVector4();
this.V1R = NormalizeVector128(this.V1R.AsVector128(), off, max).AsVector4();
this.V2L = NormalizeVector128(this.V2L.AsVector128(), off, max).AsVector4();
this.V2R = NormalizeVector128(this.V2R.AsVector128(), off, max).AsVector4();
this.V3L = NormalizeVector128(this.V3L.AsVector128(), off, max).AsVector4();
this.V3R = NormalizeVector128(this.V3R.AsVector128(), off, max).AsVector4();
this.V4L = NormalizeVector128(this.V4L.AsVector128(), off, max).AsVector4();
this.V4R = NormalizeVector128(this.V4R.AsVector128(), off, max).AsVector4();
this.V5L = NormalizeVector128(this.V5L.AsVector128(), off, max).AsVector4();
this.V5R = NormalizeVector128(this.V5R.AsVector128(), off, max).AsVector4();
this.V6L = NormalizeVector128(this.V6L.AsVector128(), off, max).AsVector4();
this.V6R = NormalizeVector128(this.V6R.AsVector128(), off, max).AsVector4();
this.V7L = NormalizeVector128(this.V7L.AsVector128(), off, max).AsVector4();
this.V7R = NormalizeVector128(this.V7R.AsVector128(), off, max).AsVector4();
}
/// <summary>
@ -71,8 +71,8 @@ internal partial struct Block8x8F
}
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector128<float> NormalizeAndRoundVector128(Vector128<float> value, Vector128<float> off, Vector128<float> max)
=> Vector128_.RoundToNearestInteger(Vector128_.Clamp(value + off, Vector128<float>.Zero, max));
private static Vector128<float> NormalizeVector128(Vector128<float> value, Vector128<float> off, Vector128<float> max)
=> Vector128_.Clamp(value + off, Vector128<float>.Zero, max);
private static void MultiplyIntoInt16Vector128(ref Block8x8F a, ref Block8x8F b, ref Block8x8 dest)
{

26
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Vector256.cs

@ -39,23 +39,23 @@ internal partial struct Block8x8F
#pragma warning restore SA1310 // Field names should not contain underscore
/// <summary>
/// <see cref="Vector256{Single}"/> version of <see cref="NormalizeColorsInPlace(float)"/> and <see cref="RoundInPlace()"/>.
/// <see cref="Vector256{Single}"/> version of <see cref="NormalizeColorsInPlace(float)"/>.
/// </summary>
/// <param name="maximum">The maximum value to normalize to.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInPlaceVector256(float maximum)
public void NormalizeColorsInPlaceVector256(float maximum)
{
Vector256<float> max = Vector256.Create(maximum);
Vector256<float> off = Vector256.Ceiling(max * .5F);
this.V256_0 = NormalizeAndRoundVector256(this.V256_0, off, max);
this.V256_1 = NormalizeAndRoundVector256(this.V256_1, off, max);
this.V256_2 = NormalizeAndRoundVector256(this.V256_2, off, max);
this.V256_3 = NormalizeAndRoundVector256(this.V256_3, off, max);
this.V256_4 = NormalizeAndRoundVector256(this.V256_4, off, max);
this.V256_5 = NormalizeAndRoundVector256(this.V256_5, off, max);
this.V256_6 = NormalizeAndRoundVector256(this.V256_6, off, max);
this.V256_7 = NormalizeAndRoundVector256(this.V256_7, off, max);
this.V256_0 = NormalizeVector256(this.V256_0, off, max);
this.V256_1 = NormalizeVector256(this.V256_1, off, max);
this.V256_2 = NormalizeVector256(this.V256_2, off, max);
this.V256_3 = NormalizeVector256(this.V256_3, off, max);
this.V256_4 = NormalizeVector256(this.V256_4, off, max);
this.V256_5 = NormalizeVector256(this.V256_5, off, max);
this.V256_6 = NormalizeVector256(this.V256_6, off, max);
this.V256_7 = NormalizeVector256(this.V256_7, off, max);
}
/// <summary>
@ -95,10 +95,10 @@ internal partial struct Block8x8F
}
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector256<float> NormalizeAndRoundVector256(Vector256<float> value, Vector256<float> off, Vector256<float> max)
=> Vector256_.RoundToNearestInteger(Vector256_.Clamp(value + off, Vector256<float>.Zero, max));
private static Vector256<float> NormalizeVector256(Vector256<float> value, Vector256<float> off, Vector256<float> max)
=> Vector256_.Clamp(value + off, Vector256<float>.Zero, max);
private static unsafe void MultiplyIntoInt16Vector256(ref Block8x8F a, ref Block8x8F b, ref Block8x8 dest)
private static void MultiplyIntoInt16Vector256(ref Block8x8F a, ref Block8x8F b, ref Block8x8 dest)
{
DebugGuard.IsTrue(Vector256.IsHardwareAccelerated, "Vector256 support is required to run this operation!");

79
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -132,7 +132,7 @@ internal partial struct Block8x8F : IEquatable<Block8x8F>
/// </summary>
/// <param name="dest">Destination</param>
[MethodImpl(InliningOptions.ShortMethod)]
public unsafe void ScaledCopyTo(float[] dest)
public readonly void ScaledCopyTo(float[] dest)
{
DebugGuard.MustBeGreaterThanOrEqualTo(dest.Length, Size, "dest is too small");
@ -193,7 +193,7 @@ internal partial struct Block8x8F : IEquatable<Block8x8F>
/// </summary>
/// <param name="other">The other block.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public unsafe void MultiplyInPlace(ref Block8x8F other)
public void MultiplyInPlace(ref Block8x8F other)
{
if (Vector256.IsHardwareAccelerated)
{
@ -324,62 +324,43 @@ internal partial struct Block8x8F : IEquatable<Block8x8F>
}
/// <summary>
/// Level shift by +maximum/2, clip to [0..maximum], and round all the values in the block.
/// Level shift by +maximum/2, clip to [0, maximum]
/// </summary>
/// <param name="maximum">The maximum value.</param>
public void NormalizeColorsAndRoundInPlace(float maximum)
/// <param name="maximum">The maximum value to normalize to.</param>
public void NormalizeColorsInPlace(float maximum)
{
if (Vector256.IsHardwareAccelerated)
{
this.NormalizeColorsAndRoundInPlaceVector256(maximum);
this.NormalizeColorsInPlaceVector256(maximum);
return;
}
else if (Vector128.IsHardwareAccelerated)
{
this.NormalizeColorsAndRoundInPlaceVector128(maximum);
this.NormalizeColorsInPlaceVector128(maximum);
return;
}
else
{
this.NormalizeColorsInPlace(maximum);
this.RoundInPlace();
}
}
/// <summary>
/// Level shift by +maximum/2, clip to [0, maximum]
/// </summary>
/// <param name="maximum">The maximum value to normalize to.</param>
public void NormalizeColorsInPlace(float maximum)
{
Vector4 min = Vector4.Zero;
Vector4 max = new(maximum);
Vector4 off = new(MathF.Ceiling(maximum * 0.5F));
this.V0L = Vector4.Clamp(this.V0L + off, min, max);
this.V0R = Vector4.Clamp(this.V0R + off, min, max);
this.V1L = Vector4.Clamp(this.V1L + off, min, max);
this.V1R = Vector4.Clamp(this.V1R + off, min, max);
this.V2L = Vector4.Clamp(this.V2L + off, min, max);
this.V2R = Vector4.Clamp(this.V2R + off, min, max);
this.V3L = Vector4.Clamp(this.V3L + off, min, max);
this.V3R = Vector4.Clamp(this.V3R + off, min, max);
this.V4L = Vector4.Clamp(this.V4L + off, min, max);
this.V4R = Vector4.Clamp(this.V4R + off, min, max);
this.V5L = Vector4.Clamp(this.V5L + off, min, max);
this.V5R = Vector4.Clamp(this.V5R + off, min, max);
this.V6L = Vector4.Clamp(this.V6L + off, min, max);
this.V6R = Vector4.Clamp(this.V6R + off, min, max);
this.V7L = Vector4.Clamp(this.V7L + off, min, max);
this.V7R = Vector4.Clamp(this.V7R + off, min, max);
}
/// <summary>
/// Rounds all values in the block.
/// </summary>
public void RoundInPlace()
{
for (int i = 0; i < Size; i++)
{
this[i] = MathF.Round(this[i]);
Vector4 min = Vector4.Zero;
Vector4 max = new(maximum);
Vector4 off = new(MathF.Ceiling(maximum * 0.5F));
this.V0L = Vector4.Clamp(this.V0L + off, min, max);
this.V0R = Vector4.Clamp(this.V0R + off, min, max);
this.V1L = Vector4.Clamp(this.V1L + off, min, max);
this.V1R = Vector4.Clamp(this.V1R + off, min, max);
this.V2L = Vector4.Clamp(this.V2L + off, min, max);
this.V2R = Vector4.Clamp(this.V2R + off, min, max);
this.V3L = Vector4.Clamp(this.V3L + off, min, max);
this.V3R = Vector4.Clamp(this.V3R + off, min, max);
this.V4L = Vector4.Clamp(this.V4L + off, min, max);
this.V4R = Vector4.Clamp(this.V4R + off, min, max);
this.V5L = Vector4.Clamp(this.V5L + off, min, max);
this.V5R = Vector4.Clamp(this.V5R + off, min, max);
this.V6L = Vector4.Clamp(this.V6L + off, min, max);
this.V6R = Vector4.Clamp(this.V6R + off, min, max);
this.V7L = Vector4.Clamp(this.V7L + off, min, max);
this.V7R = Vector4.Clamp(this.V7R + off, min, max);
}
}
@ -533,7 +514,7 @@ internal partial struct Block8x8F : IEquatable<Block8x8F>
}
/// <inheritdoc />
public bool Equals(Block8x8F other)
public readonly bool Equals(Block8x8F other)
=> this.V0L == other.V0L
&& this.V0R == other.V0R
&& this.V1L == other.V1L

8
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DirectComponentProcessor.cs

@ -53,10 +53,10 @@ internal sealed class DirectComponentProcessor : ComponentProcessor
// Convert from spectral to color
FloatingPointDCT.TransformIDCT(ref workspaceBlock);
// To conform better to libjpeg we actually NEED TO loose precision here.
// This is because they store blocks as Int16 between all the operations.
// To be "more accurate", we need to emulate this by rounding!
workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue);
// Normalize into the component sample range without quantizing away
// fractional precision. The later color conversion / final pack stage
// performs the only rounding we actually need for output samples.
workspaceBlock.NormalizeColorsInPlace(maximumValue);
// Write to color buffer acording to sampling factors
int xColorBufferStart = xBlock * this.BlockAreaSize.Width;

339
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs

@ -66,32 +66,335 @@ internal sealed class DownScalingComponentProcessor2 : ComponentProcessor
[MethodImpl(InliningOptions.ShortMethod)]
public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
{
// TODO: Optimize: implement all cases with scale-specific, loopless code!
if (horizontalScale == 1 && verticalScale == 1)
{
CopyTo1x1Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 2 && verticalScale == 2)
{
CopyTo2x2Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 2 && verticalScale == 1)
{
CopyTo2x1Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 1 && verticalScale == 2)
{
CopyTo1x2Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 4 && verticalScale == 1)
{
CopyTo4x1Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 4 && verticalScale == 2)
{
CopyTo4x2Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 1 && verticalScale == 4)
{
CopyTo1x4Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 2 && verticalScale == 4)
{
CopyTo2x4Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 4 && verticalScale == 4)
{
CopyTo4x4Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
// The common 1x, 2x, and 4x integral scales are specialized above.
// Uncommon legal factor-3 scales use the generic fallback.
CopyArbitraryScale(ref block, ref destRef, (uint)destStrideWidth, (uint)horizontalScale, (uint)verticalScale);
}
/// <summary>
/// Copies a 4x4 reduced block directly into the destination buffer when no chroma expansion is needed.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo1x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
CopyRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 2u, 2u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 3u, 3u, areaStride);
}
/// <summary>
/// Copies a 4x4 reduced block into the destination buffer while doubling only the horizontal axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo2x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
WidenRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 2u, 2u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 3u, 3u, areaStride);
}
/// <summary>
/// Copies a 4x4 reduced block into the destination buffer while doubling only the vertical axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo1x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
CopyRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 2u, 4u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 2u, 5u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 3u, 6u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 3u, 7u, areaStride);
}
/// <summary>
/// Copies a 4x4 reduced block into the destination buffer while doubling both axes.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo2x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
WidenRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 2u, 4u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 2u, 5u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 3u, 6u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 3u, 7u, areaStride);
}
[MethodImpl(InliningOptions.ColdPath)]
static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale)
/// <summary>
/// Copies a 4x4 reduced block into the destination buffer while quadrupling only the horizontal axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo4x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 2u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 3u, areaStride);
}
/// <summary>
/// Copies a 4x4 reduced block into the destination buffer while quadrupling horizontally and doubling vertically.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo4x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 4u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 5u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 6u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 7u, areaStride);
}
/// <summary>
/// Copies a 4x4 reduced block into the destination buffer while quadrupling only the vertical axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo1x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
CopyRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 2u, 8u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 2u, 9u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 2u, 10u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 2u, 11u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 3u, 12u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 3u, 13u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 3u, 14u, areaStride);
CopyRow4(ref sourceBase, ref areaOrigin, 3u, 15u, areaStride);
}
/// <summary>
/// Copies a 4x4 reduced block into the destination buffer while doubling horizontally and quadrupling vertically.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo2x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
WidenRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 2u, 8u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 2u, 9u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 2u, 10u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 2u, 11u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 3u, 12u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 3u, 13u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 3u, 14u, areaStride);
WidenRow4(ref sourceBase, ref areaOrigin, 3u, 15u, areaStride);
}
/// <summary>
/// Copies a 4x4 reduced block into the destination buffer while quadrupling both axes.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo4x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 8u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 9u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 10u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 11u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 12u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 13u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 14u, areaStride);
ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 15u, areaStride);
}
/// <summary>
/// Copies one four-sample row from the reduced block to the destination row.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyRow4(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
{
ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
Unsafe.CopyBlock(
ref Unsafe.As<float, byte>(ref dest),
ref Unsafe.As<float, byte>(ref source),
4u * sizeof(float));
}
/// <summary>
/// Expands one four-sample row to eight samples by duplicating each source value horizontally.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void WidenRow4(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
{
ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
float value0 = source;
float value1 = Unsafe.Add(ref source, 1u);
float value2 = Unsafe.Add(ref source, 2u);
float value3 = Unsafe.Add(ref source, 3u);
dest = value0;
Unsafe.Add(ref dest, 1u) = value0;
Unsafe.Add(ref dest, 2u) = value1;
Unsafe.Add(ref dest, 3u) = value1;
Unsafe.Add(ref dest, 4u) = value2;
Unsafe.Add(ref dest, 5u) = value2;
Unsafe.Add(ref dest, 6u) = value3;
Unsafe.Add(ref dest, 7u) = value3;
}
/// <summary>
/// Expands one four-sample row to sixteen samples by duplicating each source value four times horizontally.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void ExpandRow4(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
{
ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
float value0 = source;
float value1 = Unsafe.Add(ref source, 1u);
float value2 = Unsafe.Add(ref source, 2u);
float value3 = Unsafe.Add(ref source, 3u);
dest = value0;
Unsafe.Add(ref dest, 1u) = value0;
Unsafe.Add(ref dest, 2u) = value0;
Unsafe.Add(ref dest, 3u) = value0;
Unsafe.Add(ref dest, 4u) = value1;
Unsafe.Add(ref dest, 5u) = value1;
Unsafe.Add(ref dest, 6u) = value1;
Unsafe.Add(ref dest, 7u) = value1;
Unsafe.Add(ref dest, 8u) = value2;
Unsafe.Add(ref dest, 9u) = value2;
Unsafe.Add(ref dest, 10u) = value2;
Unsafe.Add(ref dest, 11u) = value2;
Unsafe.Add(ref dest, 12u) = value3;
Unsafe.Add(ref dest, 13u) = value3;
Unsafe.Add(ref dest, 14u) = value3;
Unsafe.Add(ref dest, 15u) = value3;
}
/// <summary>
/// Replicates each reduced sample into an arbitrary integral expansion rectangle for uncommon subsampling ratios.
/// </summary>
[MethodImpl(InliningOptions.ColdPath)]
private static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale)
{
for (nuint y = 0u; y < 4u; y++)
{
for (nuint y = 0; y < 4; y++)
nuint yy = y * verticalScale;
nuint y8 = y * 8u;
for (nuint x = 0u; x < 4u; x++)
{
nuint yy = y * verticalScale;
nuint y8 = y * 8;
nuint xx = x * horizontalScale;
for (nuint x = 0; x < 4; x++)
{
nuint xx = x * horizontalScale;
float value = block[y8 + x];
float value = block[y8 + x];
for (nuint i = 0u; i < verticalScale; i++)
{
nuint baseIdx = ((yy + i) * areaStride) + xx;
for (nuint i = 0; i < verticalScale; i++)
for (nuint j = 0u; j < horizontalScale; j++)
{
nuint baseIdx = ((yy + i) * areaStride) + xx;
for (nuint j = 0; j < horizontalScale; j++)
{
// area[xx + j, yy + i] = value;
Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
}
// area[xx + j, yy + i] = value;
Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
}
}
}

281
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs

@ -66,32 +66,277 @@ internal sealed class DownScalingComponentProcessor4 : ComponentProcessor
[MethodImpl(InliningOptions.ShortMethod)]
public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
{
// TODO: Optimize: implement all cases with scale-specific, loopless code!
if (horizontalScale == 1 && verticalScale == 1)
{
CopyTo1x1Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 2 && verticalScale == 2)
{
CopyTo2x2Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 2 && verticalScale == 1)
{
CopyTo2x1Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 1 && verticalScale == 2)
{
CopyTo1x2Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 4 && verticalScale == 1)
{
CopyTo4x1Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 4 && verticalScale == 2)
{
CopyTo4x2Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 1 && verticalScale == 4)
{
CopyTo1x4Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 2 && verticalScale == 4)
{
CopyTo2x4Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 4 && verticalScale == 4)
{
CopyTo4x4Scale(ref block, ref destRef, (uint)destStrideWidth);
return;
}
// The common 1x, 2x, and 4x integral scales are specialized above.
// Uncommon legal factor-3 scales use the generic fallback.
CopyArbitraryScale(ref block, ref destRef, (uint)destStrideWidth, (uint)horizontalScale, (uint)verticalScale);
}
/// <summary>
/// Copies a 2x2 reduced block directly into the destination buffer when no chroma expansion is needed.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo1x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
CopyRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
}
/// <summary>
/// Copies a 2x2 reduced block into the destination buffer while doubling only the horizontal axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo2x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
WidenRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
}
/// <summary>
/// Copies a 2x2 reduced block into the destination buffer while doubling only the vertical axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo1x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
CopyRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
}
/// <summary>
/// Copies a 2x2 reduced block into the destination buffer while doubling both axes.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo2x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
WidenRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
}
[MethodImpl(InliningOptions.ColdPath)]
static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale)
/// <summary>
/// Copies a 2x2 reduced block into the destination buffer while quadrupling only the horizontal axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo4x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
}
/// <summary>
/// Copies a 2x2 reduced block into the destination buffer while quadrupling horizontally and doubling vertically.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo4x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
}
/// <summary>
/// Copies a 2x2 reduced block into the destination buffer while quadrupling only the vertical axis.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo1x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
CopyRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
CopyRow2(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
}
/// <summary>
/// Copies a 2x2 reduced block into the destination buffer while doubling horizontally and quadrupling vertically.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo2x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
WidenRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
WidenRow2(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
}
/// <summary>
/// Copies a 2x2 reduced block into the destination buffer while quadrupling both axes.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo4x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
{
ref float sourceBase = ref Unsafe.As<Block8x8F, float>(ref block);
ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
}
/// <summary>
/// Copies one two-sample row from the reduced block to the destination row.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyRow2(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
{
ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
Unsafe.CopyBlock(
ref Unsafe.As<float, byte>(ref dest),
ref Unsafe.As<float, byte>(ref source),
2u * sizeof(float));
}
/// <summary>
/// Expands one two-sample row to four samples by duplicating each source value horizontally.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void WidenRow2(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
{
ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
float value0 = source;
float value1 = Unsafe.Add(ref source, 1u);
dest = value0;
Unsafe.Add(ref dest, 1u) = value0;
Unsafe.Add(ref dest, 2u) = value1;
Unsafe.Add(ref dest, 3u) = value1;
}
/// <summary>
/// Expands one two-sample row to eight samples by duplicating each source value four times horizontally.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void ExpandRow2(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
{
ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
float value0 = source;
float value1 = Unsafe.Add(ref source, 1u);
dest = value0;
Unsafe.Add(ref dest, 1u) = value0;
Unsafe.Add(ref dest, 2u) = value0;
Unsafe.Add(ref dest, 3u) = value0;
Unsafe.Add(ref dest, 4u) = value1;
Unsafe.Add(ref dest, 5u) = value1;
Unsafe.Add(ref dest, 6u) = value1;
Unsafe.Add(ref dest, 7u) = value1;
}
/// <summary>
/// Replicates each reduced sample into an arbitrary integral expansion rectangle for uncommon subsampling ratios.
/// </summary>
[MethodImpl(InliningOptions.ColdPath)]
private static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale)
{
for (nuint y = 0u; y < 2u; y++)
{
for (nuint y = 0; y < 2; y++)
nuint yy = y * verticalScale;
nuint y8 = y * 8u;
for (nuint x = 0u; x < 2u; x++)
{
nuint yy = y * verticalScale;
nuint y8 = y * 8;
nuint xx = x * horizontalScale;
for (nuint x = 0; x < 2; x++)
{
nuint xx = x * horizontalScale;
float value = block[y8 + x];
float value = block[y8 + x];
for (nuint i = 0u; i < verticalScale; i++)
{
nuint baseIdx = ((yy + i) * areaStride) + xx;
for (nuint i = 0; i < verticalScale; i++)
for (nuint j = 0u; j < horizontalScale; j++)
{
nuint baseIdx = ((yy + i) * areaStride) + xx;
for (nuint j = 0; j < horizontalScale; j++)
{
// area[xx + j, yy + i] = value;
Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
}
// area[xx + j, yy + i] = value;
Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
}
}
}

164
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs

@ -63,16 +63,64 @@ internal sealed class DownScalingComponentProcessor8 : ComponentProcessor
return;
}
if (horizontalScale == 2 && verticalScale == 1)
{
CopyTo2x1Scale(value, ref destRef);
return;
}
if (horizontalScale == 1 && verticalScale == 2)
{
CopyTo1x2Scale(value, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 2 && verticalScale == 2)
{
destRef = value;
Unsafe.Add(ref destRef, 1) = value;
Unsafe.Add(ref destRef, 0 + (uint)destStrideWidth) = value;
Unsafe.Add(ref destRef, 1 + (uint)destStrideWidth) = value;
CopyTo2x2Scale(value, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 4 && verticalScale == 1)
{
CopyTo4x1Scale(value, ref destRef);
return;
}
if (horizontalScale == 4 && verticalScale == 2)
{
CopyTo4x2Scale(value, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 1 && verticalScale == 4)
{
CopyTo1x4Scale(value, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 2 && verticalScale == 4)
{
CopyTo2x4Scale(value, ref destRef, (uint)destStrideWidth);
return;
}
if (horizontalScale == 4 && verticalScale == 4)
{
CopyTo4x4Scale(value, ref destRef, (uint)destStrideWidth);
return;
}
// TODO: Optimize: implement all cases with scale-specific, loopless code!
// The common 1x, 2x, and 4x integral scales are specialized above.
// Uncommon legal factor-3 scales use the generic fallback.
CopyArbitraryScale(value, ref destRef, destStrideWidth, horizontalScale, verticalScale);
}
[MethodImpl(InliningOptions.ColdPath)]
private static float CopyArbitraryScale(float value, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
{
// The common 1x, 2x, and 4x integral scales are specialized above.
// Uncommon legal factor-3 scales use the generic fallback.
for (nuint y = 0; y < (uint)verticalScale; y++)
{
for (nuint x = 0; x < (uint)horizontalScale; x++)
@ -82,5 +130,111 @@ internal sealed class DownScalingComponentProcessor8 : ComponentProcessor
destRef = ref Unsafe.Add(ref destRef, (uint)destStrideWidth);
}
return destRef;
}
/// <summary>
/// Writes a single source value to two horizontally adjacent samples.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo2x1Scale(float value, ref float areaOrigin)
{
areaOrigin = value;
Unsafe.Add(ref areaOrigin, 1u) = value;
}
/// <summary>
/// Writes a single source value to two vertically adjacent samples.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo1x2Scale(float value, ref float areaOrigin, uint areaStride)
{
areaOrigin = value;
Unsafe.Add(ref areaOrigin, areaStride) = value;
}
/// <summary>
/// Writes a single source value to a 2x2 rectangle.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo2x2Scale(float value, ref float areaOrigin, uint areaStride)
{
areaOrigin = value;
Unsafe.Add(ref areaOrigin, 1u) = value;
Unsafe.Add(ref areaOrigin, areaStride) = value;
Unsafe.Add(ref areaOrigin, areaStride + 1u) = value;
}
/// <summary>
/// Writes a single source value to four horizontally adjacent samples.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo4x1Scale(float value, ref float areaOrigin)
{
areaOrigin = value;
Unsafe.Add(ref areaOrigin, 1u) = value;
Unsafe.Add(ref areaOrigin, 2u) = value;
Unsafe.Add(ref areaOrigin, 3u) = value;
}
/// <summary>
/// Writes a single source value to a 4x2 rectangle.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo4x2Scale(float value, ref float areaOrigin, uint areaStride)
{
CopyTo4x1Scale(value, ref areaOrigin);
ref float nextRow = ref Unsafe.Add(ref areaOrigin, areaStride);
CopyTo4x1Scale(value, ref nextRow);
}
/// <summary>
/// Writes a single source value to four vertically adjacent samples.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo1x4Scale(float value, ref float areaOrigin, uint areaStride)
{
areaOrigin = value;
Unsafe.Add(ref areaOrigin, areaStride) = value;
Unsafe.Add(ref areaOrigin, areaStride * 2u) = value;
Unsafe.Add(ref areaOrigin, areaStride * 3u) = value;
}
/// <summary>
/// Writes a single source value to a 2x4 rectangle.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo2x4Scale(float value, ref float areaOrigin, uint areaStride)
{
CopyTo2x1Scale(value, ref areaOrigin);
ref float row1 = ref Unsafe.Add(ref areaOrigin, areaStride);
CopyTo2x1Scale(value, ref row1);
ref float row2 = ref Unsafe.Add(ref areaOrigin, areaStride * 2u);
CopyTo2x1Scale(value, ref row2);
ref float row3 = ref Unsafe.Add(ref areaOrigin, areaStride * 3u);
CopyTo2x1Scale(value, ref row3);
}
/// <summary>
/// Writes a single source value to a 4x4 rectangle.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static void CopyTo4x4Scale(float value, ref float areaOrigin, uint areaStride)
{
CopyTo4x1Scale(value, ref areaOrigin);
ref float row1 = ref Unsafe.Add(ref areaOrigin, areaStride);
CopyTo4x1Scale(value, ref row1);
ref float row2 = ref Unsafe.Add(ref areaOrigin, areaStride * 2u);
CopyTo4x1Scale(value, ref row2);
ref float row3 = ref Unsafe.Add(ref areaOrigin, areaStride * 3u);
CopyTo4x1Scale(value, ref row3);
}
}

14
src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs

@ -136,10 +136,10 @@ internal static class ScaledFloatingPointDCT
(z4 * FP32_2_562915447);
// Save results to the top left 4x4 subregion
block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(((tmp10 + tmp2) * 0.5F) + normalizationValue, 0, maxValue));
block[(ctr * 8) + 3] = MathF.Round(Numerics.Clamp(((tmp10 - tmp2) * 0.5F) + normalizationValue, 0, maxValue));
block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(((tmp12 + tmp0) * 0.5F) + normalizationValue, 0, maxValue));
block[(ctr * 8) + 2] = MathF.Round(Numerics.Clamp(((tmp12 - tmp0) * 0.5F) + normalizationValue, 0, maxValue));
block[(ctr * 8) + 0] = Numerics.Clamp(((tmp10 + tmp2) * 0.5F) + normalizationValue, 0, maxValue);
block[(ctr * 8) + 3] = Numerics.Clamp(((tmp10 - tmp2) * 0.5F) + normalizationValue, 0, maxValue);
block[(ctr * 8) + 1] = Numerics.Clamp(((tmp12 + tmp0) * 0.5F) + normalizationValue, 0, maxValue);
block[(ctr * 8) + 2] = Numerics.Clamp(((tmp12 - tmp0) * 0.5F) + normalizationValue, 0, maxValue);
}
}
@ -199,8 +199,8 @@ internal static class ScaledFloatingPointDCT
(block[ctr + (8 * 1) + 2] * FP32_3_624509785);
// Save results to the top left 2x2 subregion
block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(((tmp10 + tmp0) * 0.25F) + normalizationValue, 0, maxValue));
block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(((tmp10 - tmp0) * 0.25F) + normalizationValue, 0, maxValue));
block[(ctr * 8) + 0] = Numerics.Clamp(((tmp10 + tmp0) * 0.25F) + normalizationValue, 0, maxValue);
block[(ctr * 8) + 1] = Numerics.Clamp(((tmp10 - tmp0) * 0.25F) + normalizationValue, 0, maxValue);
}
}
@ -213,6 +213,6 @@ internal static class ScaledFloatingPointDCT
/// <param name="normalizationValue">Output range normalization value, 1/2 of the <paramref name="maxValue"/>.</param>
/// <param name="maxValue">Maximum value of the output range.</param>
public static float TransformIDCT_1x1(float dc, float dequantizer, float normalizationValue, float maxValue)
=> MathF.Round(Numerics.Clamp((dc * dequantizer) + normalizationValue, 0, maxValue));
=> Numerics.Clamp((dc * dequantizer) + normalizationValue, 0, maxValue);
}
#pragma warning restore IDE0078

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

@ -181,54 +181,6 @@ public partial class Block8x8FTests : JpegFixture
}
}
[Theory]
[InlineData(1)]
[InlineData(2)]
public void NormalizeColorsAndRoundVector256(int seed)
{
if (this.SkipOnNonVector256Runner())
{
return;
}
Block8x8F source = CreateRandomFloatBlock(-200, 200, seed);
Block8x8F expected = source;
expected.NormalizeColorsInPlace(255);
expected.RoundInPlace();
Block8x8F actual = source;
actual.NormalizeColorsAndRoundInPlaceVector256(255);
this.Output.WriteLine(expected.ToString());
this.Output.WriteLine(actual.ToString());
this.CompareBlocks(expected, actual, 0);
}
[Theory]
[InlineData(1)]
[InlineData(2)]
public void NormalizeColorsAndRoundVector128(int seed)
{
if (this.SkipOnNonVector128Runner())
{
return;
}
Block8x8F source = CreateRandomFloatBlock(-200, 200, seed);
Block8x8F expected = source;
expected.NormalizeColorsInPlace(255);
expected.RoundInPlace();
Block8x8F actual = source;
actual.NormalizeColorsAndRoundInPlaceVector128(255);
this.Output.WriteLine(expected.ToString());
this.Output.WriteLine(actual.ToString());
this.CompareBlocks(expected, actual, 0);
}
[Theory]
[InlineData(1, 2)]
[InlineData(2, 1)]
@ -290,29 +242,6 @@ public partial class Block8x8FTests : JpegFixture
}
}
[Theory]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public void RoundInPlaceSlow(int seed)
{
Block8x8F s = CreateRandomFloatBlock(-500, 500, seed);
Block8x8F d = s;
d.RoundInPlace();
this.Output.WriteLine(s.ToString());
this.Output.WriteLine(d.ToString());
for (int i = 0; i < 64; i++)
{
float expected = (float)Math.Round(s[i]);
float actual = d[i];
Assert.Equal(expected, actual);
}
}
[Fact]
public void MultiplyInPlace_ByOtherBlock()
{

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

@ -193,7 +193,7 @@ public static class DCTTests
{
for (int x = 0; x < 4; x++)
{
AssertScaledElementEquality(expectedSpan.Slice((y * 16) + (x * 2)), actualSpan.Slice((y * 8) + x));
AssertScaledElementEquality(expectedSpan[((y * 16) + (x * 2))..], actualSpan[((y * 8) + x)..]);
}
}
@ -210,7 +210,7 @@ public static class DCTTests
}
}
average2x2 = MathF.Round(average2x2 / 4f);
average2x2 /= 4f;
Assert.Equal((int)average2x2, (int)actual[0]);
}
@ -254,7 +254,7 @@ public static class DCTTests
{
for (int x = 0; x < 2; x++)
{
AssertScaledElementEquality(expectedSpan.Slice((y * 32) + (x * 4)), actualSpan.Slice((y * 8) + x));
AssertScaledElementEquality(expectedSpan[((y * 32) + (x * 4))..], actualSpan[((y * 8) + x)..]);
}
}
@ -271,7 +271,7 @@ public static class DCTTests
}
}
average4x4 = MathF.Round(average4x4 / 16f);
average4x4 /= 16f;
Assert.Equal((int)average4x4, (int)actual[0]);
}
@ -311,7 +311,7 @@ public static class DCTTests
NormalizationValue,
MaxOutputValue);
float expected = MathF.Round(Numerics.Clamp(expectedDest[0] + NormalizationValue, 0, MaxOutputValue));
float expected = Numerics.Clamp(expectedDest[0] + NormalizationValue, 0, MaxOutputValue);
Assert.Equal((int)actual, (int)expected);
}

2
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs

@ -30,7 +30,7 @@ public partial class JpegDecoderTests
}
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr);
image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr, appendPixelTypeToFileName: false);
provider.Utility.TestName = DecodeBaselineJpegOutputName;
image.CompareToReferenceOutput(

2
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs

@ -21,7 +21,7 @@ public partial class JpegDecoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
image.DebugSave(provider);
image.DebugSave(provider, appendPixelTypeToFileName: false);
provider.Utility.TestName = DecodeProgressiveJpegOutputName;
image.CompareToReferenceOutput(

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

@ -49,7 +49,7 @@ public partial class JpegEncoderTests
public static readonly TheoryData<JpegColorType, int, float> CmykEncodingSetups = new()
{
{ JpegColorType.Cmyk, 100, 0.0159f / 100 },
{ JpegColorType.Cmyk, 100, 0.0164f / 100 },
{ JpegColorType.Cmyk, 80, 0.3922f / 100 },
{ JpegColorType.Cmyk, 40, 0.6488f / 100 },
};

14
tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

@ -707,17 +707,17 @@ public class TiffDecoderTests : TiffDecoderBaseTester
[WithFile(YCbCrJpegCompressed2, PixelTypes.Rgba32)]
[WithFile(RgbJpegCompressedNoJpegTable, PixelTypes.Rgba32)]
[WithFile(GrayscaleJpegCompressed, PixelTypes.Rgba32)]
[WithFile(Issues2123, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_JpegCompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider, useExactComparer: false);
[WithFile(Issues2123, PixelTypes.Rgba32, 0.110f)]
public void TiffDecoder_CanDecode_JpegCompressed<TPixel>(TestImageProvider<TPixel> provider, float tolerance = 0.001f)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider, useExactComparer: false, compareTolerance: tolerance);
[Theory]
[WithFile(RgbOldJpegCompressed, PixelTypes.Rgba32)]
[WithFile(RgbOldJpegCompressed2, PixelTypes.Rgba32)]
[WithFile(RgbOldJpegCompressed2, PixelTypes.Rgba32, 0.1003f)]
[WithFile(RgbOldJpegCompressed3, PixelTypes.Rgba32)]
[WithFile(RgbOldJpegCompressedGray, PixelTypes.Rgba32)]
[WithFile(YCbCrOldJpegCompressed, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_OldJpegCompressed<TPixel>(TestImageProvider<TPixel> provider)
public void TiffDecoder_CanDecode_OldJpegCompressed<TPixel>(TestImageProvider<TPixel> provider, float tolerance = 0.001f)
where TPixel : unmanaged, IPixel<TPixel>
{
DecoderOptions decoderOptions = new()
@ -728,7 +728,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
image.DebugSave(provider);
image.CompareToOriginal(
provider,
ImageComparer.Tolerant(0.001f),
ImageComparer.Tolerant(tolerance),
ReferenceDecoder,
decoderOptions);
}
@ -784,7 +784,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
// The image is handcrafted to simulate issue 2679. ImageMagick will throw an expection here and wont decode,
// so we compare to rererence output instead.
image.DebugSave(provider);
image.DebugSave(provider, appendPixelTypeToFileName: false);
image.CompareToReferenceOutput(
ImageComparer.Exact,
provider,

4
tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SeparateChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aada4a2ccf45de24f2a591a18d9bc0260ceb3829e104fee6982061013ed87282
size 14107709
oid sha256:4a4d2a3a1f320d3fcb121bd8edf716354b7e8ceb251a9eaa2087f443a3adbbc1
size 10896218

4
tests/Images/External/ReferenceOutput/HistogramEqualizationTests/AutoLevel_SynchronizedChannels_CompareToReferenceOutput_Rgba32_forest_bridge.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dca9b5b890d3a79b0002b7093d254d484ada4207e5010d1f0c6248d4dd6e22db
size 13909894
oid sha256:0c3f30908a803ac5956dbd5959b225bceab1fc673a1b89d55d1096ba024c7bcc
size 12519351

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Calliphora.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:362eddc5e06d672b4654bfe7a1ded995934a1c59719a3f909773b2e61931ffac
size 1332495
oid sha256:9a6f484f06c0b466d16591db78fec3cd2c3f6ebc7578896075cd4af95d8ceaab
size 1411818

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_Issue394-MultiHuffmanBaseline-Speakers.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b7a1e89adcd70f792d678786a177bac927a15d6065001cd76aab0bf3cc1b7b4c
size 1034853
oid sha256:88271ffea79e42914b8c37828da65afb691c7319b8e28fdd35942ecba4087baa
size 1060013

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_MultiScanBaselineCMYK.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d9dbb00a4be909d4b7fd605f83f786ff85545da54272a256c100afd80d687e8f
size 93253
oid sha256:3b160cc44490303c23c0abaa0dda827d68e11bfa67127e19da974073085abbe4
size 96042

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badeof.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:24f637742951c438da5acbae6a93545830ea4d0065031572a3bdd1c96be15cce
size 48990
oid sha256:7ce1a5d71cefd6527daecb8c1d9089f63189b833f1186971ddb89c6257d076ef
size 46745

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_badrst.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0501e53f97ddb4252e15073dc75f4129428befe5594a07297c21ae9f89f075b2
size 316278
oid sha256:522b0a2f9a97689217af461ab2a63a41553c0ae6ca28cde046b01019c87e2393
size 348228

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_cmyk.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d2253c83151238056caef2f3bca8800108a84360b2ff21979e4ff37fdddd2232
size 419852
oid sha256:5e81ac851f979c017082180a13ee4ddfe26c4151bfc082f7915252e57da44ec7
size 416967

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg420small.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2b5e1d91fb6dc1ddb696fbee63331ba9c6ef3548b619c005887e60c5b01f4981
size 27303
oid sha256:79291e1eddee66548156fc40bb5c8b1209c7b0048fb56f2bb7890c7d89ce655e
size 27264

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg422.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:733cc46271c4402974db2536a55e6ecae3110856df73031ca48dad03745d852d
size 35375
oid sha256:5196d5a20f639d833cf1ad979a290c144c97b41897fa5f0fa7454f5f4e8d3c11
size 28697

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_jpeg444.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6543f546fac9d05ebdac7a534b0cc422f31bfd81067212a19cb3a52ad24560a8
size 3978
oid sha256:9894983085f3d3d45b8c4ea9acc680932f236a4602230046386130aa2af916f1
size 4936

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:24f637742951c438da5acbae6a93545830ea4d0065031572a3bdd1c96be15cce
size 48990
oid sha256:7ce1a5d71cefd6527daecb8c1d9089f63189b833f1186971ddb89c6257d076ef
size 46745

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_testorig12.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ad57cf87eade9ee6663f575b358eafaa869a16b3b02d0fb00ca8c683422b85a0
size 47601
oid sha256:26ef4244aa761c7b6c51534e414f79438753b9dbd1fbf45d2f1e2fd2dd4ec4c1
size 46636

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck-subsample-1222.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a17478333c6ef0943aeb96faba1c0ea560995d0612564fe033b72b2949f4869f
size 66748
oid sha256:91a522b32485af3be304c2d98a60b9906112637a4627d69a072976474cef7485
size 66932

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeBaselineJpeg_ycck.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:63f7105e9e2d0794b3f7225af2187a5717c12b806b68b33b98e08e0a0b1ffa79
size 71051
oid sha256:f374012655af194ebece2e9329da416be830d1cadd24a2bd584480d45751ea78
size 58272

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_BadEofProgressive.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6e5d84fa88ac8b552c21c042f155801924240d764da365440d28eb8133950925
size 568177
oid sha256:36b557cd6d55389b8f02b811ecd2b14f1f1c440c7e8dd5a5a3c55cea57547203
size 564682

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_ExifUndefType.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fb636a9f304cdef713b823adc7424108b4d44fada294c48a366278c9b9e7b4b5
size 20163
oid sha256:ff9554b1f421ca04e0580fbb505895f002b80298feb128a15cdc045a1625a490
size 20111

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_fb.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2935d3dc66eec334eec65c1ed9fbb04046404a9c28bed413ac635878e63ea6cf
size 114688
oid sha256:c2eb2bd4db9b67466d96fe154cd771d6780a24a798a0c051f307425f1b0e3d15
size 115708

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/DecodeProgressiveJpeg_progress.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7b48730a7a149e13dd87a24e62c0704895b08a5ed1f8ea48a6a6a7248450750d
size 301416
oid sha256:c5c5b5711041f22e8ee1afb344137c00ee80ba51e777689513e8dfeea855befa
size 297941

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_CMYK_ICC_Jpeg_Rgba32_issue-129.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:215cba73dfb0e19f75f6dc0c3fefca474bd65f57684a207a11d896e1637bb643
size 1240827
oid sha256:9750b54f5220020e0c3f5ce22e4f108cb70290750c613c29beeb49473d5c80a6
size 1349665

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-AdobeRGB-yes.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c942b534baa51b8e46e88bd38d1ced319bccf1b55a5711ae5761697b7437fe4e
size 458321
oid sha256:dcc32a36dcdfe3b9cfae17a81d3eb180248db4507ae93fdd4bb92e1e5e29950d
size 466215

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-AppleRGB-yes.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:163711226bdfa2f102b314435baea9f69ad1be1b11ef5ad8348358cd09a029ae
size 482963
oid sha256:ae9a32eba21fa0bd2039803b5af8ffdba0409c104746455d5028741f214eb1a1
size 479922

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-ColorMatch-yes.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6f7981f40bab5bffff3e7c9ea1676d224c173fabbaa6e7a920d7a9dabd58655f
size 464917
oid sha256:e2bf66bb6d7eb3ff402b8564fb51bc951792ad47dc17fc9c7c886744638a59bb
size 469096

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-ProPhoto-yes.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:11b02b982c024e295915e88f56a428ad73068217a7ae625f705127ab8c35a4bf
size 477061
oid sha256:e9c015e599b1710fd50c4ad61104fb03f27f211c13686cb162baba492ec4a935
size 482675

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-WideRGB-yes.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a49967c20cccf824df4de3f105f5ddb44d7a602c072ce22caa38939f21f62505
size 469827
oid sha256:e25ba3afb3346633db52e91062c4266474bc9d9c0b3fcf686f1040b3a4d70d0d
size 476050

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Momiji-sRGB-yes.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4b33fc8fd03142aaaf8aabc39e084acd9e82e9222292e281953d92d65edcc1a7
size 436111
oid sha256:b80e88a7fdb27491687202c247eafbeb9d1c024affd5dc4633bcf15602fed990
size 464921

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Perceptual-cLUT-only.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fe06798b92c9b476c167407e752b4379d50f1b1ad6329eceb368c8c36097b401
size 95103
oid sha256:dbe96e449dfeadc71bbfb0b48d2f56e6357d5b64a225153eed60263ea3356e71
size 119987

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_Perceptual.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:21f8d54d4b789b783f3020402d4c1b91bb541de6565e2960976b569f60694631
size 99385
oid sha256:6e9e4da17b7270565fd592f4ff710ff317e1054794d966d33f11748defc7c35b
size 124944

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_RGB_ICC_Jpeg_Rgba32_sRGB_Gray.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:18ad361f79b4ab26d452d5cc7ada4c121dfbf45d20da7c23a58f71a9497d17a2
size 5341
oid sha256:0eaf217570e283b6fb9a7d52ba5087d760d9a98827e507cbae299915871e1c31
size 2595

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/Decode_YCCK_ICC_Jpeg_Rgba32_issue_2723.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cef85199e8560d6669766c094d078831024e44ac7fc537f8696f802c8e06138b
size 420141
oid sha256:de73fa7a2cdd79763e40c51381f9a0cf5c7d43db2dcd7a86dad0a5b68377665f
size 421249

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Bicubic_Calliphora_150_150.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1bb6ed717a2af582d60ccd6c1c9c1ac92df0f8662755530b7e9063724835b23b
size 27709
oid sha256:9be82bf220095b5650c7cd0a0eba9ee2b0fbe39881ff2bbde465ca9d2f94490f
size 27803

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Resize_Calliphora_150_150.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a4804948a2ba604e383dd2dcc4ca4cac91c75ac97a0ab10bd884478429fa50a5
size 28178
oid sha256:b7e4b589fdbad2688895317fc760a834624f76cc9a027a65ce3cd6b35563ec85
size 28514

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Combined_Resize_Calliphora_150_150.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a4804948a2ba604e383dd2dcc4ca4cac91c75ac97a0ab10bd884478429fa50a5
size 28178
oid sha256:b7e4b589fdbad2688895317fc760a834624f76cc9a027a65ce3cd6b35563ec85
size 28514

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_IDCT_Resize_Calliphora_150_150.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fc67170d70378ad8b8c0e1c1695b5c268341f0d26a6c788d1a8dffa8c90482a0
size 102165
oid sha256:d28335f6ad2421b6bc7e420c2e61ead73cd64f63ba7e1c9bd98ac1c140188ddb
size 104381

4
tests/Images/External/ReferenceOutput/JpegDecoderTests/JpegDecoder_Decode_Specialized_Scale_Resize_Calliphora_150_150.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ae7f6ebfd9f2ddd85611827fda13eaf316d36d5187900458568f80b929effb9b
size 28291
oid sha256:d0db0474f28d6302aae207b09f63e2de9e9ce6a9777a95dbac69ccb957d08daa
size 28381

4
tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_Cmyk_Rgba32_Cmyk-jpeg.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f68db78d765a7f36570cd7b57a1f06cfca24c3b4916d0692a4aa051209ec327
size 616
oid sha256:b1ff0ce13bf0521b8a6f0d77806473c837c45428391786f751186a53fd951c0c
size 394

4
tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_JpegCompressedWithIssue2679_Issue2679.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6cd36c7e07a08e22cceecd28a056c80e5553a8c092bfc091e902d13bd5c46f4d
size 120054
oid sha256:96729898fee87fc4305913c111a5841af205564d032b916aeb04425a20c6b22c
size 46540

4
tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_ICC_Rgba32_Issue2454_A.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c4f77673028643af0ac02a8f6a1e2db14052177e3401c369391a8ff7e943770c
size 7679254
oid sha256:13f51f69b061303d06b73fa7e626d7252a9fd14aa84c961ea894f6e7cd7218cc
size 7603818

4
tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_ICC_Rgba32_Issue2454_B.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e616895c21fd8b19a216e8a3ef4968bd413589b5875efdac29860f019a710527
size 7517284
oid sha256:b6a35853e44d06a6f74e3aaf558e6a4b9c3209c6c3fa2c8f3ee650bf049ac0d3
size 7624359

4
tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_Rgba32_Issue2454_A.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d7911e059049c427229136479740fd62e2e09907549ec3e1421a6a60da6167cc
size 7840892
oid sha256:9e91f6ae534b95a9e052158043e929e1da22e0851b4d6dfdb8b802605396758f
size 7888373

4
tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YccK_Rgba32_Issue2454_B.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:291f2033a7b4cfc10fb3301283c167b3fbc288bc173c95b21bc726bf076865af
size 7649213
oid sha256:81bb1f05a6f72aebee41941b2390ca0a35c4368758af2d27b3e81bbee8901c56
size 7956102

4
tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.25.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a68aa183691be3240e881057cb2f5785e50228c9c5dd98163ba766b5cafa8b55
size 88601
oid sha256:1f312445263030ea79e2d1a05c62d19ade816c9cda9f0d3494ab76fde1e03ace
size 92534

4
tests/Images/External/ReferenceOutput/Transforms/EntropyCropTest/EntropyCrop_MultiScanBaselineCMYK_0.75.png

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:53a7bc1aa7279cce0b73568b6ed9b141377f7654b73dcf926c43c22dd908039a
size 88502
oid sha256:672f167d514f01c625145701ea0d45ced503a6e84cae75fbdb67e6b0db179aee
size 92318

Loading…
Cancel
Save