Skip to content

Commit c5cc7a7

Browse files
Merge pull request #2582 from SixLabors/js/fix-quantizer-in-box
Fix boxed quantization and update refs
2 parents afe2133 + 8d7ac6e commit c5cc7a7

File tree

29 files changed

+89
-114
lines changed

29 files changed

+89
-114
lines changed

src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public OctreeQuantizer(Configuration configuration, QuantizerOptions options)
6060
public QuantizerOptions Options { get; }
6161

6262
/// <inheritdoc/>
63-
public ReadOnlyMemory<TPixel> Palette
63+
public readonly ReadOnlyMemory<TPixel> Palette
6464
{
6565
get
6666
{
@@ -72,16 +72,14 @@ public ReadOnlyMemory<TPixel> Palette
7272
/// <inheritdoc/>
7373
public void AddPaletteColors(Buffer2DRegion<TPixel> pixelRegion)
7474
{
75-
Rectangle bounds = pixelRegion.Rectangle;
76-
Buffer2D<TPixel> source = pixelRegion.Buffer;
77-
using (IMemoryOwner<Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate<Rgba32>(bounds.Width))
75+
using (IMemoryOwner<Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate<Rgba32>(pixelRegion.Width))
7876
{
7977
Span<Rgba32> bufferSpan = buffer.GetSpan();
8078

8179
// Loop through each row
82-
for (int y = bounds.Top; y < bounds.Bottom; y++)
80+
for (int y = 0; y < pixelRegion.Height; y++)
8381
{
84-
Span<TPixel> row = source.DangerousGetRowSpan(y).Slice(bounds.Left, bounds.Width);
82+
Span<TPixel> row = pixelRegion.DangerousGetRowSpan(y);
8583
PixelOperations<TPixel>.Instance.ToRgba32(this.Configuration, row, bufferSpan);
8684

8785
for (int x = 0; x < bufferSpan.Length; x++)

src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public QuantizeProcessor(Configuration configuration, IQuantizer quantizer, Imag
3232
/// <inheritdoc />
3333
protected override void OnFrameApply(ImageFrame<TPixel> source)
3434
{
35-
var interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle);
35+
Rectangle interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle);
3636

3737
Configuration configuration = this.Configuration;
3838
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(configuration);
@@ -43,14 +43,14 @@ protected override void OnFrameApply(ImageFrame<TPixel> source)
4343
int offsetX = interest.Left;
4444
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
4545

46-
for (int y = interest.Y; y < interest.Height; y++)
46+
for (int y = 0; y < quantized.Height; y++)
4747
{
48-
Span<TPixel> row = sourceBuffer.DangerousGetRowSpan(y);
49-
ReadOnlySpan<byte> quantizedRow = quantized.DangerousGetRowSpan(y - offsetY);
48+
ReadOnlySpan<byte> quantizedRow = quantized.DangerousGetRowSpan(y);
49+
Span<TPixel> row = sourceBuffer.DangerousGetRowSpan(y + offsetY);
5050

51-
for (int x = interest.Left; x < interest.Right; x++)
51+
for (int x = 0; x < quantized.Width; x++)
5252
{
53-
row[x] = paletteSpan[quantizedRow[x - offsetX]];
53+
row[x + offsetX] = paletteSpan[quantizedRow[x]];
5454
}
5555
}
5656
}

src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,14 @@ private static void SecondPass<TFrameQuantizer, TPixel>(
154154
int offsetY = bounds.Top;
155155
int offsetX = bounds.Left;
156156

157-
for (int y = bounds.Y; y < bounds.Height; y++)
157+
for (int y = 0; y < destination.Height; y++)
158158
{
159-
Span<TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y);
160-
Span<byte> destinationRow = destination.GetWritablePixelRowSpanUnsafe(y - offsetY);
159+
Span<TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y + offsetY);
160+
Span<byte> destinationRow = destination.GetWritablePixelRowSpanUnsafe(y);
161161

162-
for (int x = bounds.Left; x < bounds.Right; x++)
162+
for (int x = 0; x < destination.Width; x++)
163163
{
164-
destinationRow[x - offsetX] = Unsafe.AsRef(quantizer).GetQuantizedColor(sourceRow[x], out TPixel _);
164+
destinationRow[x] = Unsafe.AsRef(quantizer).GetQuantizedColor(sourceRow[x + offsetX], out TPixel _);
165165
}
166166
}
167167

src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,11 @@ public readonly ReadOnlyMemory<TPixel> Palette
123123
/// <inheritdoc/>
124124
public void AddPaletteColors(Buffer2DRegion<TPixel> pixelRegion)
125125
{
126-
Rectangle bounds = pixelRegion.Rectangle;
127-
Buffer2D<TPixel> source = pixelRegion.Buffer;
128-
129-
this.Build3DHistogram(source, bounds);
126+
// TODO: Something is destroying the existing palette when adding new colors.
127+
// When the QuantizingImageEncoder.PixelSamplingStrategy is DefaultPixelSamplingStrategy
128+
// this leads to performance issues + the palette is not preserved.
129+
// https://github.com/SixLabors/ImageSharp/issues/2498
130+
this.Build3DHistogram(pixelRegion);
130131
this.Get3DMoments(this.memoryAllocator);
131132
this.BuildCube();
132133

@@ -360,19 +361,18 @@ private static Moment Top(ref Box cube, int direction, int position, ReadOnlySpa
360361
/// <summary>
361362
/// Builds a 3-D color histogram of <c>counts, r/g/b, c^2</c>.
362363
/// </summary>
363-
/// <param name="source">The source data.</param>
364-
/// <param name="bounds">The bounds within the source image to quantize.</param>
365-
private readonly void Build3DHistogram(Buffer2D<TPixel> source, Rectangle bounds)
364+
/// <param name="source">The source pixel data.</param>
365+
private readonly void Build3DHistogram(Buffer2DRegion<TPixel> source)
366366
{
367367
Span<Moment> momentSpan = this.momentsOwner.GetSpan();
368368

369369
// Build up the 3-D color histogram
370-
using IMemoryOwner<Rgba32> buffer = this.memoryAllocator.Allocate<Rgba32>(bounds.Width);
370+
using IMemoryOwner<Rgba32> buffer = this.memoryAllocator.Allocate<Rgba32>(source.Width);
371371
Span<Rgba32> bufferSpan = buffer.GetSpan();
372372

373-
for (int y = bounds.Top; y < bounds.Bottom; y++)
373+
for (int y = 0; y < source.Height; y++)
374374
{
375-
Span<TPixel> row = source.DangerousGetRowSpan(y).Slice(bounds.Left, bounds.Width);
375+
Span<TPixel> row = source.DangerousGetRowSpan(y);
376376
PixelOperations<TPixel>.Instance.ToRgba32(this.Configuration, row, bufferSpan);
377377

378378
for (int x = 0; x < bufferSpan.Length; x++)

tests/ImageSharp.Tests/Processing/Processors/Quantization/QuantizerTests.cs

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,74 +12,66 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization;
1212
[Trait("Category", "Processors")]
1313
public class QuantizerTests
1414
{
15-
/// <summary>
16-
/// Something is causing tests to fail on NETFX in CI.
17-
/// Could be a JIT error as everything runs well and is identical to .NET Core output.
18-
/// Not worth investigating for now.
19-
/// <see href="https://github.com/SixLabors/ImageSharp/pull/1114/checks?check_run_id=448891164#step:11:631"/>
20-
/// </summary>
21-
private static readonly bool SkipAllQuantizerTests = TestEnvironment.IsFramework;
22-
2315
public static readonly string[] CommonTestImages =
2416
{
2517
TestImages.Png.CalliphoraPartial,
2618
TestImages.Png.Bike
2719
};
2820

29-
private static readonly QuantizerOptions NoDitherOptions = new QuantizerOptions { Dither = null };
30-
private static readonly QuantizerOptions DiffuserDitherOptions = new QuantizerOptions { Dither = KnownDitherings.FloydSteinberg };
31-
private static readonly QuantizerOptions OrderedDitherOptions = new QuantizerOptions { Dither = KnownDitherings.Bayer8x8 };
21+
private static readonly QuantizerOptions NoDitherOptions = new() { Dither = null };
22+
private static readonly QuantizerOptions DiffuserDitherOptions = new() { Dither = KnownDitherings.FloydSteinberg };
23+
private static readonly QuantizerOptions OrderedDitherOptions = new() { Dither = KnownDitherings.Bayer8x8 };
3224

33-
private static readonly QuantizerOptions Diffuser0_ScaleDitherOptions = new QuantizerOptions
25+
private static readonly QuantizerOptions Diffuser0_ScaleDitherOptions = new()
3426
{
3527
Dither = KnownDitherings.FloydSteinberg,
3628
DitherScale = 0F
3729
};
3830

39-
private static readonly QuantizerOptions Diffuser0_25_ScaleDitherOptions = new QuantizerOptions
31+
private static readonly QuantizerOptions Diffuser0_25_ScaleDitherOptions = new()
4032
{
4133
Dither = KnownDitherings.FloydSteinberg,
4234
DitherScale = .25F
4335
};
4436

45-
private static readonly QuantizerOptions Diffuser0_5_ScaleDitherOptions = new QuantizerOptions
37+
private static readonly QuantizerOptions Diffuser0_5_ScaleDitherOptions = new()
4638
{
4739
Dither = KnownDitherings.FloydSteinberg,
4840
DitherScale = .5F
4941
};
5042

51-
private static readonly QuantizerOptions Diffuser0_75_ScaleDitherOptions = new QuantizerOptions
43+
private static readonly QuantizerOptions Diffuser0_75_ScaleDitherOptions = new()
5244
{
5345
Dither = KnownDitherings.FloydSteinberg,
5446
DitherScale = .75F
5547
};
5648

57-
private static readonly QuantizerOptions Ordered0_ScaleDitherOptions = new QuantizerOptions
49+
private static readonly QuantizerOptions Ordered0_ScaleDitherOptions = new()
5850
{
5951
Dither = KnownDitherings.Bayer8x8,
6052
DitherScale = 0F
6153
};
6254

63-
private static readonly QuantizerOptions Ordered0_25_ScaleDitherOptions = new QuantizerOptions
55+
private static readonly QuantizerOptions Ordered0_25_ScaleDitherOptions = new()
6456
{
6557
Dither = KnownDitherings.Bayer8x8,
6658
DitherScale = .25F
6759
};
6860

69-
private static readonly QuantizerOptions Ordered0_5_ScaleDitherOptions = new QuantizerOptions
61+
private static readonly QuantizerOptions Ordered0_5_ScaleDitherOptions = new()
7062
{
7163
Dither = KnownDitherings.Bayer8x8,
7264
DitherScale = .5F
7365
};
7466

75-
private static readonly QuantizerOptions Ordered0_75_ScaleDitherOptions = new QuantizerOptions
67+
private static readonly QuantizerOptions Ordered0_75_ScaleDitherOptions = new()
7668
{
7769
Dither = KnownDitherings.Bayer8x8,
7870
DitherScale = .75F
7971
};
8072

8173
public static readonly TheoryData<IQuantizer> Quantizers
82-
= new TheoryData<IQuantizer>
74+
= new()
8375
{
8476
// Known uses error diffusion by default.
8577
KnownQuantizers.Octree,
@@ -97,7 +89,7 @@ public static readonly TheoryData<IQuantizer> Quantizers
9789
};
9890

9991
public static readonly TheoryData<IQuantizer> DitherScaleQuantizers
100-
= new TheoryData<IQuantizer>
92+
= new()
10193
{
10294
new OctreeQuantizer(Diffuser0_ScaleDitherOptions),
10395
new WebSafePaletteQuantizer(Diffuser0_ScaleDitherOptions),
@@ -151,7 +143,7 @@ public static readonly TheoryData<IQuantizer> DitherScaleQuantizers
151143
};
152144

153145
public static readonly TheoryData<IDither> DefaultInstanceDitherers
154-
= new TheoryData<IDither>
146+
= new()
155147
{
156148
default(ErrorDither),
157149
default(OrderedDither)
@@ -164,11 +156,6 @@ public static readonly TheoryData<IDither> DefaultInstanceDitherers
164156
public void ApplyQuantizationInBox<TPixel>(TestImageProvider<TPixel> provider, IQuantizer quantizer)
165157
where TPixel : unmanaged, IPixel<TPixel>
166158
{
167-
if (SkipAllQuantizerTests)
168-
{
169-
return;
170-
}
171-
172159
string quantizerName = quantizer.GetType().Name;
173160
string ditherName = quantizer.Options.Dither?.GetType()?.Name ?? "NoDither";
174161
string testOutputDetails = $"{quantizerName}_{ditherName}";
@@ -185,11 +172,6 @@ public void ApplyQuantizationInBox<TPixel>(TestImageProvider<TPixel> provider, I
185172
public void ApplyQuantization<TPixel>(TestImageProvider<TPixel> provider, IQuantizer quantizer)
186173
where TPixel : unmanaged, IPixel<TPixel>
187174
{
188-
if (SkipAllQuantizerTests)
189-
{
190-
return;
191-
}
192-
193175
string quantizerName = quantizer.GetType().Name;
194176
string ditherName = quantizer.Options.Dither?.GetType()?.Name ?? "NoDither";
195177
string testOutputDetails = $"{quantizerName}_{ditherName}";
@@ -206,11 +188,6 @@ public void ApplyQuantization<TPixel>(TestImageProvider<TPixel> provider, IQuant
206188
public void ApplyQuantizationWithDitheringScale<TPixel>(TestImageProvider<TPixel> provider, IQuantizer quantizer)
207189
where TPixel : unmanaged, IPixel<TPixel>
208190
{
209-
if (SkipAllQuantizerTests)
210-
{
211-
return;
212-
}
213-
214191
string quantizerName = quantizer.GetType().Name;
215192
string ditherName = quantizer.Options.Dither.GetType().Name;
216193
float ditherScale = quantizer.Options.DitherScale;
@@ -229,8 +206,8 @@ public void ShouldThrowForDefaultDitherInstance(IDither dither)
229206
{
230207
void Command()
231208
{
232-
using var image = new Image<Rgba32>(10, 10);
233-
var quantizer = new WebSafePaletteQuantizer();
209+
using Image<Rgba32> image = new(10, 10);
210+
WebSafePaletteQuantizer quantizer = new();
234211
quantizer.Options.Dither = dither;
235212
image.Mutate(x => x.Quantize(quantizer));
236213
}
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading

0 commit comments

Comments
 (0)