mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
153 changed files with 21091 additions and 933 deletions
@ -0,0 +1,110 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Processing.Processors.Dithering; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A generic palette quantizer.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
|
public class PaletteQuantizer<TPixel> : IQuantizer |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="palette">The color palette to use.</param>
|
||||
|
public PaletteQuantizer(TPixel[] palette) |
||||
|
: this(palette, true) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="palette">The color palette to use.</param>
|
||||
|
/// <param name="dither">Whether to apply dithering to the output image</param>
|
||||
|
public PaletteQuantizer(TPixel[] palette, bool dither) |
||||
|
: this(palette, GetDiffuser(dither)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="palette">The color palette to use.</param>
|
||||
|
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
|
||||
|
public PaletteQuantizer(TPixel[] palette, IErrorDiffuser diffuser) |
||||
|
{ |
||||
|
Guard.MustBeBetweenOrEqualTo(palette.Length, QuantizerConstants.MinColors, QuantizerConstants.MaxColors, nameof(palette)); |
||||
|
this.Palette = palette; |
||||
|
this.Diffuser = diffuser; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public IErrorDiffuser Diffuser { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the palette.
|
||||
|
/// </summary>
|
||||
|
public TPixel[] Palette { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates the generic frame quantizer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
|
||||
|
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/>.</returns>
|
||||
|
public IFrameQuantizer<TPixel> CreateFrameQuantizer(Configuration configuration) |
||||
|
=> ((IQuantizer)this).CreateFrameQuantizer<TPixel>(configuration); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates the generic frame quantizer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
|
||||
|
/// <param name="maxColors">The maximum number of colors to hold in the color palette.</param>
|
||||
|
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/>.</returns>
|
||||
|
public IFrameQuantizer<TPixel> CreateFrameQuantizer(Configuration configuration, int maxColors) |
||||
|
=> ((IQuantizer)this).CreateFrameQuantizer<TPixel>(configuration, maxColors); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
IFrameQuantizer<TPixel1> IQuantizer.CreateFrameQuantizer<TPixel1>(Configuration configuration) |
||||
|
{ |
||||
|
if (!typeof(TPixel).Equals(typeof(TPixel1))) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Generic method type must be the same as class type."); |
||||
|
} |
||||
|
|
||||
|
TPixel[] paletteRef = this.Palette; |
||||
|
return new PaletteFrameQuantizer<TPixel1>(this, Unsafe.As<TPixel[], TPixel1[]>(ref paletteRef)); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
IFrameQuantizer<TPixel1> IQuantizer.CreateFrameQuantizer<TPixel1>(Configuration configuration, int maxColors) |
||||
|
{ |
||||
|
if (!typeof(TPixel).Equals(typeof(TPixel1))) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Generic method type must be the same as class type."); |
||||
|
} |
||||
|
|
||||
|
TPixel[] paletteRef = this.Palette; |
||||
|
TPixel1[] castPalette = Unsafe.As<TPixel[], TPixel1[]>(ref paletteRef); |
||||
|
|
||||
|
maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); |
||||
|
int max = Math.Min(maxColors, castPalette.Length); |
||||
|
|
||||
|
if (max != castPalette.Length) |
||||
|
{ |
||||
|
return new PaletteFrameQuantizer<TPixel1>(this, castPalette.AsSpan(0, max).ToArray()); |
||||
|
} |
||||
|
|
||||
|
return new PaletteFrameQuantizer<TPixel1>(this, castPalette); |
||||
|
} |
||||
|
|
||||
|
private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Contains color quantization specific constants.
|
||||
|
/// </summary>
|
||||
|
internal static class QuantizerConstants |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The minimum number of colors to use when quantizing an image.
|
||||
|
/// </summary>
|
||||
|
public const int MinColors = 1; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The maximum number of colors to use when quantizing an image.
|
||||
|
/// </summary>
|
||||
|
public const int MaxColors = 256; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Processing.Processors.Dithering; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A palette quantizer consisting of web safe colors as defined in the CSS Color Module Level 4.
|
||||
|
/// </summary>
|
||||
|
public class WebSafePaletteQuantizer : PaletteQuantizer |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
|
||||
|
/// </summary>
|
||||
|
public WebSafePaletteQuantizer() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="dither">Whether to apply dithering to the output image</param>
|
||||
|
public WebSafePaletteQuantizer(bool dither) |
||||
|
: base(dither) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
|
||||
|
public WebSafePaletteQuantizer(IErrorDiffuser diffuser) |
||||
|
: base(diffuser) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration) |
||||
|
=> this.CreateFrameQuantizer<TPixel>(configuration, NamedColors<TPixel>.WebSafePalette.Length); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors) |
||||
|
=> this.CreateFrameQuantizer(configuration, NamedColors<TPixel>.WebSafePalette, maxColors); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Processing.Processors.Dithering; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A palette quantizer consisting of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
|
||||
|
/// The hex codes were collected and defined by Nicholas Rougeux <see href="https://www.c82.net/werner"/>
|
||||
|
/// </summary>
|
||||
|
public class WernerPaletteQuantizer : PaletteQuantizer |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
|
||||
|
/// </summary>
|
||||
|
public WernerPaletteQuantizer() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="dither">Whether to apply dithering to the output image</param>
|
||||
|
public WernerPaletteQuantizer(bool dither) |
||||
|
: base(dither) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
|
||||
|
public WernerPaletteQuantizer(IErrorDiffuser diffuser) |
||||
|
: base(diffuser) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration) |
||||
|
=> this.CreateFrameQuantizer<TPixel>(configuration, NamedColors<TPixel>.WernerPalette.Length); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors) |
||||
|
=> this.CreateFrameQuantizer(configuration, NamedColors<TPixel>.WernerPalette, maxColors); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,133 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations |
||||
|
{ |
||||
|
public class Block8x8F_CopyTo1x1 |
||||
|
{ |
||||
|
private Block8x8F block; |
||||
|
|
||||
|
private Buffer2D<float> buffer; |
||||
|
|
||||
|
private BufferArea<float> destArea; |
||||
|
|
||||
|
[GlobalSetup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
if (!SimdUtils.IsAvx2CompatibleArchitecture) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Benchmark Block8x8F_CopyTo1x1 is invalid on platforms without AVX2 support."); |
||||
|
} |
||||
|
|
||||
|
this.buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(1000, 500); |
||||
|
this.destArea = this.buffer.GetArea(200, 100, 64, 64); |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void Original() |
||||
|
{ |
||||
|
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this.block); |
||||
|
ref byte destBase = ref Unsafe.As<float, byte>(ref this.destArea.GetReferenceToOrigin()); |
||||
|
int destStride = this.destArea.Stride * sizeof(float); |
||||
|
|
||||
|
CopyRowImpl(ref selfBase, ref destBase, destStride, 0); |
||||
|
CopyRowImpl(ref selfBase, ref destBase, destStride, 1); |
||||
|
CopyRowImpl(ref selfBase, ref destBase, destStride, 2); |
||||
|
CopyRowImpl(ref selfBase, ref destBase, destStride, 3); |
||||
|
CopyRowImpl(ref selfBase, ref destBase, destStride, 4); |
||||
|
CopyRowImpl(ref selfBase, ref destBase, destStride, 5); |
||||
|
CopyRowImpl(ref selfBase, ref destBase, destStride, 6); |
||||
|
CopyRowImpl(ref selfBase, ref destBase, destStride, 7); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) |
||||
|
{ |
||||
|
ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); |
||||
|
ref byte d = ref Unsafe.Add(ref destBase, row * destStride); |
||||
|
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void UseVector8() |
||||
|
{ |
||||
|
ref Block8x8F s = ref this.block; |
||||
|
ref float origin = ref this.destArea.GetReferenceToOrigin(); |
||||
|
int stride = this.destArea.Stride; |
||||
|
|
||||
|
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin); |
||||
|
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride)); |
||||
|
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2)); |
||||
|
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3)); |
||||
|
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4)); |
||||
|
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5)); |
||||
|
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6)); |
||||
|
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7)); |
||||
|
|
||||
|
Vector<float> row0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L); |
||||
|
Vector<float> row1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L); |
||||
|
Vector<float> row2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L); |
||||
|
Vector<float> row3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L); |
||||
|
Vector<float> row4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L); |
||||
|
Vector<float> row5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L); |
||||
|
Vector<float> row6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L); |
||||
|
Vector<float> row7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L); |
||||
|
|
||||
|
d0 = row0; |
||||
|
d1 = row1; |
||||
|
d2 = row2; |
||||
|
d3 = row3; |
||||
|
d4 = row4; |
||||
|
d5 = row5; |
||||
|
d6 = row6; |
||||
|
d7 = row7; |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void UseVector8_V2() |
||||
|
{ |
||||
|
ref Block8x8F s = ref this.block; |
||||
|
ref float origin = ref this.destArea.GetReferenceToOrigin(); |
||||
|
int stride = this.destArea.Stride; |
||||
|
|
||||
|
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin); |
||||
|
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride)); |
||||
|
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2)); |
||||
|
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3)); |
||||
|
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4)); |
||||
|
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5)); |
||||
|
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6)); |
||||
|
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7)); |
||||
|
|
||||
|
d0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L); |
||||
|
d1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L); |
||||
|
d2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L); |
||||
|
d3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L); |
||||
|
d4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L); |
||||
|
d5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L); |
||||
|
d6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L); |
||||
|
d7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L); |
||||
|
} |
||||
|
|
||||
|
// RESULTS:
|
||||
|
//
|
||||
|
// Method | Mean | Error | StdDev | Scaled |
|
||||
|
// -------------- |---------:|----------:|----------:|-------:|
|
||||
|
// Original | 22.53 ns | 0.1660 ns | 0.1553 ns | 1.00 |
|
||||
|
// UseVector8 | 21.59 ns | 0.3079 ns | 0.2571 ns | 0.96 |
|
||||
|
// UseVector8_V2 | 22.57 ns | 0.1699 ns | 0.1506 ns | 1.00 |
|
||||
|
//
|
||||
|
// Conclusion:
|
||||
|
// Doesn't worth to bother with this
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,404 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations |
||||
|
{ |
||||
|
public class Block8x8F_CopyTo2x2 |
||||
|
{ |
||||
|
private Block8x8F block; |
||||
|
|
||||
|
private Buffer2D<float> buffer; |
||||
|
|
||||
|
private BufferArea<float> destArea; |
||||
|
|
||||
|
[GlobalSetup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(1000, 500); |
||||
|
this.destArea = this.buffer.GetArea(200, 100, 128, 128); |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void Original() |
||||
|
{ |
||||
|
ref float destBase = ref this.destArea.GetReferenceToOrigin(); |
||||
|
int destStride = this.destArea.Stride; |
||||
|
|
||||
|
ref Block8x8F src = ref this.block; |
||||
|
|
||||
|
WidenCopyImpl2x2(ref src, ref destBase, 0, destStride); |
||||
|
WidenCopyImpl2x2(ref src, ref destBase, 1, destStride); |
||||
|
WidenCopyImpl2x2(ref src, ref destBase, 2, destStride); |
||||
|
WidenCopyImpl2x2(ref src, ref destBase, 3, destStride); |
||||
|
WidenCopyImpl2x2(ref src, ref destBase, 4, destStride); |
||||
|
WidenCopyImpl2x2(ref src, ref destBase, 5, destStride); |
||||
|
WidenCopyImpl2x2(ref src, ref destBase, 6, destStride); |
||||
|
WidenCopyImpl2x2(ref src, ref destBase, 7, destStride); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static void WidenCopyImpl2x2(ref Block8x8F src, ref float destBase, int row, int destStride) |
||||
|
{ |
||||
|
ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); |
||||
|
ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); |
||||
|
ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); |
||||
|
|
||||
|
Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; |
||||
|
Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; |
||||
|
Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y; |
||||
|
Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y; |
||||
|
Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z; |
||||
|
Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z; |
||||
|
Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W; |
||||
|
Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W; |
||||
|
|
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; |
||||
|
|
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; |
||||
|
|
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W; |
||||
|
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W; |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void Original_V2() |
||||
|
{ |
||||
|
ref float destBase = ref this.destArea.GetReferenceToOrigin(); |
||||
|
int destStride = this.destArea.Stride; |
||||
|
|
||||
|
ref Block8x8F src = ref this.block; |
||||
|
|
||||
|
WidenCopyImpl2x2_V2(ref src, ref destBase, 0, destStride); |
||||
|
WidenCopyImpl2x2_V2(ref src, ref destBase, 1, destStride); |
||||
|
WidenCopyImpl2x2_V2(ref src, ref destBase, 2, destStride); |
||||
|
WidenCopyImpl2x2_V2(ref src, ref destBase, 3, destStride); |
||||
|
WidenCopyImpl2x2_V2(ref src, ref destBase, 4, destStride); |
||||
|
WidenCopyImpl2x2_V2(ref src, ref destBase, 5, destStride); |
||||
|
WidenCopyImpl2x2_V2(ref src, ref destBase, 6, destStride); |
||||
|
WidenCopyImpl2x2_V2(ref src, ref destBase, 7, destStride); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static void WidenCopyImpl2x2_V2(ref Block8x8F src, ref float destBase, int row, int destStride) |
||||
|
{ |
||||
|
ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); |
||||
|
ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); |
||||
|
ref float dest0 = ref Unsafe.Add(ref destBase, row * 2 * destStride); |
||||
|
|
||||
|
Unsafe.Add(ref dest0, 0) = selfLeft.X; |
||||
|
Unsafe.Add(ref dest0, 1) = selfLeft.X; |
||||
|
Unsafe.Add(ref dest0, 2) = selfLeft.Y; |
||||
|
Unsafe.Add(ref dest0, 3) = selfLeft.Y; |
||||
|
Unsafe.Add(ref dest0, 4) = selfLeft.Z; |
||||
|
Unsafe.Add(ref dest0, 5) = selfLeft.Z; |
||||
|
Unsafe.Add(ref dest0, 6) = selfLeft.W; |
||||
|
Unsafe.Add(ref dest0, 7) = selfLeft.W; |
||||
|
|
||||
|
ref float dest1 = ref Unsafe.Add(ref dest0, 8); |
||||
|
|
||||
|
Unsafe.Add(ref dest1, 0) = selfRight.X; |
||||
|
Unsafe.Add(ref dest1, 1) = selfRight.X; |
||||
|
Unsafe.Add(ref dest1, 2) = selfRight.Y; |
||||
|
Unsafe.Add(ref dest1, 3) = selfRight.Y; |
||||
|
Unsafe.Add(ref dest1, 4) = selfRight.Z; |
||||
|
Unsafe.Add(ref dest1, 5) = selfRight.Z; |
||||
|
Unsafe.Add(ref dest1, 6) = selfRight.W; |
||||
|
Unsafe.Add(ref dest1, 7) = selfRight.W; |
||||
|
|
||||
|
ref float dest2 = ref Unsafe.Add(ref dest0, destStride); |
||||
|
|
||||
|
Unsafe.Add(ref dest2, 0) = selfLeft.X; |
||||
|
Unsafe.Add(ref dest2, 1) = selfLeft.X; |
||||
|
Unsafe.Add(ref dest2, 2) = selfLeft.Y; |
||||
|
Unsafe.Add(ref dest2, 3) = selfLeft.Y; |
||||
|
Unsafe.Add(ref dest2, 4) = selfLeft.Z; |
||||
|
Unsafe.Add(ref dest2, 5) = selfLeft.Z; |
||||
|
Unsafe.Add(ref dest2, 6) = selfLeft.W; |
||||
|
Unsafe.Add(ref dest2, 7) = selfLeft.W; |
||||
|
|
||||
|
ref float dest3 = ref Unsafe.Add(ref dest2, 8); |
||||
|
|
||||
|
Unsafe.Add(ref dest3, 0) = selfRight.X; |
||||
|
Unsafe.Add(ref dest3, 1) = selfRight.X; |
||||
|
Unsafe.Add(ref dest3, 2) = selfRight.Y; |
||||
|
Unsafe.Add(ref dest3, 3) = selfRight.Y; |
||||
|
Unsafe.Add(ref dest3, 4) = selfRight.Z; |
||||
|
Unsafe.Add(ref dest3, 5) = selfRight.Z; |
||||
|
Unsafe.Add(ref dest3, 6) = selfRight.W; |
||||
|
Unsafe.Add(ref dest3, 7) = selfRight.W; |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void UseVector2() |
||||
|
{ |
||||
|
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin()); |
||||
|
int destStride = this.destArea.Stride / 2; |
||||
|
|
||||
|
ref Block8x8F src = ref this.block; |
||||
|
|
||||
|
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 0, destStride); |
||||
|
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 1, destStride); |
||||
|
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 2, destStride); |
||||
|
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 3, destStride); |
||||
|
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 4, destStride); |
||||
|
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 5, destStride); |
||||
|
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 6, destStride); |
||||
|
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 7, destStride); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static void WidenCopyImpl2x2_Vector2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) |
||||
|
{ |
||||
|
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); |
||||
|
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); |
||||
|
|
||||
|
ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); |
||||
|
ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); |
||||
|
ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); |
||||
|
ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); |
||||
|
|
||||
|
var xLeft = new Vector2(sLeft.X); |
||||
|
var yLeft = new Vector2(sLeft.Y); |
||||
|
var zLeft = new Vector2(sLeft.Z); |
||||
|
var wLeft = new Vector2(sLeft.W); |
||||
|
|
||||
|
var xRight = new Vector2(sRight.X); |
||||
|
var yRight = new Vector2(sRight.Y); |
||||
|
var zRight = new Vector2(sRight.Z); |
||||
|
var wRight = new Vector2(sRight.W); |
||||
|
|
||||
|
dTopLeft = xLeft; |
||||
|
Unsafe.Add(ref dTopLeft, 1) = yLeft; |
||||
|
Unsafe.Add(ref dTopLeft, 2) = zLeft; |
||||
|
Unsafe.Add(ref dTopLeft, 3) = wLeft; |
||||
|
|
||||
|
dTopRight = xRight; |
||||
|
Unsafe.Add(ref dTopRight, 1) = yRight; |
||||
|
Unsafe.Add(ref dTopRight, 2) = zRight; |
||||
|
Unsafe.Add(ref dTopRight, 3) = wRight; |
||||
|
|
||||
|
dBottomLeft = xLeft; |
||||
|
Unsafe.Add(ref dBottomLeft, 1) = yLeft; |
||||
|
Unsafe.Add(ref dBottomLeft, 2) = zLeft; |
||||
|
Unsafe.Add(ref dBottomLeft, 3) = wLeft; |
||||
|
|
||||
|
dBottomRight = xRight; |
||||
|
Unsafe.Add(ref dBottomRight, 1) = yRight; |
||||
|
Unsafe.Add(ref dBottomRight, 2) = zRight; |
||||
|
Unsafe.Add(ref dBottomRight, 3) = wRight; |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void UseVector4() |
||||
|
{ |
||||
|
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin()); |
||||
|
int destStride = this.destArea.Stride / 2; |
||||
|
|
||||
|
ref Block8x8F src = ref this.block; |
||||
|
|
||||
|
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 0, destStride); |
||||
|
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 1, destStride); |
||||
|
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 2, destStride); |
||||
|
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 3, destStride); |
||||
|
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 4, destStride); |
||||
|
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 5, destStride); |
||||
|
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 6, destStride); |
||||
|
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 7, destStride); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static void WidenCopyImpl2x2_Vector4(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) |
||||
|
{ |
||||
|
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); |
||||
|
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); |
||||
|
|
||||
|
ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); |
||||
|
ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); |
||||
|
ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); |
||||
|
ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); |
||||
|
|
||||
|
var xLeft = new Vector4(sLeft.X); |
||||
|
var yLeft = new Vector4(sLeft.Y); |
||||
|
var zLeft = new Vector4(sLeft.Z); |
||||
|
var wLeft = new Vector4(sLeft.W); |
||||
|
|
||||
|
var xRight = new Vector4(sRight.X); |
||||
|
var yRight = new Vector4(sRight.Y); |
||||
|
var zRight = new Vector4(sRight.Z); |
||||
|
var wRight = new Vector4(sRight.W); |
||||
|
|
||||
|
Unsafe.As<Vector2, Vector4>(ref dTopLeft) = xLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft; |
||||
|
|
||||
|
Unsafe.As<Vector2, Vector4>(ref dTopRight) = xRight; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 1)) = yRight; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 2)) = zRight; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 3)) = wRight; |
||||
|
|
||||
|
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft; |
||||
|
|
||||
|
Unsafe.As<Vector2, Vector4>(ref dBottomRight) = xRight; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 1)) = yRight; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 2)) = zRight; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 3)) = wRight; |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void UseVector4_SafeRightCorner() |
||||
|
{ |
||||
|
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin()); |
||||
|
int destStride = this.destArea.Stride / 2; |
||||
|
|
||||
|
ref Block8x8F src = ref this.block; |
||||
|
|
||||
|
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 0, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 1, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 2, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 3, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 4, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 5, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 6, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 7, destStride); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static void WidenCopyImpl2x2_Vector4_SafeRightCorner(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) |
||||
|
{ |
||||
|
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); |
||||
|
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); |
||||
|
|
||||
|
ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); |
||||
|
ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); |
||||
|
|
||||
|
var xLeft = new Vector4(sLeft.X); |
||||
|
var yLeft = new Vector4(sLeft.Y); |
||||
|
var zLeft = new Vector4(sLeft.Z); |
||||
|
var wLeft = new Vector4(sLeft.W); |
||||
|
|
||||
|
var xRight = new Vector4(sRight.X); |
||||
|
var yRight = new Vector4(sRight.Y); |
||||
|
var zRight = new Vector4(sRight.Z); |
||||
|
var wRight = new Vector2(sRight.W); |
||||
|
|
||||
|
Unsafe.As<Vector2, Vector4>(ref dTopLeft) = xLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft; |
||||
|
|
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 4)) = xRight; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 5)) = yRight; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 6)) = zRight; |
||||
|
Unsafe.Add(ref dTopLeft, 7) = wRight; |
||||
|
|
||||
|
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft; |
||||
|
|
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight; |
||||
|
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight; |
||||
|
Unsafe.Add(ref dBottomLeft, 7) = wRight; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
[Benchmark] |
||||
|
public void UseVector4_V2() |
||||
|
{ |
||||
|
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin()); |
||||
|
int destStride = this.destArea.Stride / 2; |
||||
|
|
||||
|
ref Block8x8F src = ref this.block; |
||||
|
|
||||
|
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 0, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 1, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 2, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 3, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 4, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 5, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 6, destStride); |
||||
|
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 7, destStride); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static void WidenCopyImpl2x2_Vector4_V2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) |
||||
|
{ |
||||
|
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); |
||||
|
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); |
||||
|
|
||||
|
int offset = 2 * row * destStride; |
||||
|
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset)); |
||||
|
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride)); |
||||
|
|
||||
|
var xyLeft = new Vector4(sLeft.X); |
||||
|
xyLeft.Z = sLeft.Y; |
||||
|
xyLeft.W = sLeft.Y; |
||||
|
|
||||
|
var zwLeft = new Vector4(sLeft.Z); |
||||
|
zwLeft.Z = sLeft.W; |
||||
|
zwLeft.W = sLeft.W; |
||||
|
|
||||
|
var xyRight = new Vector4(sRight.X); |
||||
|
xyRight.Z = sRight.Y; |
||||
|
xyRight.W = sRight.Y; |
||||
|
|
||||
|
var zwRight = new Vector4(sRight.Z); |
||||
|
zwRight.Z = sRight.W; |
||||
|
zwRight.W = sRight.W; |
||||
|
|
||||
|
dTopLeft = xyLeft; |
||||
|
Unsafe.Add(ref dTopLeft, 1) = zwLeft; |
||||
|
Unsafe.Add(ref dTopLeft, 2) = xyRight; |
||||
|
Unsafe.Add(ref dTopLeft, 3) = zwRight; |
||||
|
|
||||
|
dBottomLeft = xyLeft; |
||||
|
Unsafe.Add(ref dBottomLeft, 1) = zwLeft; |
||||
|
Unsafe.Add(ref dBottomLeft, 2) = xyRight; |
||||
|
Unsafe.Add(ref dBottomLeft, 3) = zwRight; |
||||
|
} |
||||
|
|
||||
|
// RESULTS:
|
||||
|
// Method | Mean | Error | StdDev | Scaled | ScaledSD |
|
||||
|
// --------------------------- |---------:|----------:|----------:|-------:|---------:|
|
||||
|
// Original | 92.69 ns | 2.4722 ns | 2.7479 ns | 1.00 | 0.00 |
|
||||
|
// Original_V2 | 91.72 ns | 1.2089 ns | 1.0095 ns | 0.99 | 0.03 |
|
||||
|
// UseVector2 | 86.70 ns | 0.5873 ns | 0.5206 ns | 0.94 | 0.03 |
|
||||
|
// UseVector4 | 55.42 ns | 0.2482 ns | 0.2322 ns | 0.60 | 0.02 |
|
||||
|
// UseVector4_SafeRightCorner | 58.97 ns | 0.4152 ns | 0.3884 ns | 0.64 | 0.02 |
|
||||
|
// UseVector4_V2 | 41.88 ns | 0.3531 ns | 0.3303 ns | 0.45 | 0.01 |
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations |
||||
|
{ |
||||
|
public class Block8x8F_LoadFromInt16 |
||||
|
{ |
||||
|
private Block8x8 source; |
||||
|
|
||||
|
private Block8x8F dest = default; |
||||
|
|
||||
|
[GlobalSetup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
if (Vector<float>.Count != 8) |
||||
|
{ |
||||
|
throw new NotSupportedException("Vector<float>.Count != 8"); |
||||
|
} |
||||
|
|
||||
|
for (short i = 0; i < Block8x8F.Size; i++) |
||||
|
{ |
||||
|
this.source[i] = i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void Scalar() |
||||
|
{ |
||||
|
this.dest.LoadFromInt16Scalar(ref this.source); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void ExtendedAvx2() |
||||
|
{ |
||||
|
this.dest.LoadFromInt16ExtendedAvx2(ref this.source); |
||||
|
} |
||||
|
|
||||
|
// RESULT:
|
||||
|
// Method | Mean | Error | StdDev | Scaled |
|
||||
|
// ------------- |---------:|----------:|----------:|-------:|
|
||||
|
// Scalar | 34.88 ns | 0.3296 ns | 0.3083 ns | 1.00 |
|
||||
|
// ExtendedAvx2 | 21.58 ns | 0.2125 ns | 0.1884 ns | 0.62 |
|
||||
|
} |
||||
|
} |
||||
@ -1,59 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System.Drawing; |
|
||||
using System.IO; |
|
||||
using BenchmarkDotNet.Attributes; |
|
||||
|
|
||||
using SixLabors.ImageSharp.Formats.Jpeg; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
using SixLabors.ImageSharp.Tests; |
|
||||
using CoreSize = SixLabors.Primitives.Size; |
|
||||
using SDImage = System.Drawing.Image; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
|
||||
{ |
|
||||
[Config(typeof(Config.ShortClr))] |
|
||||
public class DecodeJpeg : BenchmarkBase |
|
||||
{ |
|
||||
private byte[] jpegBytes; |
|
||||
|
|
||||
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
|
||||
|
|
||||
[Params(TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Calliphora)] |
|
||||
public string TestImage { get; set; } |
|
||||
|
|
||||
[GlobalSetup] |
|
||||
public void ReadImages() |
|
||||
{ |
|
||||
if (this.jpegBytes == null) |
|
||||
{ |
|
||||
this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")] |
|
||||
public Size JpegSystemDrawing() |
|
||||
{ |
|
||||
using (var memoryStream = new MemoryStream(this.jpegBytes)) |
|
||||
{ |
|
||||
using (var image = SDImage.FromStream(memoryStream)) |
|
||||
{ |
|
||||
return image.Size; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Benchmark(Description = "Decode Jpeg - ImageSharp")] |
|
||||
public CoreSize JpegImageSharp() |
|
||||
{ |
|
||||
using (var memoryStream = new MemoryStream(this.jpegBytes)) |
|
||||
{ |
|
||||
using (var image = Image.Load<Rgba32>(memoryStream, new JpegDecoder())) |
|
||||
{ |
|
||||
return new CoreSize(image.Width, image.Height); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,35 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using BenchmarkDotNet.Attributes; |
|
||||
using SixLabors.ImageSharp.Formats.Jpeg; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
using SDImage = System.Drawing.Image; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
|
||||
{ |
|
||||
[Config(typeof(Config.ShortClr))] |
|
||||
public class DecodeJpegMultiple : MultiImageBenchmarkBase |
|
||||
{ |
|
||||
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[] |
|
||||
{ |
|
||||
"Jpg/baseline", |
|
||||
"Jpg/progressive", |
|
||||
}; |
|
||||
|
|
||||
protected override IEnumerable<string> SearchPatterns => new[] { "*.jpg" }; |
|
||||
|
|
||||
[Benchmark(Description = "DecodeJpegMultiple - ImageSharp")] |
|
||||
public void DecodeJpegImageSharp() |
|
||||
{ |
|
||||
this.ForEachStream(ms => Image.Load<Rgba32>(ms, new JpegDecoder())); |
|
||||
} |
|
||||
|
|
||||
[Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")] |
|
||||
public void DecodeJpegSystemDrawing() |
|
||||
{ |
|
||||
this.ForEachStream(SDImage.FromStream); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,48 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Jpeg; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Tests; |
||||
|
|
||||
|
using SDImage = System.Drawing.Image; |
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// An expensive Jpeg benchmark, running on a wide range of input images, showing aggregate results.
|
||||
|
/// </summary>
|
||||
|
[Config(typeof(MultiImageBenchmarkBase.Config))] |
||||
|
public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase |
||||
|
{ |
||||
|
protected override IEnumerable<string> InputImageSubfoldersOrFiles => |
||||
|
new[] |
||||
|
{ |
||||
|
TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, |
||||
|
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, |
||||
|
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, |
||||
|
TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, |
||||
|
TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, |
||||
|
}; |
||||
|
|
||||
|
[Params(InputImageCategory.AllImages)] |
||||
|
public override InputImageCategory InputCategory { get; set; } |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void ImageSharp() |
||||
|
{ |
||||
|
this.ForEachStream(ms => Image.Load<Rgba32>(ms, new JpegDecoder())); |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void SystemDrawing() |
||||
|
{ |
||||
|
this.ForEachStream(SDImage.FromStream); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,119 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Drawing; |
||||
|
using System.IO; |
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
using BenchmarkDotNet.Configs; |
||||
|
using BenchmarkDotNet.Jobs; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Jpeg; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Tests; |
||||
|
using CoreSize = SixLabors.Primitives.Size; |
||||
|
using SDImage = System.Drawing.Image; |
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Image-specific Jpeg benchmarks
|
||||
|
/// </summary>
|
||||
|
[Config(typeof(Config.ShortClr))] |
||||
|
public class DecodeJpeg_ImageSpecific |
||||
|
{ |
||||
|
public class Config : ManualConfig |
||||
|
{ |
||||
|
public Config() |
||||
|
{ |
||||
|
// Uncomment if you want to use any of the diagnoser
|
||||
|
this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser()); |
||||
|
} |
||||
|
|
||||
|
public class ShortClr : Benchmarks.Config |
||||
|
{ |
||||
|
public ShortClr() |
||||
|
{ |
||||
|
this.Add( |
||||
|
//Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3),
|
||||
|
Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private byte[] jpegBytes; |
||||
|
|
||||
|
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
||||
|
|
||||
|
[Params( |
||||
|
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, |
||||
|
TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr, |
||||
|
|
||||
|
// The scaled result for the large image "ExifGetString750Transform_Huge420YCbCr"
|
||||
|
// is almost the same as the result for Jpeg420Exif,
|
||||
|
// which proves that the execution time for the most common YCbCr 420 path scales linearly.
|
||||
|
//
|
||||
|
// TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
|
||||
|
|
||||
|
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr |
||||
|
)] |
||||
|
public string TestImage { get; set; } |
||||
|
|
||||
|
|
||||
|
[GlobalSetup] |
||||
|
public void ReadImages() |
||||
|
{ |
||||
|
if (this.jpegBytes == null) |
||||
|
{ |
||||
|
this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")] |
||||
|
public Size JpegSystemDrawing() |
||||
|
{ |
||||
|
using (var memoryStream = new MemoryStream(this.jpegBytes)) |
||||
|
{ |
||||
|
using (var image = SDImage.FromStream(memoryStream)) |
||||
|
{ |
||||
|
return image.Size; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Description = "Decode Jpeg - ImageSharp")] |
||||
|
public CoreSize JpegImageSharp() |
||||
|
{ |
||||
|
using (var memoryStream = new MemoryStream(this.jpegBytes)) |
||||
|
{ |
||||
|
using (var image = Image.Load<Rgba32>(memoryStream, new JpegDecoder(){ IgnoreMetadata = true})) |
||||
|
{ |
||||
|
return new CoreSize(image.Width, image.Height); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// RESULTS (2018 November 4):
|
||||
|
//
|
||||
|
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
|
||||
|
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
|
||||
|
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
|
||||
|
// .NET Core SDK=2.1.403
|
||||
|
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
|
||||
|
//
|
||||
|
// Method | TestImage | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
|
||||
|
// ------------------------------- |-------------------------------------------- |-----------:|-----------:|----------:|-------:|---------:|----------:|---------:|---------:|------------:|
|
||||
|
// 'Decode Jpeg - System.Drawing' | Jpg/baseline/Lake.jpg | 6.117 ms | 0.3923 ms | 0.0222 ms | 1.00 | 0.00 | 62.5000 | - | - | 205.83 KB |
|
||||
|
// 'Decode Jpeg - ImageSharp' | Jpg/baseline/Lake.jpg | 18.126 ms | 0.6023 ms | 0.0340 ms | 2.96 | 0.01 | - | - | - | 19.97 KB |
|
||||
|
// | | | | | | | | | | |
|
||||
|
// 'Decode Jpeg - System.Drawing' | Jpg/baseline/jpeg420exif.jpg | 17.063 ms | 2.6096 ms | 0.1474 ms | 1.00 | 0.00 | 218.7500 | - | - | 757.04 KB |
|
||||
|
// 'Decode Jpeg - ImageSharp' | Jpg/baseline/jpeg420exif.jpg | 41.366 ms | 1.0115 ms | 0.0572 ms | 2.42 | 0.02 | - | - | - | 21.94 KB |
|
||||
|
// | | | | | | | | | | |
|
||||
|
// 'Decode Jpeg - System.Drawing' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 428.282 ms | 94.9163 ms | 5.3629 ms | 1.00 | 0.00 | 2375.0000 | - | - | 7403.76 KB |
|
||||
|
// 'Decode Jpeg - ImageSharp' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 386.698 ms | 33.0065 ms | 1.8649 ms | 0.90 | 0.01 | 125.0000 | 125.0000 | 125.0000 | 35186.97 KB |
|
||||
|
// | | | | | | | | | | |
|
||||
|
// 'Decode Jpeg - System.Drawing' | Jpg/issues/issue750-exif-tranform.jpg | 95.192 ms | 3.1762 ms | 0.1795 ms | 1.00 | 0.00 | 1750.0000 | - | - | 5492.63 KB |
|
||||
|
// 'Decode Jpeg - ImageSharp' | Jpg/issues/issue750-exif-tranform.jpg | 230.158 ms | 48.8128 ms | 2.7580 ms | 2.42 | 0.02 | 312.5000 | 312.5000 | 312.5000 | 58834.66 KB |
|
||||
|
} |
||||
|
} |
||||
@ -1,86 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using BenchmarkDotNet.Attributes; |
|
||||
using System; |
|
||||
using System.IO; |
|
||||
using SixLabors.ImageSharp.Tests; |
|
||||
using System.Drawing; |
|
||||
using System.Drawing.Drawing2D; |
|
||||
using System.Drawing.Imaging; |
|
||||
using SixLabors.ImageSharp.Processing; |
|
||||
using SDImage = System.Drawing.Image; |
|
||||
using SixLabors.ImageSharp.Formats.Jpeg; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
|
||||
{ |
|
||||
[Config(typeof(Config.ShortClr))] |
|
||||
public class LoadResizeSave : BenchmarkBase |
|
||||
{ |
|
||||
private readonly Configuration configuration = new Configuration(new JpegConfigurationModule()); |
|
||||
|
|
||||
private byte[] sourceBytes; |
|
||||
|
|
||||
private byte[] destBytes; |
|
||||
|
|
||||
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
|
||||
|
|
||||
[Params( |
|
||||
TestImages.Jpeg.Baseline.Jpeg420Exif |
|
||||
//, TestImages.Jpeg.Baseline.Calliphora
|
|
||||
)] |
|
||||
public string TestImage { get; set; } |
|
||||
|
|
||||
[Params(false, true)] |
|
||||
public bool EnableParallelExecution { get; set; } |
|
||||
|
|
||||
[GlobalSetup] |
|
||||
public void Setup() |
|
||||
{ |
|
||||
this.configuration.MaxDegreeOfParallelism = |
|
||||
this.EnableParallelExecution ? Environment.ProcessorCount : 1; |
|
||||
|
|
||||
if (this.sourceBytes == null) |
|
||||
{ |
|
||||
this.sourceBytes = File.ReadAllBytes(this.TestImageFullPath); |
|
||||
} |
|
||||
|
|
||||
if (this.destBytes == null) |
|
||||
{ |
|
||||
this.destBytes = new byte[this.sourceBytes.Length]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Benchmark(Baseline = true)] |
|
||||
public void SystemDrawing() |
|
||||
{ |
|
||||
using (var sourceStream = new MemoryStream(this.sourceBytes)) |
|
||||
using (var destStream = new MemoryStream(this.destBytes)) |
|
||||
using (var source = SDImage.FromStream(sourceStream)) |
|
||||
using (var destination = new Bitmap(source.Width / 4, source.Height / 4)) |
|
||||
{ |
|
||||
using (var graphics = Graphics.FromImage(destination)) |
|
||||
{ |
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; |
|
||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; |
|
||||
graphics.CompositingQuality = CompositingQuality.HighQuality; |
|
||||
graphics.DrawImage(source, 0, 0, 400, 400); |
|
||||
} |
|
||||
|
|
||||
destination.Save(destStream, ImageFormat.Jpeg); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Benchmark] |
|
||||
public void ImageSharp() |
|
||||
{ |
|
||||
var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true }); |
|
||||
using (source) |
|
||||
using (var destStream = new MemoryStream(this.destBytes)) |
|
||||
{ |
|
||||
source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4)); |
|
||||
source.SaveAsJpeg(destStream); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,96 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Drawing; |
||||
|
using System.Drawing.Drawing2D; |
||||
|
using System.Drawing.Imaging; |
||||
|
using System.IO; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Jpeg; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Processing; |
||||
|
using SixLabors.ImageSharp.Tests; |
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
||||
|
{ |
||||
|
[Config(typeof(MultiImageBenchmarkBase.Config))] |
||||
|
public class LoadResizeSave_Aggregate : MultiImageBenchmarkBase |
||||
|
{ |
||||
|
protected override IEnumerable<string> InputImageSubfoldersOrFiles => |
||||
|
new[] |
||||
|
{ |
||||
|
TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, |
||||
|
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, |
||||
|
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, |
||||
|
TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, |
||||
|
TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, |
||||
|
}; |
||||
|
|
||||
|
[Params(InputImageCategory.AllImages)] |
||||
|
public override InputImageCategory InputCategory { get; set; } |
||||
|
|
||||
|
private readonly Configuration configuration = new Configuration(new JpegConfigurationModule()); |
||||
|
|
||||
|
private byte[] destBytes; |
||||
|
|
||||
|
public override void Setup() |
||||
|
{ |
||||
|
base.Setup(); |
||||
|
|
||||
|
this.configuration.MaxDegreeOfParallelism = 1; |
||||
|
const int MaxOutputSizeInBytes = 2 * 1024 * 1024; // ~2 MB
|
||||
|
this.destBytes = new byte[MaxOutputSizeInBytes]; |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void SystemDrawing() |
||||
|
{ |
||||
|
this.ForEachStream( |
||||
|
sourceStream => |
||||
|
{ |
||||
|
using (var destStream = new MemoryStream(this.destBytes)) |
||||
|
using (var source = System.Drawing.Image.FromStream(sourceStream)) |
||||
|
using (var destination = new Bitmap(source.Width / 4, source.Height / 4)) |
||||
|
{ |
||||
|
using (var g = Graphics.FromImage(destination)) |
||||
|
{ |
||||
|
g.InterpolationMode = InterpolationMode.HighQualityBicubic; |
||||
|
g.PixelOffsetMode = PixelOffsetMode.HighQuality; |
||||
|
g.CompositingQuality = CompositingQuality.HighQuality; |
||||
|
g.DrawImage(source, 0, 0, 400, 400); |
||||
|
} |
||||
|
|
||||
|
destination.Save(destStream, ImageFormat.Jpeg); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void ImageSharp() |
||||
|
{ |
||||
|
this.ForEachStream( |
||||
|
sourceStream => |
||||
|
{ |
||||
|
using (var source = Image.Load<Rgba32>( |
||||
|
this.configuration, |
||||
|
sourceStream, |
||||
|
new JpegDecoder { IgnoreMetadata = true })) |
||||
|
{ |
||||
|
using (var destStream = new MemoryStream(this.destBytes)) |
||||
|
{ |
||||
|
source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4)); |
||||
|
source.SaveAsJpeg(destStream); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
using System; |
||||
|
using System.IO; |
||||
|
using SixLabors.ImageSharp.Tests; |
||||
|
using System.Drawing; |
||||
|
using System.Drawing.Drawing2D; |
||||
|
using System.Drawing.Imaging; |
||||
|
using SixLabors.ImageSharp.Processing; |
||||
|
using SDImage = System.Drawing.Image; |
||||
|
using SixLabors.ImageSharp.Formats.Jpeg; |
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg |
||||
|
{ |
||||
|
[Config(typeof(Config.ShortClr))] |
||||
|
public class LoadResizeSave_ImageSpecific |
||||
|
{ |
||||
|
private readonly Configuration configuration = new Configuration(new JpegConfigurationModule()); |
||||
|
|
||||
|
private byte[] sourceBytes; |
||||
|
|
||||
|
private byte[] destBytes; |
||||
|
|
||||
|
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); |
||||
|
|
||||
|
[Params( |
||||
|
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, |
||||
|
TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr, |
||||
|
|
||||
|
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr |
||||
|
)] |
||||
|
public string TestImage { get; set; } |
||||
|
|
||||
|
[Params(false, true)] |
||||
|
public bool ParallelExec { get; set; } |
||||
|
|
||||
|
[GlobalSetup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.configuration.MaxDegreeOfParallelism = |
||||
|
this.ParallelExec ? Environment.ProcessorCount : 1; |
||||
|
|
||||
|
this.sourceBytes = File.ReadAllBytes(this.TestImageFullPath); |
||||
|
|
||||
|
this.destBytes = new byte[this.sourceBytes.Length * 2]; |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void SystemDrawing() |
||||
|
{ |
||||
|
using (var sourceStream = new MemoryStream(this.sourceBytes)) |
||||
|
using (var destStream = new MemoryStream(this.destBytes)) |
||||
|
using (var source = SDImage.FromStream(sourceStream)) |
||||
|
using (var destination = new Bitmap(source.Width / 4, source.Height / 4)) |
||||
|
{ |
||||
|
using (var g = Graphics.FromImage(destination)) |
||||
|
{ |
||||
|
g.InterpolationMode = InterpolationMode.HighQualityBicubic; |
||||
|
g.PixelOffsetMode = PixelOffsetMode.HighQuality; |
||||
|
g.CompositingQuality = CompositingQuality.HighQuality; |
||||
|
g.DrawImage(source, 0, 0, 400, 400); |
||||
|
} |
||||
|
|
||||
|
destination.Save(destStream, ImageFormat.Jpeg); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void ImageSharp() |
||||
|
{ |
||||
|
var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true }); |
||||
|
using (source) |
||||
|
using (var destStream = new MemoryStream(this.destBytes)) |
||||
|
{ |
||||
|
source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4)); |
||||
|
source.SaveAsJpeg(destStream); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// RESULTS (2018 October 31):
|
||||
|
//
|
||||
|
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
|
||||
|
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
|
||||
|
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
|
||||
|
// .NET Core SDK=2.1.403
|
||||
|
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
|
||||
|
// Job-ZPEZGV : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
|
||||
|
// Job-SGOCJT : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
|
||||
|
//
|
||||
|
// Method | Runtime | TestImage | ParallelExec | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated |
|
||||
|
// -------------- |-------- |----------------------------- |------------- |----------:|----------:|----------:|-------:|---------:|---------:|----------:|
|
||||
|
// SystemDrawing | Clr | Jpg/baseline/jpeg420exif.jpg | False | 64.88 ms | 3.735 ms | 0.2110 ms | 1.00 | 0.00 | 250.0000 | 791.07 KB |
|
||||
|
// ImageSharp | Clr | Jpg/baseline/jpeg420exif.jpg | False | 129.53 ms | 23.423 ms | 1.3234 ms | 2.00 | 0.02 | - | 50.09 KB |
|
||||
|
// | | | | | | | | | | |
|
||||
|
// SystemDrawing | Core | Jpg/baseline/jpeg420exif.jpg | False | 65.87 ms | 10.488 ms | 0.5926 ms | 1.00 | 0.00 | 250.0000 | 789.79 KB |
|
||||
|
// ImageSharp | Core | Jpg/baseline/jpeg420exif.jpg | False | 92.00 ms | 7.241 ms | 0.4091 ms | 1.40 | 0.01 | - | 46.36 KB |
|
||||
|
// | | | | | | | | | | |
|
||||
|
// SystemDrawing | Clr | Jpg/baseline/jpeg420exif.jpg | True | 64.23 ms | 5.998 ms | 0.3389 ms | 1.00 | 0.00 | 250.0000 | 791.07 KB |
|
||||
|
// ImageSharp | Clr | Jpg/baseline/jpeg420exif.jpg | True | 82.63 ms | 29.320 ms | 1.6566 ms | 1.29 | 0.02 | - | 57.59 KB |
|
||||
|
// | | | | | | | | | | |
|
||||
|
// SystemDrawing | Core | Jpg/baseline/jpeg420exif.jpg | True | 64.20 ms | 6.560 ms | 0.3707 ms | 1.00 | 0.00 | 250.0000 | 789.79 KB |
|
||||
|
// ImageSharp | Core | Jpg/baseline/jpeg420exif.jpg | True | 68.08 ms | 18.376 ms | 1.0383 ms | 1.06 | 0.01 | - | 50.49 KB |
|
||||
|
} |
||||
|
} |
||||
@ -1,35 +0,0 @@ |
|||||
using SixLabors.ImageSharp.Formats.Jpeg; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
using SixLabors.ImageSharp.Processing; |
|
||||
using SixLabors.Primitives; |
|
||||
|
|
||||
using Xunit; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Tests |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Might be useful to catch complex bugs
|
|
||||
/// </summary>
|
|
||||
public class ComplexIntegrationTests |
|
||||
{ |
|
||||
[Theory] |
|
||||
[WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] |
|
||||
[WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] |
|
||||
[WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] |
|
||||
[WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] |
|
||||
public void LoadResizeSave<TPixel>(TestImageProvider<TPixel> provider, int quality, JpegSubsample subsample) |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
using (Image<TPixel> image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) |
|
||||
{ |
|
||||
|
|
||||
image.MetaData.ExifProfile = null; // Reduce the size of the file
|
|
||||
JpegEncoder options = new JpegEncoder { Subsample = subsample, Quality = quality }; |
|
||||
|
|
||||
provider.Utility.TestName += $"{subsample}_Q{quality}"; |
|
||||
provider.Utility.SaveTestOutputFile(image, "png"); |
|
||||
provider.Utility.SaveTestOutputFile(image, "jpg", options); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,58 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Processing; |
||||
|
using SixLabors.ImageSharp.Processing.Processors.Quantization; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization |
||||
|
{ |
||||
|
public class OctreeQuantizerTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void OctreeQuantizerConstructor() |
||||
|
{ |
||||
|
var quantizer = new OctreeQuantizer(128); |
||||
|
|
||||
|
Assert.Equal(128, quantizer.MaxColors); |
||||
|
Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser); |
||||
|
|
||||
|
quantizer = new OctreeQuantizer(false); |
||||
|
Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors); |
||||
|
Assert.Null(quantizer.Diffuser); |
||||
|
|
||||
|
quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson); |
||||
|
Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors); |
||||
|
Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser); |
||||
|
|
||||
|
quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson, 128); |
||||
|
Assert.Equal(128, quantizer.MaxColors); |
||||
|
Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void OctreeQuantizerCanCreateFrameQuantizer() |
||||
|
{ |
||||
|
var quantizer = new OctreeQuantizer(); |
||||
|
IFrameQuantizer<Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default); |
||||
|
|
||||
|
Assert.NotNull(frameQuantizer); |
||||
|
Assert.True(frameQuantizer.Dither); |
||||
|
Assert.Equal(KnownDiffusers.FloydSteinberg, frameQuantizer.Diffuser); |
||||
|
|
||||
|
quantizer = new OctreeQuantizer(false); |
||||
|
frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default); |
||||
|
|
||||
|
Assert.NotNull(frameQuantizer); |
||||
|
Assert.False(frameQuantizer.Dither); |
||||
|
Assert.Null(frameQuantizer.Diffuser); |
||||
|
|
||||
|
quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson); |
||||
|
frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default); |
||||
|
Assert.NotNull(frameQuantizer); |
||||
|
Assert.True(frameQuantizer.Dither); |
||||
|
Assert.Equal(KnownDiffusers.Atkinson, frameQuantizer.Diffuser); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue