Browse Source

Better performance

af/qhack03
James Jackson-South 6 years ago
parent
commit
4071c65f4c
  1. 4
      src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
  2. 2
      src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs
  3. 4
      src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs
  4. 2
      src/ImageSharp/Processing/Processors/Dithering/OrderedDither.KnownTypes.cs
  5. 4
      src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs
  6. 2
      src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
  7. 36
      src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs
  8. 2
      src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerExtensions.cs
  9. 2
      src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs
  10. 4
      src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs
  11. 4
      src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs
  12. 3
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs
  13. 4
      src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs
  14. 4
      src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs
  15. 38
      src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs
  16. 4
      src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs

4
src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs

@ -18,12 +18,12 @@ namespace SixLabors.ImageSharp.Memory
/// Gets the number of elements per contiguous sub-buffer preceding the last buffer.
/// The last buffer is allowed to be smaller.
/// </summary>
public int BufferLength { get; }
int BufferLength { get; }
/// <summary>
/// Gets the aggregate number of elements in the group.
/// </summary>
public long TotalLength { get; }
long TotalLength { get; }
/// <summary>
/// Gets a value indicating whether the group has been invalidated.

2
src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs

@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
for (int x = bounds.Left; x < bounds.Right; x++)
{
TPixel sourcePixel = row[x];
TPixel transformed = processor.GetPaletteColor(sourcePixel, palette);
TPixel transformed = Unsafe.AsRef(processor).GetPaletteColor(sourcePixel, palette);
this.Dither(source, bounds, sourcePixel, transformed, x, y, scale);
row[x] = transformed;
}

4
src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs

@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
where TPixel : unmanaged, IPixel<TPixel>
{
/// <summary>
/// Gets the configration instance to use when performing operations.
/// Gets the configuration instance to use when performing operations.
/// </summary>
public Configuration Configuration { get; }
Configuration Configuration { get; }
/// <summary>
/// Gets the dithering palette.

2
src/ImageSharp/Processing/Processors/Dithering/OrderedDither.KnownTypes.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// <content>
/// An ordered dithering matrix with equal sides of arbitrary length
/// </content>
public readonly partial struct OrderedDither : IDither
public readonly partial struct OrderedDither
{
/// <summary>
/// Applies order dithering using the 2x2 Bayer dithering matrix.

4
src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs

@ -237,7 +237,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
for (int x = this.bounds.Left; x < this.bounds.Right; x++)
{
TPixel dithered = this.dither.Dither(sourceRow[x], x, y, this.bitDepth, scale);
destinationRow[x - offsetX] = this.quantizer.GetQuantizedColor(dithered, paletteSpan, out TPixel _);
destinationRow[x - offsetX] = Unsafe.AsRef(this.quantizer).GetQuantizedColor(dithered, paletteSpan, out TPixel _);
}
}
}
@ -281,7 +281,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
{
ref TPixel sourcePixel = ref row[x];
TPixel dithered = this.dither.Dither(sourcePixel, x, y, this.bitDepth, this.scale);
sourcePixel = this.processor.GetPaletteColor(dithered, paletteSpan);
sourcePixel = Unsafe.AsRef(this.processor).GetPaletteColor(dithered, paletteSpan);
}
}
}

2
src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs

@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
float ditherScale)
{
this.Configuration = configuration;
this.pixelMap = new EuclideanPixelMap<TPixel>(palette);
this.pixelMap = new EuclideanPixelMap<TPixel>(configuration, palette);
this.Palette = palette;
this.DitherScale = ditherScale;
}

36
src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Concurrent;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
@ -17,27 +18,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
internal readonly struct EuclideanPixelMap<TPixel> : IPixelMap<TPixel>, IEquatable<EuclideanPixelMap<TPixel>>
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly ConcurrentDictionary<int, Vector4> vectorCache;
private readonly Vector4[] vectorCache;
private readonly ConcurrentDictionary<TPixel, int> distanceCache;
/// <summary>
/// Initializes a new instance of the <see cref="EuclideanPixelMap{TPixel}"/> struct.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="palette">The color palette to map from.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public EuclideanPixelMap(ReadOnlyMemory<TPixel> palette)
public EuclideanPixelMap(Configuration configuration, ReadOnlyMemory<TPixel> palette)
{
Guard.MustBeGreaterThan(palette.Length, 0, nameof(palette));
this.Palette = palette;
ReadOnlySpan<TPixel> paletteSpan = this.Palette.Span;
this.vectorCache = new ConcurrentDictionary<int, Vector4>();
this.vectorCache = new Vector4[paletteSpan.Length];
this.distanceCache = new ConcurrentDictionary<TPixel, int>();
for (int i = 0; i < paletteSpan.Length; i++)
{
this.vectorCache[i] = paletteSpan[i].ToScaledVector4();
}
PixelOperations<TPixel>.Instance.ToVector4(configuration, paletteSpan, this.vectorCache);
}
/// <inheritdoc/>
@ -81,31 +80,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
int index = 0;
float leastDistance = float.MaxValue;
Vector4 vector = color.ToScaledVector4();
ref TPixel paletteRef = ref MemoryMarshal.GetReference(palette);
ref Vector4 vectorCacheRef = ref MemoryMarshal.GetReference<Vector4>(this.vectorCache);
for (int i = 0; i < palette.Length; i++)
{
Vector4 candidate = this.vectorCache[i];
Vector4 candidate = Unsafe.Add(ref vectorCacheRef, i);
float distance = Vector4.DistanceSquared(vector, candidate);
if (!(distance < leastDistance))
// If it's an exact match, exit the loop
if (distance == 0)
{
continue;
index = i;
break;
}
// Less than... assign.
index = i;
leastDistance = distance;
// And if it's an exact match, exit the loop
if (distance == 0)
if (distance < leastDistance)
{
break;
// Less than... assign.
index = i;
leastDistance = distance;
}
}
// Now I have the index, pop it into the cache for next time
this.distanceCache[color] = index;
match = palette[index];
match = Unsafe.Add(ref paletteRef, index);
return index;
}
}

2
src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerExtensions.cs

@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
for (int x = this.bounds.Left; x < this.bounds.Right; x++)
{
destinationRow[x - offsetX] = this.quantizer.GetQuantizedColor(sourceRow[x], paletteSpan, out TPixel _);
destinationRow[x - offsetX] = Unsafe.AsRef(this.quantizer).GetQuantizedColor(sourceRow[x], paletteSpan, out TPixel _);
}
}
}

2
src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs

@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.octree.Palletize(paletteSpan, this.colors);
// TODO: Cannot make method readonly due to this line.
this.pixelMap = new EuclideanPixelMap<TPixel>(this.palette.Memory);
this.pixelMap = new EuclideanPixelMap<TPixel>(this.Configuration, this.palette.Memory);
return paletteSpan;
}

4
src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs

@ -11,12 +11,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
public class OctreeQuantizer : IQuantizer
{
private static readonly QuantizerOptions DefaultOptions = new QuantizerOptions();
/// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer"/> class
/// using the default <see cref="QuantizerOptions"/>.
/// </summary>
public OctreeQuantizer()
: this(new QuantizerOptions())
: this(DefaultOptions)
{
}

4
src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(maxLength);
Color.ToPixel(configuration, colors, this.paletteOwner.GetSpan());
this.pixelMap = new EuclideanPixelMap<TPixel>(this.paletteOwner.Memory);
this.pixelMap = new EuclideanPixelMap<TPixel>(configuration, this.paletteOwner.Memory);
this.isDisposed = false;
}
@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(maxLength);
palette.CopyTo(this.paletteOwner.GetSpan());
this.pixelMap = new EuclideanPixelMap<TPixel>(this.paletteOwner.Memory);
this.pixelMap = new EuclideanPixelMap<TPixel>(configuration, this.paletteOwner.Memory);
this.isDisposed = false;
}

3
src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
public class PaletteQuantizer : IQuantizer
{
private static readonly QuantizerOptions DefaultOptions = new QuantizerOptions();
private readonly ReadOnlyMemory<Color> palette;
/// <summary>
@ -18,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="palette">The color palette.</param>
public PaletteQuantizer(ReadOnlyMemory<Color> palette)
: this(palette, new QuantizerOptions())
: this(palette, DefaultOptions)
{
}

4
src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs

@ -10,11 +10,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
public class WebSafePaletteQuantizer : PaletteQuantizer
{
private static readonly QuantizerOptions DefaultOptions = new QuantizerOptions();
/// <summary>
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
/// </summary>
public WebSafePaletteQuantizer()
: this(new QuantizerOptions())
: this(DefaultOptions)
{
}

4
src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs

@ -9,11 +9,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
public class WernerPaletteQuantizer : PaletteQuantizer
{
private static readonly QuantizerOptions DefaultOptions = new QuantizerOptions();
/// <summary>
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
/// </summary>
public WernerPaletteQuantizer()
: this(new QuantizerOptions())
: this(DefaultOptions)
{
}

38
src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs

@ -132,30 +132,30 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
// TODO: Cannot make methods readonly due to this line.
this.pixelMap = new EuclideanPixelMap<TPixel>(this.palette.Memory);
this.pixelMap = new EuclideanPixelMap<TPixel>(this.Configuration, this.palette.Memory);
return paletteSpan;
}
/// <inheritdoc/>
public readonly byte GetQuantizedColor(TPixel color, ReadOnlySpan<TPixel> palette, out TPixel match)
{
if (!this.isDithering)
if (this.isDithering)
{
Rgba32 rgba = default;
color.ToRgba32(ref rgba);
int r = rgba.R >> (8 - IndexBits);
int g = rgba.G >> (8 - IndexBits);
int b = rgba.B >> (8 - IndexBits);
int a = rgba.A >> (8 - IndexAlphaBits);
ReadOnlySpan<byte> tagSpan = this.tag.GetSpan();
byte index = tagSpan[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)];
match = palette[index];
return index;
return (byte)this.pixelMap.GetClosestColor(color, out match);
}
return (byte)this.pixelMap.GetClosestColor(color, out match);
Rgba32 rgba = default;
color.ToRgba32(ref rgba);
int r = rgba.R >> (8 - IndexBits);
int g = rgba.G >> (8 - IndexBits);
int b = rgba.B >> (8 - IndexBits);
int a = rgba.A >> (8 - IndexAlphaBits);
ReadOnlySpan<byte> tagSpan = this.tag.GetSpan();
byte index = tagSpan[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)];
match = palette[index];
return index;
}
/// <inheritdoc/>
@ -376,11 +376,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <summary>
/// Converts the histogram into moments so that we can rapidly calculate the sums of the above quantities over any desired box.
/// </summary>
/// <param name="memoryAllocator">The memory allocator used for allocating buffers.</param>
private void Get3DMoments(MemoryAllocator memoryAllocator)
/// <param name="allocator">The memory allocator used for allocating buffers.</param>
private void Get3DMoments(MemoryAllocator allocator)
{
using IMemoryOwner<Moment> volume = memoryAllocator.Allocate<Moment>(IndexCount * IndexAlphaCount);
using IMemoryOwner<Moment> area = memoryAllocator.Allocate<Moment>(IndexAlphaCount);
using IMemoryOwner<Moment> volume = allocator.Allocate<Moment>(IndexCount * IndexAlphaCount);
using IMemoryOwner<Moment> area = allocator.Allocate<Moment>(IndexAlphaCount);
Span<Moment> momentSpan = this.moments.GetSpan();
Span<Moment> volumeSpan = volume.GetSpan();

4
src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs

@ -10,12 +10,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
public class WuQuantizer : IQuantizer
{
private static readonly QuantizerOptions DefaultOptions = new QuantizerOptions();
/// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer"/> class
/// using the default <see cref="QuantizerOptions"/>.
/// </summary>
public WuQuantizer()
: this(new QuantizerOptions())
: this(DefaultOptions)
{
}

Loading…
Cancel
Save