Browse Source

Simplify API

pull/1138/head
James Jackson-South 6 years ago
parent
commit
c69eb2bee0
  1. 2
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  2. 4
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  3. 30
      src/ImageSharp/Processing/Processors/Dithering/ErrorDither.cs
  4. 21
      src/ImageSharp/Processing/Processors/Dithering/IDither.cs
  5. 39
      src/ImageSharp/Processing/Processors/Dithering/IPaletteDitherImageProcessor{TPixel}.cs
  6. 77
      src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs
  7. 78
      src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
  8. 7
      src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs
  9. 34
      src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerExtensions.cs
  10. 6
      src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs
  11. 11
      src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs

2
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -360,7 +360,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = image.Height - 1; y >= 0; y--)
{
ReadOnlySpan<byte> pixelSpan = quantized.GetRowSpan(y);
ReadOnlySpan<byte> pixelSpan = quantized.GetPixelRowSpan(y);
stream.Write(pixelSpan);
for (int i = 0; i < this.padding; i++)

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

@ -380,7 +380,7 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.bitDepth < 8)
{
PngEncoderHelpers.ScaleDownFrom8BitArray(quantized.GetRowSpan(row), this.currentScanline.GetSpan(), this.bitDepth);
PngEncoderHelpers.ScaleDownFrom8BitArray(quantized.GetPixelRowSpan(row), this.currentScanline.GetSpan(), this.bitDepth);
}
else
{
@ -987,7 +987,7 @@ namespace SixLabors.ImageSharp.Formats.Png
row += Adam7.RowIncrement[pass])
{
// collect data
ReadOnlySpan<byte> srcRow = quantized.GetRowSpan(row);
ReadOnlySpan<byte> srcRow = quantized.GetPixelRowSpan(row);
for (int col = startCol, i = 0;
col < width;
col += Adam7.ColumnIncrement[pass])

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

@ -89,29 +89,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyQuantizationDither<TFrameQuantizer, TPixel>(
ref TFrameQuantizer quantizer,
ReadOnlyMemory<TPixel> palette,
ImageFrame<TPixel> source,
Memory<byte> output,
QuantizedFrame<TPixel> destination,
Rectangle bounds)
where TFrameQuantizer : struct, IFrameQuantizer<TPixel>
where TPixel : struct, IPixel<TPixel>
{
Span<byte> outputSpan = output.Span;
ReadOnlySpan<TPixel> paletteSpan = palette.Span;
int width = bounds.Width;
ReadOnlySpan<TPixel> paletteSpan = destination.Palette.Span;
int offsetY = bounds.Top;
int offsetX = bounds.Left;
float scale = quantizer.Options.DitherScale;
for (int y = bounds.Top; y < bounds.Bottom; y++)
{
Span<TPixel> row = source.GetPixelRowSpan(y);
int rowStart = (y - offsetY) * width;
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<byte> destinationRow = destination.GetPixelRowSpan(y - offsetY);
for (int x = bounds.Left; x < bounds.Right; x++)
{
TPixel sourcePixel = row[x];
outputSpan[rowStart + x - offsetX] = quantizer.GetQuantizedColor(sourcePixel, paletteSpan, out TPixel transformed);
TPixel sourcePixel = sourceRow[x];
destinationRow[x - offsetX] = quantizer.GetQuantizedColor(sourcePixel, paletteSpan, out TPixel transformed);
this.Dither(source, bounds, sourcePixel, transformed, x, y, scale);
}
}
@ -119,23 +116,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyPaletteDither<TPixel>(
Configuration configuration,
ReadOnlyMemory<TPixel> palette,
public void ApplyPaletteDither<TPaletteDitherImageProcessor, TPixel>(
in TPaletteDitherImageProcessor processor,
ImageFrame<TPixel> source,
Rectangle bounds,
float scale)
Rectangle bounds)
where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
var pixelMap = new EuclideanPixelMap<TPixel>(palette);
float scale = processor.DitherScale;
ReadOnlySpan<TPixel> palette = processor.Palette.Span;
for (int y = bounds.Top; y < bounds.Bottom; y++)
{
Span<TPixel> row = source.GetPixelRowSpan(y);
for (int x = bounds.Left; x < bounds.Right; x++)
{
TPixel sourcePixel = row[x];
pixelMap.GetClosestColor(sourcePixel, out TPixel transformed);
TPixel transformed = processor.GetPaletteColor(sourcePixel, palette);
this.Dither(source, bounds, sourcePixel, transformed, x, y, scale);
row[x] = transformed;
}

21
src/ImageSharp/Processing/Processors/Dithering/IDither.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -19,15 +18,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// <typeparam name="TFrameQuantizer">The type of frame quantizer.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="quantizer">The frame quantizer.</param>
/// <param name="palette">The quantized palette.</param>
/// <param name="source">The source image.</param>
/// <param name="output">The output target</param>
/// <param name="destination">The destination quantized frame.</param>
/// <param name="bounds">The region of interest bounds.</param>
void ApplyQuantizationDither<TFrameQuantizer, TPixel>(
ref TFrameQuantizer quantizer,
ReadOnlyMemory<TPixel> palette,
ImageFrame<TPixel> source,
Memory<byte> output,
QuantizedFrame<TPixel> destination,
Rectangle bounds)
where TFrameQuantizer : struct, IFrameQuantizer<TPixel>
where TPixel : struct, IPixel<TPixel>;
@ -36,18 +33,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// Transforms the image frame applying a dither matrix.
/// This method should be treated as destructive, altering the input pixels.
/// </summary>
/// <typeparam name="TPaletteDitherImageProcessor">The type of palette dithering processor.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="palette">The quantized palette.</param>
/// <param name="processor">The palette dithering processor.</param>
/// <param name="source">The source image.</param>
/// <param name="bounds">The region of interest bounds.</param>
/// <param name="scale">The dithering scale used to adjust the amount of dither. Range 0..1.</param>
void ApplyPaletteDither<TPixel>(
Configuration configuration,
ReadOnlyMemory<TPixel> palette,
void ApplyPaletteDither<TPaletteDitherImageProcessor, TPixel>(
in TPaletteDitherImageProcessor processor,
ImageFrame<TPixel> source,
Rectangle bounds,
float scale)
Rectangle bounds)
where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>;
}
}

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

@ -0,0 +1,39 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Dithering
{
/// <summary>
/// Implements an algorithm to alter the pixels of an image via palette dithering.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public interface IPaletteDitherImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets the configration instance to use when performing operations.
/// </summary>
public Configuration Configuration { get; }
/// <summary>
/// Gets the dithering palette.
/// </summary>
ReadOnlyMemory<TPixel> Palette { get; }
/// <summary>
/// Gets the dithering scale used to adjust the amount of dither. Range 0..1.
/// </summary>
float DitherScale { get; }
/// <summary>
/// Returns the color from the dithering palette corresponding to the given color.
/// </summary>
/// <param name="color">The color to match.</param>
/// <param name="palette">The output color palette.</param>
/// <returns>The <typeparamref name="TPixel"/> match.</returns>
TPixel GetPaletteColor(TPixel color, ReadOnlySpan<TPixel> palette);
}
}

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

@ -105,9 +105,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyQuantizationDither<TFrameQuantizer, TPixel>(
ref TFrameQuantizer quantizer,
ReadOnlyMemory<TPixel> palette,
ImageFrame<TPixel> source,
Memory<byte> output,
QuantizedFrame<TPixel> destination,
Rectangle bounds)
where TFrameQuantizer : struct, IFrameQuantizer<TPixel>
where TPixel : struct, IPixel<TPixel>
@ -116,10 +115,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
ref quantizer,
in Unsafe.AsRef(this),
source,
output,
bounds,
palette,
ImageMaths.GetBitsNeededForColorDepth(palette.Span.Length));
destination,
bounds);
ParallelRowIterator.IterateRows(
quantizer.Configuration,
@ -129,24 +126,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyPaletteDither<TPixel>(
Configuration configuration,
ReadOnlyMemory<TPixel> palette,
public void ApplyPaletteDither<TPaletteDitherImageProcessor, TPixel>(
in TPaletteDitherImageProcessor processor,
ImageFrame<TPixel> source,
Rectangle bounds,
float scale)
Rectangle bounds)
where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
var ditherOperation = new PaletteDitherRowIntervalOperation<TPixel>(
var ditherOperation = new PaletteDitherRowIntervalOperation<TPaletteDitherImageProcessor, TPixel>(
in processor,
in Unsafe.AsRef(this),
source,
bounds,
palette,
scale,
ImageMaths.GetBitsNeededForColorDepth(palette.Span.Length));
bounds);
ParallelRowIterator.IterateRows(
configuration,
processor.Configuration,
bounds,
in ditherOperation);
}
@ -207,7 +201,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
private readonly TFrameQuantizer quantizer;
private readonly OrderedDither dither;
private readonly ImageFrame<TPixel> source;
private readonly Memory<byte> output;
private readonly QuantizedFrame<TPixel> destination;
private readonly Rectangle bounds;
private readonly ReadOnlyMemory<TPixel> palette;
private readonly int bitDepth;
@ -217,84 +211,79 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
ref TFrameQuantizer quantizer,
in OrderedDither dither,
ImageFrame<TPixel> source,
Memory<byte> output,
Rectangle bounds,
ReadOnlyMemory<TPixel> palette,
int bitDepth)
QuantizedFrame<TPixel> destination,
Rectangle bounds)
{
this.quantizer = quantizer;
this.dither = dither;
this.source = source;
this.output = output;
this.destination = destination;
this.bounds = bounds;
this.palette = palette;
this.bitDepth = bitDepth;
this.palette = destination.Palette;
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(destination.Palette.Span.Length);
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
ReadOnlySpan<TPixel> paletteSpan = this.palette.Span;
Span<byte> outputSpan = this.output.Span;
int width = this.bounds.Width;
int offsetY = this.bounds.Top;
int offsetX = this.bounds.Left;
float scale = this.quantizer.Options.DitherScale;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> row = this.source.GetPixelRowSpan(y);
int rowStart = (y - offsetY) * width;
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y);
Span<byte> destinationRow = this.destination.GetPixelRowSpan(y - offsetY);
// TODO: This can be a bulk operation.
for (int x = this.bounds.Left; x < this.bounds.Right; x++)
{
TPixel dithered = this.dither.Dither(row[x], x, y, this.bitDepth, scale);
outputSpan[rowStart + x - offsetX] = this.quantizer.GetQuantizedColor(dithered, paletteSpan, out TPixel _);
TPixel dithered = this.dither.Dither(sourceRow[x], x, y, this.bitDepth, scale);
destinationRow[x - offsetX] = this.quantizer.GetQuantizedColor(dithered, paletteSpan, out TPixel _);
}
}
}
}
private readonly struct PaletteDitherRowIntervalOperation<TPixel> : IRowIntervalOperation
private readonly struct PaletteDitherRowIntervalOperation<TPaletteDitherImageProcessor, TPixel> : IRowIntervalOperation
where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly TPaletteDitherImageProcessor processor;
private readonly OrderedDither dither;
private readonly ImageFrame<TPixel> source;
private readonly Rectangle bounds;
private readonly EuclideanPixelMap<TPixel> pixelMap;
private readonly float scale;
private readonly int bitDepth;
[MethodImpl(InliningOptions.ShortMethod)]
public PaletteDitherRowIntervalOperation(
in TPaletteDitherImageProcessor processor,
in OrderedDither dither,
ImageFrame<TPixel> source,
Rectangle bounds,
ReadOnlyMemory<TPixel> palette,
float scale,
int bitDepth)
Rectangle bounds)
{
this.processor = processor;
this.dither = dither;
this.source = source;
this.bounds = bounds;
this.pixelMap = new EuclideanPixelMap<TPixel>(palette);
this.scale = scale;
this.bitDepth = bitDepth;
this.scale = processor.DitherScale;
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(processor.Palette.Span.Length);
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
ReadOnlySpan<TPixel> paletteSpan = this.processor.Palette.Span;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> row = this.source.GetPixelRowSpan(y);
for (int x = this.bounds.Left; x < this.bounds.Right; x++)
{
TPixel dithered = this.dither.Dither(row[x], x, y, this.bitDepth, this.scale);
this.pixelMap.GetClosestColor(dithered, out TPixel transformed);
row[x] = transformed;
ref TPixel sourcePixel = ref row[x];
TPixel dithered = this.dither.Dither(sourcePixel, x, y, this.bitDepth, this.scale);
sourcePixel = this.processor.GetPaletteColor(dithered, paletteSpan);
}
}
}

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

@ -3,7 +3,9 @@
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Processing.Processors.Dithering
{
@ -14,11 +16,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
internal sealed class PaletteDitherProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly int paletteLength;
private readonly DitherProcessor ditherProcessor;
private readonly IDither dither;
private readonly float ditherScale;
private readonly ReadOnlyMemory<Color> sourcePalette;
private IMemoryOwner<TPixel> palette;
private IMemoryOwner<TPixel> paletteMemory;
private bool isDisposed;
/// <summary>
@ -31,37 +31,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
public PaletteDitherProcessor(Configuration configuration, PaletteDitherProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
this.paletteLength = definition.Palette.Span.Length;
this.dither = definition.Dither;
this.ditherScale = definition.DitherScale;
this.sourcePalette = definition.Palette;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
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.dither.ApplyPaletteDither(
this.ditherProcessor = new DitherProcessor(
this.Configuration,
this.palette.Memory,
source,
interest,
this.ditherScale);
this.paletteMemory.Memory,
definition.DitherScale);
}
/// <inheritdoc/>
protected override void BeforeFrameApply(ImageFrame<TPixel> source)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
// Lazy init palettes:
if (this.palette is null)
{
this.palette = this.Configuration.MemoryAllocator.Allocate<TPixel>(this.paletteLength);
ReadOnlySpan<Color> sourcePalette = this.sourcePalette.Span;
Color.ToPixel(this.Configuration, sourcePalette, this.palette.Memory.Span);
}
base.BeforeFrameApply(source);
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
this.dither.ApplyPaletteDither(in this.ditherProcessor, source, interest);
}
/// <inheritdoc/>
@ -74,13 +60,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
if (disposing)
{
this.palette?.Dispose();
this.paletteMemory?.Dispose();
}
this.palette = null;
this.paletteMemory = null;
this.isDisposed = true;
base.Dispose(disposing);
}
/// <summary>
/// Used to allow inlining of calls to
/// <see cref="IPaletteDitherImageProcessor{TPixel}.GetPaletteColor(TPixel, ReadOnlySpan{TPixel})"/>.
/// </summary>
private readonly struct DitherProcessor : IPaletteDitherImageProcessor<TPixel>
{
private readonly EuclideanPixelMap<TPixel> pixelMap;
[MethodImpl(InliningOptions.ShortMethod)]
public DitherProcessor(
Configuration configuration,
ReadOnlyMemory<TPixel> palette,
float ditherScale)
{
this.Configuration = configuration;
this.pixelMap = new EuclideanPixelMap<TPixel>(palette);
this.Palette = palette;
this.DitherScale = ditherScale;
}
public Configuration Configuration { get; }
public ReadOnlyMemory<TPixel> Palette { get; }
public float DitherScale { get; }
[MethodImpl(InliningOptions.ShortMethod)]
public TPixel GetPaletteColor(TPixel color, ReadOnlySpan<TPixel> palette)
{
this.pixelMap.GetClosestColor(color, out TPixel match);
return match;
}
}
}
}

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

@ -24,6 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Initializes a new instance of the <see cref="EuclideanPixelMap{TPixel}"/> struct.
/// </summary>
/// <param name="palette">The color palette to map from.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public EuclideanPixelMap(ReadOnlyMemory<TPixel> palette)
{
Guard.MustBeGreaterThan(palette.Length, 0, nameof(palette));
@ -40,7 +41,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
/// <inheritdoc/>
public ReadOnlyMemory<TPixel> Palette { get; }
public ReadOnlyMemory<TPixel> Palette
{
[MethodImpl(InliningOptions.ShortMethod)]
get;
}
/// <inheritdoc/>
public override bool Equals(object obj)

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

@ -41,18 +41,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
MemoryAllocator memoryAllocator = quantizer.Configuration.MemoryAllocator;
var quantizedFrame = new QuantizedFrame<TPixel>(memoryAllocator, interest.Width, interest.Height, palette);
Memory<byte> output = quantizedFrame.GetWritablePixelMemory();
if (quantizer.Options.Dither is null)
{
SecondPass(ref quantizer, source, interest, output, palette);
SecondPass(ref quantizer, source, quantizedFrame, interest);
}
else
{
// We clone the image as we don't want to alter the original via error diffusion based dithering.
using (ImageFrame<TPixel> clone = source.Clone())
{
SecondPass(ref quantizer, clone, interest, output, palette);
SecondPass(ref quantizer, clone, quantizedFrame, interest);
}
}
@ -63,9 +62,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
private static void SecondPass<TFrameQuantizer, TPixel>(
ref TFrameQuantizer quantizer,
ImageFrame<TPixel> source,
Rectangle bounds,
Memory<byte> output,
ReadOnlyMemory<TPixel> palette)
QuantizedFrame<TPixel> destination,
Rectangle bounds)
where TFrameQuantizer : struct, IFrameQuantizer<TPixel>
where TPixel : struct, IPixel<TPixel>
{
@ -73,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
if (dither is null)
{
var operation = new RowIntervalOperation<TFrameQuantizer, TPixel>(quantizer, source, output, bounds, palette);
var operation = new RowIntervalOperation<TFrameQuantizer, TPixel>(quantizer, source, destination, bounds);
ParallelRowIterator.IterateRows(
quantizer.Configuration,
bounds,
@ -82,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
return;
}
dither.ApplyQuantizationDither(ref quantizer, palette, source, output, bounds);
dither.ApplyQuantizationDither(ref quantizer, source, destination, bounds);
}
private readonly struct RowIntervalOperation<TFrameQuantizer, TPixel> : IRowIntervalOperation
@ -91,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
private readonly TFrameQuantizer quantizer;
private readonly ImageFrame<TPixel> source;
private readonly Memory<byte> output;
private readonly QuantizedFrame<TPixel> destination;
private readonly Rectangle bounds;
private readonly ReadOnlyMemory<TPixel> palette;
@ -99,35 +97,31 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public RowIntervalOperation(
in TFrameQuantizer quantizer,
ImageFrame<TPixel> source,
Memory<byte> output,
Rectangle bounds,
ReadOnlyMemory<TPixel> palette)
QuantizedFrame<TPixel> destination,
Rectangle bounds)
{
this.quantizer = quantizer;
this.source = source;
this.output = output;
this.destination = destination;
this.bounds = bounds;
this.palette = palette;
this.palette = destination.Palette;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
ReadOnlySpan<TPixel> paletteSpan = this.palette.Span;
Span<byte> outputSpan = this.output.Span;
int width = this.bounds.Width;
int offsetY = this.bounds.Top;
int offsetX = this.bounds.Left;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> row = this.source.GetPixelRowSpan(y);
int rowStart = (y - offsetY) * width;
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y);
Span<byte> destinationRow = this.destination.GetPixelRowSpan(y - offsetY);
// TODO: This can be a bulk operation.
for (int x = this.bounds.Left; x < this.bounds.Right; x++)
{
outputSpan[rowStart + x - offsetX] = this.quantizer.GetQuantizedColor(row[x], paletteSpan, out TPixel _);
destinationRow[x - offsetX] = this.quantizer.GetQuantizedColor(sourceRow[x], paletteSpan, out TPixel _);
}
}
}

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

@ -31,9 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <returns>
/// A <see cref="QuantizedFrame{TPixel}"/> representing a quantized version of the source frame pixels.
/// </returns>
QuantizedFrame<TPixel> QuantizeFrame(
ImageFrame<TPixel> source,
Rectangle bounds);
QuantizedFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds);
/// <summary>
/// Builds the quantized palette from the given image frame and bounds.
@ -44,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
ReadOnlyMemory<TPixel> BuildPalette(ImageFrame<TPixel> source, Rectangle bounds);
/// <summary>
/// Returns the index and color from the quantized palette corresponding to the give to the given color.
/// Returns the index and color from the quantized palette corresponding to the given color.
/// </summary>
/// <param name="color">The color to match.</param>
/// <param name="palette">The output color palette.</param>

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

@ -57,16 +57,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
public ReadOnlySpan<byte> GetPixelSpan() => this.pixels.GetSpan();
public Span<byte> GetPixelSpan() => this.pixels.GetSpan();
/// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the the first pixel on that row.
/// </summary>
/// <param name="rowIndex">The row.</param>
/// <returns>The pixel row as a <see cref="ReadOnlySpan{T}"/>.</returns>
/// <returns>The pixel row as a <see cref="Span{T}"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public ReadOnlySpan<byte> GetRowSpan(int rowIndex)
public Span<byte> GetPixelRowSpan(int rowIndex)
=> this.GetPixelSpan().Slice(rowIndex * this.Width, this.Width);
/// <inheritdoc/>
@ -82,10 +82,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.pixels = null;
this.Palette = null;
}
/// <summary>
/// Get the non-readonly memory of pixel data so <see cref="IFrameQuantizer{TPixel}"/> can fill it.
/// </summary>
internal Memory<byte> GetWritablePixelMemory() => this.pixels.Memory;
}
}

Loading…
Cancel
Save