Browse Source

Introduce palette property

pull/1138/head
James Jackson-South 6 years ago
parent
commit
89c5fe249f
  1. 3
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  2. 16
      src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
  3. 38
      src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerUtilities.cs
  4. 11
      src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs
  5. 22
      src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs
  6. 10
      src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs
  7. 22
      src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs

3
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -384,8 +384,7 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
else else
{ {
int stride = this.currentScanline.Length(); quantized.GetPixelRowSpan(row).CopyTo(this.currentScanline.GetSpan());
quantized.GetPixelBufferSpan().Slice(row * stride, stride).CopyTo(this.currentScanline.GetSpan());
} }
break; break;

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

@ -4,7 +4,6 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -19,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
{ {
private readonly DitherProcessor ditherProcessor; private readonly DitherProcessor ditherProcessor;
private readonly IDither dither; private readonly IDither dither;
private IMemoryOwner<TPixel> paletteMemory; private IMemoryOwner<TPixel> paletteOwner;
private bool isDisposed; private bool isDisposed;
/// <summary> /// <summary>
@ -35,12 +34,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
this.dither = definition.Dither; this.dither = definition.Dither;
ReadOnlySpan<Color> sourcePalette = definition.Palette.Span; ReadOnlySpan<Color> sourcePalette = definition.Palette.Span;
this.paletteMemory = this.Configuration.MemoryAllocator.Allocate<TPixel>(sourcePalette.Length); this.paletteOwner = this.Configuration.MemoryAllocator.Allocate<TPixel>(sourcePalette.Length);
Color.ToPixel(this.Configuration, sourcePalette, this.paletteMemory.Memory.Span); Color.ToPixel(this.Configuration, sourcePalette, this.paletteOwner.Memory.Span);
this.ditherProcessor = new DitherProcessor( this.ditherProcessor = new DitherProcessor(
this.Configuration, this.Configuration,
this.paletteMemory.Memory, this.paletteOwner.Memory,
definition.DitherScale); definition.DitherScale);
} }
@ -59,14 +58,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
return; return;
} }
this.isDisposed = true;
if (disposing) if (disposing)
{ {
this.paletteMemory?.Dispose(); this.paletteOwner.Dispose();
} }
this.paletteMemory = null; this.paletteOwner = null;
this.isDisposed = true;
base.Dispose(disposing); base.Dispose(disposing);
} }

38
src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerExtensions.cs → src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerUtilities.cs

@ -11,10 +11,28 @@ using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{ {
/// <summary> /// <summary>
/// Contains extension methods for frame quantizers. /// Contains utility methods for <see cref="IFrameQuantizer{TPixel}"/> instances.
/// </summary> /// </summary>
public static class FrameQuantizerExtensions public static class FrameQuantizerUtilities
{ {
/// <summary>
/// Helper method for throwing an exception when a frame quantizer palette has
/// been requested but not built yet.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="palette">The frame quantizer palette.</param>
/// <exception cref="InvalidOperationException">
/// The palette has not been built via <see cref="IFrameQuantizer{TPixel}.BuildPalette(ImageFrame{TPixel}, Rectangle)"/>
/// </exception>
public static void CheckPaletteState<TPixel>(in ReadOnlyMemory<TPixel> palette)
where TPixel : unmanaged, IPixel<TPixel>
{
if (palette.Equals(default))
{
throw new InvalidOperationException("Frame Quantizer palette has not been built.");
}
}
/// <summary> /// <summary>
/// Quantizes an image frame and return the resulting output pixels. /// Quantizes an image frame and return the resulting output pixels.
/// </summary> /// </summary>
@ -37,8 +55,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
var interest = Rectangle.Intersect(source.Bounds(), bounds); var interest = Rectangle.Intersect(source.Bounds(), bounds);
// Collect the palette. Required before the second pass runs. // Collect the palette. Required before the second pass runs.
ReadOnlyMemory<TPixel> palette = quantizer.BuildPalette(source, interest); quantizer.BuildPalette(source, interest);
var destination = new IndexedImageFrame<TPixel>(quantizer.Configuration, interest.Width, interest.Height, palette);
var destination = new IndexedImageFrame<TPixel>(
quantizer.Configuration,
interest.Width,
interest.Height,
quantizer.Palette);
if (quantizer.Options.Dither is null) if (quantizer.Options.Dither is null)
{ {
@ -67,7 +90,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
if (dither is null) if (dither is null)
{ {
var operation = new RowIntervalOperation<TFrameQuantizer, TPixel>(ref quantizer, source, destination, bounds); var operation = new RowIntervalOperation<TFrameQuantizer, TPixel>(
ref quantizer,
source,
destination,
bounds);
ParallelRowIterator.IterateRowIntervals( ParallelRowIterator.IterateRowIntervals(
quantizer.Configuration, quantizer.Configuration,
bounds, bounds,

11
src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs

@ -23,13 +23,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary> /// </summary>
QuantizerOptions Options { get; } QuantizerOptions Options { get; }
/// <summary>
/// Gets the quantized color palette.
/// </summary>
/// <exception cref="InvalidOperationException">
/// The palette has not been built via <see cref="BuildPalette(ImageFrame{TPixel}, Rectangle)"/>.
/// </exception>
ReadOnlyMemory<TPixel> Palette { get; }
/// <summary> /// <summary>
/// Builds the quantized palette from the given image frame and bounds. /// Builds the quantized palette from the given image frame and bounds.
/// </summary> /// </summary>
/// <param name="source">The source image frame.</param> /// <param name="source">The source image frame.</param>
/// <param name="bounds">The region of interest bounds.</param> /// <param name="bounds">The region of interest bounds.</param>
/// <returns>The <see cref="ReadOnlyMemory{TPixel}"/> palette.</returns> void BuildPalette(ImageFrame<TPixel> source, Rectangle bounds);
ReadOnlyMemory<TPixel> BuildPalette(ImageFrame<TPixel> source, Rectangle bounds);
/// <summary> /// <summary>
/// Quantizes an image frame and return the resulting output pixels. /// Quantizes an image frame and return the resulting output pixels.

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

@ -23,6 +23,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
private readonly int maxColors; private readonly int maxColors;
private readonly Octree octree; private readonly Octree octree;
private IMemoryOwner<TPixel> paletteOwner; private IMemoryOwner<TPixel> paletteOwner;
private ReadOnlyMemory<TPixel> palette;
private EuclideanPixelMap<TPixel> pixelMap; private EuclideanPixelMap<TPixel> pixelMap;
private readonly bool isDithering; private readonly bool isDithering;
private bool isDisposed; private bool isDisposed;
@ -44,6 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.maxColors = this.Options.MaxColors; this.maxColors = this.Options.MaxColors;
this.octree = new Octree(ImageMaths.GetBitsNeededForColorDepth(this.maxColors).Clamp(1, 8)); this.octree = new Octree(ImageMaths.GetBitsNeededForColorDepth(this.maxColors).Clamp(1, 8));
this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(this.maxColors, AllocationOptions.Clean); this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(this.maxColors, AllocationOptions.Clean);
this.palette = default;
this.pixelMap = default; this.pixelMap = default;
this.isDithering = !(this.Options.Dither is null); this.isDithering = !(this.Options.Dither is null);
this.isDisposed = false; this.isDisposed = false;
@ -56,13 +58,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public QuantizerOptions Options { get; } public QuantizerOptions Options { get; }
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] public ReadOnlyMemory<TPixel> Palette
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds) {
=> FrameQuantizerExtensions.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds); get
{
FrameQuantizerUtilities.CheckPaletteState(in this.palette);
return this.palette;
}
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public ReadOnlyMemory<TPixel> BuildPalette(ImageFrame<TPixel> source, Rectangle bounds) public void BuildPalette(ImageFrame<TPixel> source, Rectangle bounds)
{ {
using IMemoryOwner<Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate<Rgba32>(bounds.Width); using IMemoryOwner<Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate<Rgba32>(bounds.Width);
Span<Rgba32> bufferSpan = buffer.GetSpan(); Span<Rgba32> bufferSpan = buffer.GetSpan();
@ -90,9 +97,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
ReadOnlyMemory<TPixel> result = this.paletteOwner.Memory.Slice(0, Math.Min(paletteIndex + 2, QuantizerConstants.MaxColors)); ReadOnlyMemory<TPixel> result = this.paletteOwner.Memory.Slice(0, Math.Min(paletteIndex + 2, QuantizerConstants.MaxColors));
this.pixelMap = new EuclideanPixelMap<TPixel>(this.Configuration, result); this.pixelMap = new EuclideanPixelMap<TPixel>(this.Configuration, result);
return result; this.palette = result;
} }
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds)
=> FrameQuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds);
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public readonly byte GetQuantizedColor(TPixel color, out TPixel match) public readonly byte GetQuantizedColor(TPixel color, out TPixel match)

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

@ -43,15 +43,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <inheritdoc/> /// <inheritdoc/>
public QuantizerOptions Options { get; } public QuantizerOptions Options { get; }
/// <inheritdoc/>
public ReadOnlyMemory<TPixel> Palette => this.pixelMap.Palette;
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds) public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds)
=> FrameQuantizerExtensions.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds); => FrameQuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds);
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public readonly ReadOnlyMemory<TPixel> BuildPalette(ImageFrame<TPixel> source, Rectangle bounds) public void BuildPalette(ImageFrame<TPixel> source, Rectangle bounds)
=> this.pixelMap.Palette; {
}
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

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

@ -69,6 +69,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
private IMemoryOwner<Moment> momentsOwner; private IMemoryOwner<Moment> momentsOwner;
private IMemoryOwner<byte> tagsOwner; private IMemoryOwner<byte> tagsOwner;
private IMemoryOwner<TPixel> paletteOwner; private IMemoryOwner<TPixel> paletteOwner;
private ReadOnlyMemory<TPixel> palette;
private int maxColors; private int maxColors;
private readonly Box[] colorCube; private readonly Box[] colorCube;
private EuclideanPixelMap<TPixel> pixelMap; private EuclideanPixelMap<TPixel> pixelMap;
@ -93,6 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.momentsOwner = this.memoryAllocator.Allocate<Moment>(TableLength, AllocationOptions.Clean); this.momentsOwner = this.memoryAllocator.Allocate<Moment>(TableLength, AllocationOptions.Clean);
this.tagsOwner = this.memoryAllocator.Allocate<byte>(TableLength, AllocationOptions.Clean); this.tagsOwner = this.memoryAllocator.Allocate<byte>(TableLength, AllocationOptions.Clean);
this.paletteOwner = this.memoryAllocator.Allocate<TPixel>(this.maxColors, AllocationOptions.Clean); this.paletteOwner = this.memoryAllocator.Allocate<TPixel>(this.maxColors, AllocationOptions.Clean);
this.palette = default;
this.colorCube = new Box[this.maxColors]; this.colorCube = new Box[this.maxColors];
this.isDisposed = false; this.isDisposed = false;
this.pixelMap = default; this.pixelMap = default;
@ -106,12 +108,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public QuantizerOptions Options { get; } public QuantizerOptions Options { get; }
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] public ReadOnlyMemory<TPixel> Palette
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds) {
=> FrameQuantizerExtensions.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds); get
{
FrameQuantizerUtilities.CheckPaletteState(in this.palette);
return this.palette;
}
}
/// <inheritdoc/> /// <inheritdoc/>
public ReadOnlyMemory<TPixel> BuildPalette(ImageFrame<TPixel> source, Rectangle bounds) public void BuildPalette(ImageFrame<TPixel> source, Rectangle bounds)
{ {
this.Build3DHistogram(source, bounds); this.Build3DHistogram(source, bounds);
this.Get3DMoments(this.memoryAllocator); this.Get3DMoments(this.memoryAllocator);
@ -134,9 +141,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
ReadOnlyMemory<TPixel> result = this.paletteOwner.Memory.Slice(0, this.maxColors); ReadOnlyMemory<TPixel> result = this.paletteOwner.Memory.Slice(0, this.maxColors);
this.pixelMap = new EuclideanPixelMap<TPixel>(this.Configuration, result); this.pixelMap = new EuclideanPixelMap<TPixel>(this.Configuration, result);
return result; this.palette = result;
} }
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds)
=> FrameQuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds);
/// <inheritdoc/> /// <inheritdoc/>
public readonly byte GetQuantizedColor(TPixel color, out TPixel match) public readonly byte GetQuantizedColor(TPixel color, out TPixel match)
{ {

Loading…
Cancel
Save