|
|
|
@ -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); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|