Browse Source

introduce IQuantizedFrame<T>

pull/908/head
Anton Firszov 7 years ago
parent
commit
fef3a11b23
  1. 16
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  2. 8
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  3. 2
      src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs
  4. 2
      src/ImageSharp/Processing/Processors/Quantization/IFrameQuantizer{TPixel}.cs
  5. 38
      src/ImageSharp/Processing/Processors/Quantization/IQuantizedFrame{TPixel}.cs
  6. 4
      src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs
  7. 29
      src/ImageSharp/Processing/Processors/Quantization/QuantizedFrameExtensions.cs
  8. 12
      src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs
  9. 6
      tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs
  10. 10
      tests/ImageSharp.Tests/Quantization/WuQuantizerTests.cs

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

@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;
// Quantize the image returning a palette.
QuantizedFrame<TPixel> quantized = null;
IQuantizedFrame<TPixel> quantized = null;
using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration()))
{
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame);
@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
stream.WriteByte(GifConstants.EndIntroducer);
}
private void EncodeGlobal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, int transparencyIndex, Stream stream)
private void EncodeGlobal<TPixel>(Image<TPixel> image, IQuantizedFrame<TPixel> quantized, int transparencyIndex, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
for (int i = 0; i < image.Frames.Count; i++)
@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
using (IFrameQuantizer<TPixel> palleteFrameQuantizer =
new PaletteFrameQuantizer<TPixel>(this.quantizer.Diffuser, quantized.Palette))
{
using (QuantizedFrame<TPixel> paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame))
using (IQuantizedFrame<TPixel> paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame))
{
this.WriteImageData(paletteQuantized, stream);
}
@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
}
private void EncodeLocal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, Stream stream)
private void EncodeLocal<TPixel>(Image<TPixel> image, IQuantizedFrame<TPixel> quantized, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
ImageFrame<TPixel> previousFrame = null;
@ -222,7 +222,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <returns>
/// The <see cref="int"/>.
/// </returns>
private int GetTransparentIndex<TPixel>(QuantizedFrame<TPixel> quantized)
private int GetTransparentIndex<TPixel>(IQuantizedFrame<TPixel> quantized)
where TPixel : struct, IPixel<TPixel>
{
// Transparent pixels are much more likely to be found at the end of a palette
@ -425,7 +425,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
private void WriteColorTable<TPixel>(IQuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
// The maximum number of colors for the bit depth
@ -447,9 +447,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Writes the image pixel data to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="QuantizedFrame{TPixel}"/> containing indexed pixels.</param>
/// <param name="image">The <see cref="IQuantizedFrame{TPixel}"/> containing indexed pixels.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteImageData<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
private void WriteImageData<TPixel>(IQuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
using (var encoder = new LzwEncoder(this.memoryAllocator, (byte)this.bitDepth))

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

@ -227,7 +227,7 @@ namespace SixLabors.ImageSharp.Formats.Png
stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);
QuantizedFrame<TPixel> quantized = null;
IQuantizedFrame<TPixel> quantized = null;
if (this.pngColorType == PngColorType.Palette)
{
byte bits = (byte)this.pngBitDepth;
@ -511,7 +511,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="quantized">The quantized pixels. Can be null.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="IManagedByteBuffer"/></returns>
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, QuantizedFrame<TPixel> quantized, int row)
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, IQuantizedFrame<TPixel> quantized, int row)
where TPixel : struct, IPixel<TPixel>
{
switch (this.pngColorType)
@ -662,7 +662,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="quantized">The quantized frame.</param>
private void WritePaletteChunk<TPixel>(Stream stream, QuantizedFrame<TPixel> quantized)
private void WritePaletteChunk<TPixel>(Stream stream, IQuantizedFrame<TPixel> quantized)
where TPixel : struct, IPixel<TPixel>
{
// Grab the palette and write it to the stream.
@ -808,7 +808,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="pixels">The image.</param>
/// <param name="quantized">The quantized pixel data. Can be null.</param>
/// <param name="stream">The stream.</param>
private void WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, QuantizedFrame<TPixel> quantized, Stream stream)
private void WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, IQuantizedFrame<TPixel> quantized, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
this.bytesPerScanline = this.CalculateScanlineLength(this.width);

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

@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
/// <inheritdoc/>
public QuantizedFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> image)
public IQuantizedFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> image)
{
Guard.NotNull(image, nameof(image));

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

@ -31,6 +31,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <returns>
/// A <see cref="QuantizedFrame{TPixel}"/> representing a quantized version of the image pixels.
/// </returns>
QuantizedFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> image);
IQuantizedFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> image);
}
}

38
src/ImageSharp/Processing/Processors/Quantization/IQuantizedFrame{TPixel}.cs

@ -0,0 +1,38 @@
// 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.Quantization
{
/// <summary>
/// Defines an abstraction to represent a quantized image frame where the pixels indexed by a color palette.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public interface IQuantizedFrame<TPixel> : IDisposable
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets the width of this <see cref="QuantizedFrame{TPixel}"/>.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height of this <see cref="QuantizedFrame{TPixel}"/>.
/// </summary>
int Height { get; }
/// <summary>
/// Gets the color palette of this <see cref="QuantizedFrame{TPixel}"/>.
/// </summary>
ReadOnlyMemory<TPixel> Palette { get; }
/// <summary>
/// Gets the pixels of this <see cref="QuantizedFrame{TPixel}"/>.
/// </summary>
/// <returns>The <see cref="Span{T}"/>The pixel span.</returns>
ReadOnlySpan<byte> GetPixelSpan();
}
}

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

@ -31,8 +31,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <inheritdoc />
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
using (IFrameQuantizer<TPixel> executor = this.quantizer.CreateFrameQuantizer<TPixel>(configuration))
using (QuantizedFrame<TPixel> quantized = executor.QuantizeFrame(source))
using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(configuration))
using (IQuantizedFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(source))
{
int paletteCount = quantized.Palette.Length - 1;

29
src/ImageSharp/Processing/Processors/Quantization/QuantizedFrameExtensions.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// Contains extension methods for <see cref="IQuantizedFrame{TPixel}"/>.
/// </summary>
public static class QuantizedFrameExtensions
{
/// <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="frame">The <see cref="IQuantizedFrame{TPixel}"/>.</param>
/// <param name="rowIndex">The row.</param>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <returns>The pixel row as a <see cref="ReadOnlySpan{T}"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static ReadOnlySpan<byte> GetRowSpan<TPixel>(this IQuantizedFrame<TPixel> frame, int rowIndex)
where TPixel : struct, IPixel<TPixel>
=> frame.GetPixelSpan().Slice(rowIndex * frame.Width, frame.Width);
}
}

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

@ -8,14 +8,13 @@ using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
// TODO: Consider pooling the TPixel palette also. For Rgba48+ this would end up on th LOH if 256 colors.
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// Represents a quantized image frame where the pixels indexed by a color palette.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class QuantizedFrame<TPixel> : IDisposable
public class QuantizedFrame<TPixel> : IQuantizedFrame<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private IMemoryOwner<byte> pixels;
@ -60,15 +59,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
[MethodImpl(InliningOptions.ShortMethod)]
public ReadOnlySpan<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 <see cref="Span{T}"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
public ReadOnlySpan<byte> GetRowSpan(int rowIndex) => this.GetPixelSpan().Slice(rowIndex * this.Width, this.Width);
/// <inheritdoc/>
public void Dispose()
{

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

@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests
foreach (ImageFrame<TPixel> frame in image.Frames)
{
QuantizedFrame<TPixel> quantized =
IQuantizedFrame<TPixel> quantized =
quantizer.CreateFrameQuantizer<TPixel>(this.Configuration).QuantizeFrame(frame);
int index = this.GetTransparentIndex(quantized);
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests
foreach (ImageFrame<TPixel> frame in image.Frames)
{
QuantizedFrame<TPixel> quantized =
IQuantizedFrame<TPixel> quantized =
quantizer.CreateFrameQuantizer<TPixel>(this.Configuration).QuantizeFrame(frame);
int index = this.GetTransparentIndex(quantized);
@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests
}
}
private int GetTransparentIndex<TPixel>(QuantizedFrame<TPixel> quantized)
private int GetTransparentIndex<TPixel>(IQuantizedFrame<TPixel> quantized)
where TPixel : struct, IPixel<TPixel>
{
// Transparent pixels are much more likely to be found at the end of a palette

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

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
var quantizer = new WuQuantizer(false);
using (var image = new Image<Rgba32>(config, 1, 1, Rgba32.Black))
using (QuantizedFrame<Rgba32> result = quantizer.CreateFrameQuantizer<Rgba32>(config).QuantizeFrame(image.Frames[0]))
using (IQuantizedFrame<Rgba32> result = quantizer.CreateFrameQuantizer<Rgba32>(config).QuantizeFrame(image.Frames[0]))
{
Assert.Equal(1, result.Palette.Length);
Assert.Equal(1, result.GetPixelSpan().Length);
@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
var quantizer = new WuQuantizer(false);
using (var image = new Image<Rgba32>(config, 1, 1, default(Rgba32)))
using (QuantizedFrame<Rgba32> result = quantizer.CreateFrameQuantizer<Rgba32>(config).QuantizeFrame(image.Frames[0]))
using (IQuantizedFrame<Rgba32> result = quantizer.CreateFrameQuantizer<Rgba32>(config).QuantizeFrame(image.Frames[0]))
{
Assert.Equal(1, result.Palette.Length);
Assert.Equal(1, result.GetPixelSpan().Length);
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
Configuration config = Configuration.Default;
var quantizer = new WuQuantizer(false);
using (IFrameQuantizer<Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(config))
using (QuantizedFrame<Rgba32> result = frameQuantizer.QuantizeFrame(image.Frames[0]))
using (IQuantizedFrame<Rgba32> result = frameQuantizer.QuantizeFrame(image.Frames[0]))
{
Assert.Equal(256, result.Palette.Length);
Assert.Equal(256, result.GetPixelSpan().Length);
@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
Configuration config = Configuration.Default;
var quantizer = new WuQuantizer(false);
using (IFrameQuantizer<TPixel> frameQuantizer = quantizer.CreateFrameQuantizer<TPixel>(config))
using (QuantizedFrame<TPixel> result = frameQuantizer.QuantizeFrame(image.Frames[0]))
using (IQuantizedFrame<TPixel> result = frameQuantizer.QuantizeFrame(image.Frames[0]))
{
Assert.Equal(48, result.Palette.Length);
}
@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization
var quantizer = new WuQuantizer(false);
using (IFrameQuantizer<Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(config))
using (QuantizedFrame<Rgba32> result = frameQuantizer.QuantizeFrame(image.Frames[0]))
using (IQuantizedFrame<Rgba32> result = frameQuantizer.QuantizeFrame(image.Frames[0]))
{
Assert.Equal(4 * 8, result.Palette.Length);
Assert.Equal(256, result.GetPixelSpan().Length);

Loading…
Cancel
Save