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. /// Gets the number of elements per contiguous sub-buffer preceding the last buffer.
/// The last buffer is allowed to be smaller. /// The last buffer is allowed to be smaller.
/// </summary> /// </summary>
public int BufferLength { get; } int BufferLength { get; }
/// <summary> /// <summary>
/// Gets the aggregate number of elements in the group. /// Gets the aggregate number of elements in the group.
/// </summary> /// </summary>
public long TotalLength { get; } long TotalLength { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether the group has been invalidated. /// 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++) for (int x = bounds.Left; x < bounds.Right; x++)
{ {
TPixel sourcePixel = row[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); this.Dither(source, bounds, sourcePixel, transformed, x, y, scale);
row[x] = transformed; 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> where TPixel : unmanaged, IPixel<TPixel>
{ {
/// <summary> /// <summary>
/// Gets the configration instance to use when performing operations. /// Gets the configuration instance to use when performing operations.
/// </summary> /// </summary>
public Configuration Configuration { get; } Configuration Configuration { get; }
/// <summary> /// <summary>
/// Gets the dithering palette. /// Gets the dithering palette.

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

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// <content> /// <content>
/// An ordered dithering matrix with equal sides of arbitrary length /// An ordered dithering matrix with equal sides of arbitrary length
/// </content> /// </content>
public readonly partial struct OrderedDither : IDither public readonly partial struct OrderedDither
{ {
/// <summary> /// <summary>
/// Applies order dithering using the 2x2 Bayer dithering matrix. /// 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++) for (int x = this.bounds.Left; x < this.bounds.Right; x++)
{ {
TPixel dithered = this.dither.Dither(sourceRow[x], x, y, this.bitDepth, scale); 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]; ref TPixel sourcePixel = ref row[x];
TPixel dithered = this.dither.Dither(sourcePixel, x, y, this.bitDepth, this.scale); 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) float ditherScale)
{ {
this.Configuration = configuration; this.Configuration = configuration;
this.pixelMap = new EuclideanPixelMap<TPixel>(palette); this.pixelMap = new EuclideanPixelMap<TPixel>(configuration, palette);
this.Palette = palette; this.Palette = palette;
this.DitherScale = ditherScale; this.DitherScale = ditherScale;
} }

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

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

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

@ -11,12 +11,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary> /// </summary>
public class OctreeQuantizer : IQuantizer public class OctreeQuantizer : IQuantizer
{ {
private static readonly QuantizerOptions DefaultOptions = new QuantizerOptions();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer"/> class /// Initializes a new instance of the <see cref="OctreeQuantizer"/> class
/// using the default <see cref="QuantizerOptions"/>. /// using the default <see cref="QuantizerOptions"/>.
/// </summary> /// </summary>
public OctreeQuantizer() 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); this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(maxLength);
Color.ToPixel(configuration, colors, this.paletteOwner.GetSpan()); 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; this.isDisposed = false;
} }
@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(maxLength); this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(maxLength);
palette.CopyTo(this.paletteOwner.GetSpan()); 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; this.isDisposed = false;
} }

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

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary> /// </summary>
public class PaletteQuantizer : IQuantizer public class PaletteQuantizer : IQuantizer
{ {
private static readonly QuantizerOptions DefaultOptions = new QuantizerOptions();
private readonly ReadOnlyMemory<Color> palette; private readonly ReadOnlyMemory<Color> palette;
/// <summary> /// <summary>
@ -18,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary> /// </summary>
/// <param name="palette">The color palette.</param> /// <param name="palette">The color palette.</param>
public PaletteQuantizer(ReadOnlyMemory<Color> palette) 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> /// </summary>
public class WebSafePaletteQuantizer : PaletteQuantizer public class WebSafePaletteQuantizer : PaletteQuantizer
{ {
private static readonly QuantizerOptions DefaultOptions = new QuantizerOptions();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class. /// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
/// </summary> /// </summary>
public WebSafePaletteQuantizer() 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> /// </summary>
public class WernerPaletteQuantizer : PaletteQuantizer public class WernerPaletteQuantizer : PaletteQuantizer
{ {
private static readonly QuantizerOptions DefaultOptions = new QuantizerOptions();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class. /// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
/// </summary> /// </summary>
public WernerPaletteQuantizer() 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. // 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; return paletteSpan;
} }
/// <inheritdoc/> /// <inheritdoc/>
public readonly byte GetQuantizedColor(TPixel color, ReadOnlySpan<TPixel> palette, out TPixel match) public readonly byte GetQuantizedColor(TPixel color, ReadOnlySpan<TPixel> palette, out TPixel match)
{ {
if (!this.isDithering) if (this.isDithering)
{ {
Rgba32 rgba = default; return (byte)this.pixelMap.GetClosestColor(color, out match);
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); 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/> /// <inheritdoc/>
@ -376,11 +376,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <summary> /// <summary>
/// Converts the histogram into moments so that we can rapidly calculate the sums of the above quantities over any desired box. /// Converts the histogram into moments so that we can rapidly calculate the sums of the above quantities over any desired box.
/// </summary> /// </summary>
/// <param name="memoryAllocator">The memory allocator used for allocating buffers.</param> /// <param name="allocator">The memory allocator used for allocating buffers.</param>
private void Get3DMoments(MemoryAllocator memoryAllocator) private void Get3DMoments(MemoryAllocator allocator)
{ {
using IMemoryOwner<Moment> volume = memoryAllocator.Allocate<Moment>(IndexCount * IndexAlphaCount); using IMemoryOwner<Moment> volume = allocator.Allocate<Moment>(IndexCount * IndexAlphaCount);
using IMemoryOwner<Moment> area = memoryAllocator.Allocate<Moment>(IndexAlphaCount); using IMemoryOwner<Moment> area = allocator.Allocate<Moment>(IndexAlphaCount);
Span<Moment> momentSpan = this.moments.GetSpan(); Span<Moment> momentSpan = this.moments.GetSpan();
Span<Moment> volumeSpan = volume.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> /// </summary>
public class WuQuantizer : IQuantizer public class WuQuantizer : IQuantizer
{ {
private static readonly QuantizerOptions DefaultOptions = new QuantizerOptions();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer"/> class /// Initializes a new instance of the <see cref="WuQuantizer"/> class
/// using the default <see cref="QuantizerOptions"/>. /// using the default <see cref="QuantizerOptions"/>.
/// </summary> /// </summary>
public WuQuantizer() public WuQuantizer()
: this(new QuantizerOptions()) : this(DefaultOptions)
{ {
} }

Loading…
Cancel
Save