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);
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.Diagnostics;
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 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;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal class FromCmyk : JpegColorConverter
internal class FromCmyk : ColorConverters.JpegColorConverter
{
public FromCmyk()
: 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;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal class FromGrayScale : JpegColorConverter
internal class FromGrayScale : ColorConverters.JpegColorConverter
{
public FromGrayScale()
: 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;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{
internal abstract partial class JpegColorConverter
{
internal class FromRgb : JpegColorConverter
internal class FromRgb : ColorConverters.JpegColorConverter
{
public FromRgb()
: 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.Runtime.CompilerServices;
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 class FromYCbCrBasic : 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
internal class FromYCbCrSimd : ColorConverters.JpegColorConverter
{
public FromYCbCrSimd()
: base(JpegColorSpace.YCbCr)
@ -147,58 +110,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
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;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{
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.Collections.Generic;
using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Common.Tuples;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
{
/// <summary>
/// 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>
/// The avalilable converters
/// </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>
/// 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>
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>
/// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s.
/// </summary>
@ -133,5 +145,57 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
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;
/// <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>
private JpegColorConverter colorConverter;
private ColorConverters.JpegColorConverter colorConverter;
/// <summary>
/// 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.rgbaBuffer = new Buffer<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
this.colorConverter = ColorConverters.JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
}
/// <summary>
@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{
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);
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 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.PixelFormats;
[Config(typeof(Config.Short))]
[Config(typeof(Config.ShortClr))]
public abstract class PackFromVector4<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());
}
public class Short : Config
public class ShortClr : Config
{
public Short()
public ShortClr()
{
this.Add(
Job.Clr.WithLaunchCount(1)

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

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General
using BenchmarkDotNet.Attributes;
[Config(typeof(Config.Short))]
[Config(typeof(Config.ShortClr))]
public class ArrayCopy
{
[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.Bmp;
[Config(typeof(Config.Short))]
[Config(typeof(Config.ShortClr))]
public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded
{
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.
// </copyright>
namespace SixLabors.ImageSharp.Benchmarks.Image
namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg
{
using System.Drawing;
using System.IO;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Jobs;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort;
using SixLabors.ImageSharp.Tests;
using CoreImage = ImageSharp.Image;
using CoreImage = SixLabors.ImageSharp.Image;
using CoreSize = SixLabors.Primitives.Size;
[Config(typeof(Config.Short))]
[Config(typeof(Config.ShortClr))]
public class DecodeJpeg : BenchmarkBase
{
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.
// </copyright>
namespace SixLabors.ImageSharp.Benchmarks.Image
namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg
{
using System.Collections.Generic;
using System.IO;
using BenchmarkDotNet.Attributes;
using ImageSharp.Formats;
using ImageSharp.Formats.Jpeg.GolangPort;
using ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Formats.Jpeg;
using CoreImage = ImageSharp.Image;
using CoreImage = SixLabors.ImageSharp.Image;
[Config(typeof(Config.Short))]
[Config(typeof(Config.ShortClr))]
public class DecodeJpegMultiple : MultiImageBenchmarkBase
{
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.
// </copyright>
namespace SixLabors.ImageSharp.Benchmarks.Image
namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg
{
using System.Drawing;
using System.Drawing.Imaging;
@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image
using BenchmarkDotNet.Attributes;
using CoreImage = ImageSharp.Image;
using CoreImage = SixLabors.ImageSharp.Image;
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.
// </copyright>
namespace SixLabors.ImageSharp.Benchmarks.Image
namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg
{
using System.Collections.Generic;
using System.Drawing.Imaging;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats;
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
{
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
{
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters;
public class JpegColorConverterTests
{
private const float Precision = 0.1f / 255;
@ -68,9 +70,37 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[MemberData(nameof(CommonConversionData))]
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]
[MemberData(nameof(CommonConversionData))]
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)
{
// do not run this on CI even by accident
if (TestEnvironment.RunsOnCI)
{
return;
}
const int ExecutionCount = 30;
if (!Vector.IsHardwareAccelerated)
@ -78,6 +84,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// [InlineData(30, 100, JpegSubsample.Ratio444)]
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
.Concat(new[] { TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk })
.ToArray();

Loading…
Cancel
Save