Browse Source

Introduce palette property

pull/1574/head
James Jackson-South 6 years ago
parent
commit
2cc1747e0b
  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
{
int stride = this.currentScanline.Length();
quantized.GetPixelBufferSpan().Slice(row * stride, stride).CopyTo(this.currentScanline.GetSpan());
quantized.GetPixelRowSpan(row).CopyTo(this.currentScanline.GetSpan());
}
break;

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

@ -4,7 +4,6 @@
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -19,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
{
private readonly DitherProcessor ditherProcessor;
private readonly IDither dither;
private IMemoryOwner<TPixel> paletteMemory;
private IMemoryOwner<TPixel> paletteOwner;
private bool isDisposed;
/// <summary>
@ -35,12 +34,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
this.dither = definition.Dither;
ReadOnlySpan<Color> sourcePalette = definition.Palette.Span;
this.paletteMemory = this.Configuration.MemoryAllocator.Allocate<TPixel>(sourcePalette.Length);
Color.ToPixel(this.Configuration, sourcePalette, this.paletteMemory.Memory.Span);
this.paletteOwner = this.Configuration.MemoryAllocator.Allocate<TPixel>(sourcePalette.Length);
Color.ToPixel(this.Configuration, sourcePalette, this.paletteOwner.Memory.Span);
this.ditherProcessor = new DitherProcessor(
this.Configuration,
this.paletteMemory.Memory,
this.paletteOwner.Memory,
definition.DitherScale);
}
@ -59,14 +58,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
return;
}
this.isDisposed = true;
if (disposing)
{
this.paletteMemory?.Dispose();
this.paletteOwner.Dispose();
}
this.paletteMemory = null;
this.isDisposed = true;
this.paletteOwner = null;
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
{
/// <summary>
/// Contains extension methods for frame quantizers.
/// Contains utility methods for <see cref="IFrameQuantizer{TPixel}"/> instances.
/// </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>
/// Quantizes an image frame and return the resulting output pixels.
/// </summary>
@ -37,8 +55,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
var interest = Rectangle.Intersect(source.Bounds(), bounds);
// Collect the palette. Required before the second pass runs.
ReadOnlyMemory<TPixel> palette = quantizer.BuildPalette(source, interest);
var destination = new IndexedImageFrame<TPixel>(quantizer.Configuration, interest.Width, interest.Height, palette);
quantizer.BuildPalette(source, interest);
var destination = new IndexedImageFrame<TPixel>(
quantizer.Configuration,
interest.Width,
interest.Height,
quantizer.Palette);
if (quantizer.Options.Dither is null)
{
@ -67,7 +90,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
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(
quantizer.Configuration,
bounds,

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

@ -23,13 +23,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
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>
/// Builds the quantized palette from the given image frame and bounds.
/// </summary>
/// <param name="source">The source image frame.</param>
/// <param name="bounds">The region of interest bounds.</param>
/// <returns>The <see cref="ReadOnlyMemory{TPixel}"/> palette.</returns>
ReadOnlyMemory<TPixel> BuildPalette(ImageFrame<TPixel> source, Rectangle bounds);
void BuildPalette(ImageFrame<TPixel> source, Rectangle bounds);
/// <summary>
/// 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 Octree octree;
private IMemoryOwner<TPixel> paletteOwner;
private ReadOnlyMemory<TPixel> palette;
private EuclideanPixelMap<TPixel> pixelMap;
private readonly bool isDithering;
private bool isDisposed;
@ -44,6 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.maxColors = this.Options.MaxColors;
this.octree = new Octree(ImageMaths.GetBitsNeededForColorDepth(this.maxColors).Clamp(1, 8));
this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(this.maxColors, AllocationOptions.Clean);
this.palette = default;
this.pixelMap = default;
this.isDithering = !(this.Options.Dither is null);
this.isDisposed = false;
@ -56,13 +58,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public QuantizerOptions Options { get; }
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds)
=> FrameQuantizerExtensions.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds);
public ReadOnlyMemory<TPixel> Palette
{
get
{
FrameQuantizerUtilities.CheckPaletteState(in this.palette);
return this.palette;
}
}
/// <inheritdoc/>
[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);
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));
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/>
[MethodImpl(InliningOptions.ShortMethod)]
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/>
public QuantizerOptions Options { get; }
/// <inheritdoc/>
public ReadOnlyMemory<TPixel> Palette => this.pixelMap.Palette;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
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/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly ReadOnlyMemory<TPixel> BuildPalette(ImageFrame<TPixel> source, Rectangle bounds)
=> this.pixelMap.Palette;
public void BuildPalette(ImageFrame<TPixel> source, Rectangle bounds)
{
}
/// <inheritdoc/>
[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<byte> tagsOwner;
private IMemoryOwner<TPixel> paletteOwner;
private ReadOnlyMemory<TPixel> palette;
private int maxColors;
private readonly Box[] colorCube;
private EuclideanPixelMap<TPixel> pixelMap;
@ -93,6 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.momentsOwner = this.memoryAllocator.Allocate<Moment>(TableLength, AllocationOptions.Clean);
this.tagsOwner = this.memoryAllocator.Allocate<byte>(TableLength, AllocationOptions.Clean);
this.paletteOwner = this.memoryAllocator.Allocate<TPixel>(this.maxColors, AllocationOptions.Clean);
this.palette = default;
this.colorCube = new Box[this.maxColors];
this.isDisposed = false;
this.pixelMap = default;
@ -106,12 +108,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public QuantizerOptions Options { get; }
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds)
=> FrameQuantizerExtensions.QuantizeFrame(ref Unsafe.AsRef(this), source, bounds);
public ReadOnlyMemory<TPixel> Palette
{
get
{
FrameQuantizerUtilities.CheckPaletteState(in this.palette);
return this.palette;
}
}
/// <inheritdoc/>
public ReadOnlyMemory<TPixel> BuildPalette(ImageFrame<TPixel> source, Rectangle bounds)
public void BuildPalette(ImageFrame<TPixel> source, Rectangle bounds)
{
this.Build3DHistogram(source, bounds);
this.Get3DMoments(this.memoryAllocator);
@ -134,9 +141,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
ReadOnlyMemory<TPixel> result = this.paletteOwner.Memory.Slice(0, this.maxColors);
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/>
public readonly byte GetQuantizedColor(TPixel color, out TPixel match)
{

Loading…
Cancel
Save