Browse Source

QuantizedFrame<T> using ReadOnlyMemory<T>

af/merge-core
Anton Firszov 7 years ago
parent
commit
b68699c82b
  1. 4
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  2. 2
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  3. 84
      src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs
  4. 5
      src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs
  5. 4
      src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs
  6. 7
      tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs
  7. 10
      tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs

4
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
Span<Rgba32> rgbaSpan = rgbaBuffer.GetSpan();
ref Rgba32 paletteRef = ref MemoryMarshal.GetReference(rgbaSpan);
PixelOperations<TPixel>.Instance.ToRgba32(this.configuration, quantized.Palette, rgbaSpan);
PixelOperations<TPixel>.Instance.ToRgba32(this.configuration, quantized.Palette.Span, rgbaSpan);
for (int i = quantized.Palette.Length - 1; i >= 0; i--)
{
@ -436,7 +436,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
PixelOperations<TPixel>.Instance.ToRgb24Bytes(
this.configuration,
image.Palette.AsSpan(),
image.Palette.Span,
colorTable.GetSpan(),
pixelCount);
stream.Write(colorTable.Array, 0, colorTableLength);

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

@ -666,7 +666,7 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : struct, IPixel<TPixel>
{
// Grab the palette and write it to the stream.
TPixel[] palette = quantized.Palette;
ReadOnlySpan<TPixel> palette = quantized.Palette.Span;
int paletteLength = Math.Min(palette.Length, 256);
int colorTableLength = paletteLength * 3;
bool anyAlpha = false;

84
src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
@ -52,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.Dither = this.Diffuser != null;
this.singlePass = singlePass;
}
/// <summary>
/// Initializes a new instance of the <see cref="FrameQuantizer{TPixel}"/> class.
/// </summary>
@ -73,10 +74,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
/// <inheritdoc />
public bool Dither { get; }
public IErrorDiffuser Diffuser { get; }
/// <inheritdoc />
public IErrorDiffuser Diffuser { get; }
public bool Dither { get; }
/// <inheritdoc/>
public virtual void Dispose()
{
}
/// <inheritdoc/>
public virtual QuantizedFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> image)
@ -98,14 +104,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
// Collect the palette. Required before the second pass runs.
ReadOnlyMemory<TPixel> palette = this.GetPalette();
this.paletteVector = new Vector4[palette.Length];
PixelOperations<TPixel>.Instance.ToVector4(image.Configuration, palette.Span, (Span<Vector4>)this.paletteVector, PixelConversionModifiers.Scale);
PixelOperations<TPixel>.Instance.ToVector4(
image.Configuration,
palette.Span,
(Span<Vector4>)this.paletteVector,
PixelConversionModifiers.Scale);
// TODO: Pass ReadOnlyMemory<T> instead of array!
var quantizedFrame = new QuantizedFrame<TPixel>(
image.MemoryAllocator,
width,
height,
palette.Span.ToArray());
var quantizedFrame = new QuantizedFrame<TPixel>(image.MemoryAllocator, width, height, palette);
if (this.Dither)
{
@ -123,11 +128,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
return quantizedFrame;
}
/// <inheritdoc/>
public virtual void Dispose()
{
}
/// <summary>
/// Execute the first pass through the pixels in the image to create the palette.
/// </summary>
@ -139,19 +139,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
/// <summary>
/// Execute a second pass through the image to assign the pixels to a palette entry.
/// Returns the closest color from the palette to the given color by calculating the
/// Euclidean distance in the Rgba colorspace.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="output">The output pixel array.</param>
/// <param name="palette">The output color palette.</param>
/// <param name="width">The width in pixels of the image.</param>
/// <param name="height">The height in pixels of the image.</param>
protected abstract void SecondPass(
ImageFrame<TPixel> source,
Span<byte> output,
ReadOnlySpan<TPixel> palette,
int width,
int height);
/// <param name="pixel">The color.</param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected byte GetClosestPixel(ref TPixel pixel)
{
// Check if the color is in the lookup table
if (this.distanceCache.TryGetValue(pixel, out byte value))
{
return value;
}
return this.GetClosestPixelSlow(ref pixel);
}
/// <summary>
/// Retrieve the palette for the quantized image.
@ -185,22 +188,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
/// <summary>
/// Returns the closest color from the palette to the given color by calculating the
/// Euclidean distance in the Rgba colorspace.
/// Execute a second pass through the image to assign the pixels to a palette entry.
/// </summary>
/// <param name="pixel">The color.</param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected byte GetClosestPixel(ref TPixel pixel)
{
// Check if the color is in the lookup table
if (this.distanceCache.TryGetValue(pixel, out byte value))
{
return value;
}
return this.GetClosestPixelSlow(ref pixel);
}
/// <param name="source">The source image.</param>
/// <param name="output">The output pixel array.</param>
/// <param name="palette">The output color palette.</param>
/// <param name="width">The width in pixels of the image.</param>
/// <param name="height">The height in pixels of the image.</param>
protected abstract void SecondPass(
ImageFrame<TPixel> source,
Span<byte> output,
ReadOnlySpan<TPixel> palette,
int width,
int height);
[MethodImpl(MethodImplOptions.NoInlining)]
private byte GetClosestPixelSlow(ref TPixel pixel)

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

@ -43,12 +43,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
Span<TPixel> row = source.GetPixelRowSpan(y);
ReadOnlySpan<byte> quantizedPixelSpan = quantized.GetPixelSpan();
ReadOnlySpan<TPixel> paletteSpan = quantized.Palette.Span;
int yy = y * source.Width;
for (int x = 0; x < source.Width; x++)
{
int i = x + yy;
row[x] = quantized.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])];
row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[i])];
}
}
}

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

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="palette">The color palette.</param>
public QuantizedFrame(MemoryAllocator memoryAllocator, int width, int height, TPixel[] palette)
public QuantizedFrame(MemoryAllocator memoryAllocator, int width, int height, ReadOnlyMemory<TPixel> palette)
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <summary>
/// Gets the color palette of this <see cref="QuantizedFrame{TPixel}"/>.
/// </summary>
public TPixel[] Palette { get; private set; }
public ReadOnlyMemory<TPixel> Palette { get; private set; }
/// <summary>
/// Gets the pixels of this <see cref="QuantizedFrame{TPixel}"/>.

7
tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs

@ -1,6 +1,8 @@
// 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;
@ -85,9 +87,10 @@ namespace SixLabors.ImageSharp.Tests
// Transparent pixels are much more likely to be found at the end of a palette
int index = -1;
Rgba32 trans = default;
for (int i = quantized.Palette.Length - 1; i >= 0; i--)
ReadOnlySpan<TPixel> paletteSpan = quantized.Palette.Span;
for (int i = paletteSpan.Length - 1; i >= 0; i--)
{
quantized.Palette[i].ToRgba32(ref trans);
paletteSpan[i].ToRgba32(ref trans);
if (trans.Equals(default))
{

10
tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
Assert.Equal(1, result.Palette.Length);
Assert.Equal(1, result.GetPixelSpan().Length);
Assert.Equal(Rgba32.Black, result.Palette[0]);
Assert.Equal(Rgba32.Black, result.Palette.Span[0]);
Assert.Equal(0, result.GetPixelSpan()[0]);
}
}
@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
Assert.Equal(1, result.Palette.Length);
Assert.Equal(1, result.GetPixelSpan().Length);
Assert.Equal(default, result.Palette[0]);
Assert.Equal(default, result.Palette.Span[0]);
Assert.Equal(0, result.GetPixelSpan()[0]);
}
}
@ -82,6 +82,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
var actualImage = new Image<Rgba32>(1, 256);
ReadOnlySpan<Rgba32> paletteSpan = result.Palette.Span;
int paletteCount = result.Palette.Length - 1;
for (int y = 0; y < actualImage.Height; y++)
{
@ -92,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
for (int x = 0; x < actualImage.Width; x++)
{
int i = x + yy;
row[x] = result.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])];
row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[i])];
}
}
@ -146,6 +147,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
Assert.Equal(4 * 8, result.Palette.Length);
Assert.Equal(256, result.GetPixelSpan().Length);
ReadOnlySpan<Rgba32> paletteSpan = result.Palette.Span;
int paletteCount = result.Palette.Length - 1;
for (int y = 0; y < actualImage.Height; y++)
{
@ -156,7 +158,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
for (int x = 0; x < actualImage.Width; x++)
{
int i = x + yy;
row[x] = result.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])];
row[x] = paletteSpan[Math.Min(paletteCount, quantizedPixelSpan[i])];
}
}
}

Loading…
Cancel
Save