Browse Source

Merge pull request #346 from SixLabors/antonfirsov/jpeg-optimization

AVX2 optimization for Jpeg YCbCr color conversion
af/merge-core
James Jackson-South 9 years ago
committed by GitHub
parent
commit
dd1b3ca058
  1. 5
      src/ImageSharp/Common/Tuples/Vector4Pair.cs
  2. 3
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  3. 5
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs
  4. 9
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
  5. 9
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
  6. 9
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
  7. 51
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
  8. 101
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
  9. 108
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  10. 7
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
  11. 70
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs
  12. 8
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
  13. 3
      src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs
  14. 2
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs
  15. 4
      tests/ImageSharp.Benchmarks/Config.cs
  16. 2
      tests/ImageSharp.Benchmarks/General/ArrayCopy.cs
  17. 2
      tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs
  18. 7
      tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpeg.cs
  19. 11
      tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpegMultiple.cs
  20. 4
      tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpeg.cs
  21. 5
      tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpegMultiple.cs
  22. 86
      tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs
  23. 32
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
  24. 12
      tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs

5
src/ImageSharp/Common/Tuples/Vector4Pair.cs

@ -71,5 +71,10 @@ namespace SixLabors.ImageSharp.Common.Tuples
v *= new Vector<float>(1 / 255f); v *= new Vector<float>(1 / 255f);
self = v; self = v;
} }
public override string ToString()
{
return $"{this.A}, {this.B}";
}
} }
} }

3
src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs

@ -1,3 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;

5
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs

@ -1,4 +1,7 @@
using System.Numerics; // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;

9
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromCmyk.cs → src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs

@ -1,11 +1,14 @@
using System; // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
internal class FromCmyk : JpegColorConverter internal class FromCmyk : ColorConverters.JpegColorConverter
{ {
public FromCmyk() public FromCmyk()
: base(JpegColorSpace.Cmyk) : base(JpegColorSpace.Cmyk)

9
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromGrayScale.cs → src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs

@ -1,11 +1,14 @@
using System; // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
internal class FromGrayScale : JpegColorConverter internal class FromGrayScale : ColorConverters.JpegColorConverter
{ {
public FromGrayScale() public FromGrayScale()
: base(JpegColorSpace.GrayScale) : base(JpegColorSpace.GrayScale)

9
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromRgb.cs → src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs

@ -1,11 +1,14 @@
using System; // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
internal class FromRgb : JpegColorConverter internal class FromRgb : ColorConverters.JpegColorConverter
{ {
public FromRgb() public FromRgb()
: base(JpegColorSpace.RGB) : base(JpegColorSpace.RGB)

51
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs

@ -0,0 +1,51 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal class FromYCbCrBasic : ColorConverters.JpegColorConverter
{
public FromYCbCrBasic()
: base(JpegColorSpace.YCbCr)
{
}
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result)
{
ConvertCore(values, result);
}
internal static void ConvertCore(ComponentValues values, Span<Vector4> result)
{
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0;
ReadOnlySpan<float> cbVals = values.Component1;
ReadOnlySpan<float> crVals = values.Component2;
var v = new Vector4(0, 0, 0, 1);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
for (int i = 0; i < result.Length; i++)
{
float y = yVals[i];
float cb = cbVals[i] - 128F;
float cr = crVals[i] - 128F;
v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero);
v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero);
v.Z = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero);
v *= scale;
result[i] = v;
}
}
}
}
}

101
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromYCbCr.cs → src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs

@ -1,53 +1,16 @@
using System; // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Common.Tuples; using SixLabors.ImageSharp.Common.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {
internal class FromYCbCrBasic : JpegColorConverter internal class FromYCbCrSimd : ColorConverters.JpegColorConverter
{
public FromYCbCrBasic()
: base(JpegColorSpace.YCbCr)
{
}
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result)
{
ConvertCore(values, result);
}
internal static void ConvertCore(ComponentValues values, Span<Vector4> result)
{
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0;
ReadOnlySpan<float> cbVals = values.Component1;
ReadOnlySpan<float> crVals = values.Component2;
var v = new Vector4(0, 0, 0, 1);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
for (int i = 0; i < result.Length; i++)
{
float y = yVals[i];
float cb = cbVals[i] - 128F;
float cr = crVals[i] - 128F;
v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero);
v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero);
v.Z = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero);
v *= scale;
result[i] = v;
}
}
}
internal class FromYCbCrSimd : JpegColorConverter
{ {
public FromYCbCrSimd() public FromYCbCrSimd()
: base(JpegColorSpace.YCbCr) : base(JpegColorSpace.YCbCr)
@ -147,58 +110,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
destination.Collect(ref r, ref g, ref b); destination.Collect(ref r, ref g, ref b);
} }
} }
private struct Vector4Octet
{
#pragma warning disable SA1132 // Do not combine fields
public Vector4 V0, V1, V2, V3, V4, V5, V6, V7;
/// <summary>
/// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order.
/// </summary>
public void Collect(ref Vector4Pair r, ref Vector4Pair g, ref Vector4Pair b)
{
this.V0.X = r.A.X;
this.V0.Y = g.A.X;
this.V0.Z = b.A.X;
this.V0.W = 1f;
this.V1.X = r.A.Y;
this.V1.Y = g.A.Y;
this.V1.Z = b.A.Y;
this.V1.W = 1f;
this.V2.X = r.A.Z;
this.V2.Y = g.A.Z;
this.V2.Z = b.A.Z;
this.V2.W = 1f;
this.V3.X = r.A.W;
this.V3.Y = g.A.W;
this.V3.Z = b.A.W;
this.V3.W = 1f;
this.V4.X = r.B.X;
this.V4.Y = g.B.X;
this.V4.Z = b.B.X;
this.V4.W = 1f;
this.V5.X = r.B.Y;
this.V5.Y = g.B.Y;
this.V5.Z = b.B.Y;
this.V5.W = 1f;
this.V6.X = r.B.Z;
this.V6.Y = g.B.Z;
this.V6.Z = b.B.Z;
this.V6.W = 1f;
this.V7.X = r.B.W;
this.V7.Y = g.B.W;
this.V7.Z = b.B.W;
this.V7.W = 1f;
}
}
} }
} }
} }

108
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs

@ -0,0 +1,108 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Common.Tuples;
// ReSharper disable ImpureMethodCallOnReadonlyValueField
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal class FromYCbCrSimdAvx2 : ColorConverters.JpegColorConverter
{
public FromYCbCrSimdAvx2()
: base(JpegColorSpace.YCbCr)
{
}
public static bool IsAvailable => Vector.IsHardwareAccelerated && SimdUtils.IsAvx2CompatibleArchitecture;
public override void ConvertToRGBA(ComponentValues values, Span<Vector4> result)
{
int remainder = result.Length % 8;
int simdCount = result.Length - remainder;
if (simdCount > 0)
{
ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount));
}
FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder));
}
/// <summary>
/// SIMD convert using buffers of sizes divisable by 8.
/// </summary>
internal static void ConvertCore(ComponentValues values, Span<Vector4> result)
{
// This implementation is actually AVX specific.
// An AVX register is capable of storing 8 float-s.
if (!IsAvailable)
{
throw new InvalidOperationException(
"JpegColorConverter.FromYCbCrSimd256 can be used only on architecture having 256 byte floating point SIMD registers!");
}
ref Vector<float> yBase =
ref Unsafe.As<float, Vector<float>>(ref values.Component0.DangerousGetPinnableReference());
ref Vector<float> cbBase =
ref Unsafe.As<float, Vector<float>>(ref values.Component1.DangerousGetPinnableReference());
ref Vector<float> crBase =
ref Unsafe.As<float, Vector<float>>(ref values.Component2.DangerousGetPinnableReference());
ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref result.DangerousGetPinnableReference());
var chromaOffset = new Vector<float>(-128f);
// Walking 8 elements at one step:
int n = result.Length / 8;
var rr = default(Vector4Pair);
var gg = default(Vector4Pair);
var bb = default(Vector4Pair);
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr);
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg);
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb);
var scale = new Vector<float>(1 / 255f);
for (int i = 0; i < n; i++)
{
// y = yVals[i];
// cb = cbVals[i] - 128F;
// cr = crVals[i] - 128F;
Vector<float> y = Unsafe.Add(ref yBase, i);
Vector<float> cb = Unsafe.Add(ref cbBase, i) + chromaOffset;
Vector<float> cr = Unsafe.Add(ref crBase, i) + chromaOffset;
// r = y + (1.402F * cr);
// g = y - (0.344136F * cb) - (0.714136F * cr);
// b = y + (1.772F * cb);
// Adding & multiplying 8 elements at one time:
Vector<float> r = y + (cr * new Vector<float>(1.402F));
Vector<float> g = y - (cb * new Vector<float>(0.344136F)) - (cr * new Vector<float>(0.714136F));
Vector<float> b = y + (cb * new Vector<float>(1.772F));
r = r.FastRound();
g = g.FastRound();
b = b.FastRound();
r *= scale;
g *= scale;
b *= scale;
rrRefAsVector = r;
ggRefAsVector = g;
bbRefAsVector = b;
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i);
destination.Collect(ref rr, ref gg, ref bb);
}
}
}
}
}

7
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromYccK.cs → src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs

@ -1,7 +1,10 @@
using System; // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics; using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{ {
internal abstract partial class JpegColorConverter internal abstract partial class JpegColorConverter
{ {

70
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.cs → src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs

@ -1,11 +1,14 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Common.Tuples;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{ {
/// <summary> /// <summary>
/// Encapsulates the conversion of Jpeg channels to RGBA values packed in <see cref="Vector4"/> buffer. /// Encapsulates the conversion of Jpeg channels to RGBA values packed in <see cref="Vector4"/> buffer.
@ -15,7 +18,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <summary> /// <summary>
/// The avalilable converters /// The avalilable converters
/// </summary> /// </summary>
private static readonly JpegColorConverter[] Converters = { new FromYCbCrSimd(), new FromYccK(), new FromCmyk(), new FromGrayScale(), new FromRgb() }; private static readonly JpegColorConverter[] Converters =
{
GetYCbCrConverter(), new FromYccK(), new FromCmyk(), new FromGrayScale(), new FromRgb()
};
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegColorConverter"/> class. /// Initializes a new instance of the <see cref="JpegColorConverter"/> class.
@ -51,6 +57,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <param name="result">The destination buffer of <see cref="Vector4"/> values</param> /// <param name="result">The destination buffer of <see cref="Vector4"/> values</param>
public abstract void ConvertToRGBA(ComponentValues values, Span<Vector4> result); public abstract void ConvertToRGBA(ComponentValues values, Span<Vector4> result);
/// <summary>
/// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture.
/// </summary>
private static JpegColorConverter GetYCbCrConverter() =>
FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd();
/// <summary> /// <summary>
/// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s. /// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s.
/// </summary> /// </summary>
@ -133,5 +145,57 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
return new ComponentValues(this.ComponentCount, c0, c1, c2, c3); return new ComponentValues(this.ComponentCount, c0, c1, c2, c3);
} }
} }
internal struct Vector4Octet
{
#pragma warning disable SA1132 // Do not combine fields
public Vector4 V0, V1, V2, V3, V4, V5, V6, V7;
/// <summary>
/// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order.
/// </summary>
public void Collect(ref Vector4Pair r, ref Vector4Pair g, ref Vector4Pair b)
{
this.V0.X = r.A.X;
this.V0.Y = g.A.X;
this.V0.Z = b.A.X;
this.V0.W = 1f;
this.V1.X = r.A.Y;
this.V1.Y = g.A.Y;
this.V1.Z = b.A.Y;
this.V1.W = 1f;
this.V2.X = r.A.Z;
this.V2.Y = g.A.Z;
this.V2.Z = b.A.Z;
this.V2.W = 1f;
this.V3.X = r.A.W;
this.V3.Y = g.A.W;
this.V3.Z = b.A.W;
this.V3.W = 1f;
this.V4.X = r.B.X;
this.V4.Y = g.B.X;
this.V4.Z = b.B.X;
this.V4.W = 1f;
this.V5.X = r.B.Y;
this.V5.Y = g.B.Y;
this.V5.Z = b.B.Y;
this.V5.W = 1f;
this.V6.X = r.B.Z;
this.V6.Y = g.B.Z;
this.V6.Z = b.B.Z;
this.V6.W = 1f;
this.V7.X = r.B.W;
this.V7.Y = g.B.W;
this.V7.Z = b.B.W;
this.V7.W = 1f;
}
}
} }
} }

8
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs

@ -37,9 +37,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
private readonly Buffer<Vector4> rgbaBuffer; private readonly Buffer<Vector4> rgbaBuffer;
/// <summary> /// <summary>
/// The <see cref="JpegColorConverter"/> corresponding to the current <see cref="JpegColorSpace"/> determined by <see cref="IRawJpegData.ColorSpace"/>. /// The <see cref="ColorConverters.JpegColorConverter"/> corresponding to the current <see cref="JpegColorSpace"/> determined by <see cref="IRawJpegData.ColorSpace"/>.
/// </summary> /// </summary>
private JpegColorConverter colorConverter; private ColorConverters.JpegColorConverter colorConverter;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class. /// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class.
@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(this, c)).ToArray(); this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(this, c)).ToArray();
this.rgbaBuffer = new Buffer<Vector4>(rawJpeg.ImageSizeInPixels.Width); this.rgbaBuffer = new Buffer<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace); this.colorConverter = ColorConverters.JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
} }
/// <summary> /// <summary>
@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{ {
int y = yy - this.PixelRowCounter; int y = yy - this.PixelRowCounter;
var values = new JpegColorConverter.ComponentValues(buffers, y); var values = new ColorConverters.JpegColorConverter.ComponentValues(buffers, y);
this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer); this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer);
Span<TPixel> destRow = destination.GetPixelRowSpan(yy); Span<TPixel> destRow = destination.GetPixelRowSpan(yy);

3
src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs

@ -1,3 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics; using System.Numerics;
using SixLabors.Primitives; using SixLabors.Primitives;

2
tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
[Config(typeof(Config.Short))] [Config(typeof(Config.ShortClr))]
public abstract class PackFromVector4<TPixel> public abstract class PackFromVector4<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {

4
tests/ImageSharp.Benchmarks/Config.cs

@ -17,9 +17,9 @@ namespace SixLabors.ImageSharp.Benchmarks
this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser()); this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser());
} }
public class Short : Config public class ShortClr : Config
{ {
public Short() public ShortClr()
{ {
this.Add( this.Add(
Job.Clr.WithLaunchCount(1) Job.Clr.WithLaunchCount(1)

2
tests/ImageSharp.Benchmarks/General/ArrayCopy.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
[Config(typeof(Config.Short))] [Config(typeof(Config.ShortClr))]
public class ArrayCopy public class ArrayCopy
{ {
[Params(10, 100, 1000, 10000)] [Params(10, 100, 1000, 10000)]

2
tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
[Config(typeof(Config.Short))] [Config(typeof(Config.ShortClr))]
public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded
{ {
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" };

7
tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs → tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpeg.cs

@ -3,23 +3,22 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace SixLabors.ImageSharp.Benchmarks.Image namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg
{ {
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Jobs;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort;
using SixLabors.ImageSharp.Tests; using SixLabors.ImageSharp.Tests;
using CoreImage = ImageSharp.Image; using CoreImage = SixLabors.ImageSharp.Image;
using CoreSize = SixLabors.Primitives.Size; using CoreSize = SixLabors.Primitives.Size;
[Config(typeof(Config.Short))] [Config(typeof(Config.ShortClr))]
public class DecodeJpeg : BenchmarkBase public class DecodeJpeg : BenchmarkBase
{ {
private byte[] jpegBytes; private byte[] jpegBytes;

11
tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs → tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpegMultiple.cs

@ -3,20 +3,15 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace SixLabors.ImageSharp.Benchmarks.Image namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using ImageSharp.Formats; using CoreImage = SixLabors.ImageSharp.Image;
using ImageSharp.Formats.Jpeg.GolangPort;
using ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Formats.Jpeg;
using CoreImage = ImageSharp.Image;
[Config(typeof(Config.Short))] [Config(typeof(Config.ShortClr))]
public class DecodeJpegMultiple : MultiImageBenchmarkBase public class DecodeJpegMultiple : MultiImageBenchmarkBase
{ {
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[]

4
tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs → tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpeg.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace SixLabors.ImageSharp.Benchmarks.Image namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg
{ {
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using CoreImage = ImageSharp.Image; using CoreImage = SixLabors.ImageSharp.Image;
public class EncodeJpeg : BenchmarkBase public class EncodeJpeg : BenchmarkBase
{ {

5
tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs → tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpegMultiple.cs

@ -3,17 +3,16 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace SixLabors.ImageSharp.Benchmarks.Image namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
[Config(typeof(Config.Short))] // It's long enough to iterate through multiple files [Config(typeof(Config.ShortClr))] // It's long enough to iterate through multiple files
public class EncodeJpegMultiple : MultiImageBenchmarkBase.WithImagesPreloaded public class EncodeJpegMultiple : MultiImageBenchmarkBase.WithImagesPreloaded
{ {
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" };

86
tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs

@ -0,0 +1,86 @@
namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg
{
using System;
using System.Numerics;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters;
using SixLabors.ImageSharp.Memory;
[Config(typeof(Config.ShortClr))]
public class YCbCrColorConversion
{
private Buffer2D<float>[] input;
private Vector4[] output;
public const int Count = 128;
[GlobalSetup]
public void Setup()
{
this.input = CreateRandomValues(3, Count);
this.output = new Vector4[Count];
}
[GlobalCleanup]
public void Cleanup()
{
foreach (Buffer2D<float> buffer in this.input)
{
buffer.Dispose();
}
}
[Benchmark(Baseline = true)]
public void Scalar()
{
var values = new JpegColorConverter.ComponentValues(this.input, 0);
JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output);
}
[Benchmark]
public void SimdVector4()
{
var values = new JpegColorConverter.ComponentValues(this.input, 0);
JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output);
}
[Benchmark]
public void SimdAvx2()
{
var values = new JpegColorConverter.ComponentValues(this.input, 0);
JpegColorConverter.FromYCbCrSimdAvx2.ConvertCore(values, this.output);
}
private static Buffer2D<float>[] CreateRandomValues(
int componentCount,
int inputBufferLength,
float minVal = 0f,
float maxVal = 255f)
{
var rnd = new Random(42);
Buffer2D<float>[] buffers = new Buffer2D<float>[componentCount];
for (int i = 0; i < componentCount; i++)
{
float[] values = new float[inputBufferLength];
for (int j = 0; j < inputBufferLength; j++)
{
values[j] = (float)rnd.NextDouble() * (maxVal - minVal) + minVal;
}
// no need to dispose when buffer is not array owner
buffers[i] = new Buffer2D<float>(values, values.Length, 1);
}
return buffers;
}
}
}

32
tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs

@ -12,6 +12,8 @@ using Xunit.Abstractions;
namespace SixLabors.ImageSharp.Tests.Formats.Jpg namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters;
public class JpegColorConverterTests public class JpegColorConverterTests
{ {
private const float Precision = 0.1f / 255; private const float Precision = 0.1f / 255;
@ -68,9 +70,37 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(CommonConversionData))] [MemberData(nameof(CommonConversionData))]
public void FromYCbCrSimd(int inputBufferLength, int resultBufferLength, int seed) public void FromYCbCrSimd(int inputBufferLength, int resultBufferLength, int seed)
{ {
ValidateConversion(new JpegColorConverter.FromYCbCrSimd(), 3, inputBufferLength, resultBufferLength, seed, ValidateYCbCr); ValidateConversion(
new JpegColorConverter.FromYCbCrSimd(),
3,
inputBufferLength,
resultBufferLength,
seed,
ValidateYCbCr);
} }
[Theory]
[MemberData(nameof(CommonConversionData))]
public void FromYCbCrSimdAvx2(int inputBufferLength, int resultBufferLength, int seed)
{
if (!SimdUtils.IsAvx2CompatibleArchitecture)
{
this.Output.WriteLine("No AVX2 present, skipping test!");
return;
}
//JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s);
ValidateConversion(
new JpegColorConverter.FromYCbCrSimdAvx2(),
3,
inputBufferLength,
resultBufferLength,
seed,
ValidateYCbCr);
}
[Theory] [Theory]
[MemberData(nameof(CommonConversionData))] [MemberData(nameof(CommonConversionData))]
public void ConvertFromYCbCr_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed) public void ConvertFromYCbCr_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed)

12
tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs

@ -50,6 +50,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
private void DecodeJpegBenchmarkImpl(string fileName, IImageDecoder decoder) private void DecodeJpegBenchmarkImpl(string fileName, IImageDecoder decoder)
{ {
// do not run this on CI even by accident
if (TestEnvironment.RunsOnCI)
{
return;
}
const int ExecutionCount = 30; const int ExecutionCount = 30;
if (!Vector.IsHardwareAccelerated) if (!Vector.IsHardwareAccelerated)
@ -78,6 +84,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// [InlineData(30, 100, JpegSubsample.Ratio444)] // [InlineData(30, 100, JpegSubsample.Ratio444)]
public void EncodeJpeg(int executionCount, int quality, JpegSubsample subsample) public void EncodeJpeg(int executionCount, int quality, JpegSubsample subsample)
{ {
// do not run this on CI even by accident
if (TestEnvironment.RunsOnCI)
{
return;
}
string[] testFiles = TestImages.Bmp.All string[] testFiles = TestImages.Bmp.All
.Concat(new[] { TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk }) .Concat(new[] { TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk })
.ToArray(); .ToArray();

Loading…
Cancel
Save