mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
33 changed files with 704 additions and 625 deletions
@ -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.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 : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the configuration instance to use when performing operations.
|
|||
/// </summary>
|
|||
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>
|
|||
/// <returns>The <typeparamref name="TPixel"/> match.</returns>
|
|||
TPixel GetPaletteColor(TPixel color); |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
// 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>
|
|||
/// Allows the mapping of input colors to colors within a given palette.
|
|||
/// TODO: Expose this somehow.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
internal interface IPixelMap<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the color palette containing colors to match.
|
|||
/// </summary>
|
|||
ReadOnlyMemory<TPixel> Palette { get; } |
|||
|
|||
/// <summary>
|
|||
/// Returns the closest color in the palette and the index of that pixel.
|
|||
/// </summary>
|
|||
/// <param name="color">The color to match.</param>
|
|||
/// <param name="match">The matched color.</param>
|
|||
/// <returns>The <see cref="int"/> index.</returns>
|
|||
int GetClosestColor(TPixel color, out TPixel match); |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|||
{ |
|||
/// <summary>
|
|||
/// A pixel-specific image frame where each pixel buffer value represents an index in a color palette.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
public sealed class IndexedImageFrame<TPixel> : IDisposable |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private IMemoryOwner<byte> pixelsOwner; |
|||
private IMemoryOwner<TPixel> paletteOwner; |
|||
private bool isDisposed; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="IndexedImageFrame{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="configuration">
|
|||
/// The configuration which allows altering default behaviour or extending the library.
|
|||
/// </param>
|
|||
/// <param name="width">The frame width.</param>
|
|||
/// <param name="height">The frame height.</param>
|
|||
/// <param name="palette">The color palette.</param>
|
|||
internal IndexedImageFrame(Configuration configuration, int width, int height, ReadOnlyMemory<TPixel> palette) |
|||
{ |
|||
Guard.NotNull(configuration, nameof(configuration)); |
|||
Guard.MustBeLessThanOrEqualTo(palette.Length, QuantizerConstants.MaxColors, nameof(palette)); |
|||
Guard.MustBeGreaterThan(width, 0, nameof(width)); |
|||
Guard.MustBeGreaterThan(height, 0, nameof(height)); |
|||
|
|||
this.Configuration = configuration; |
|||
this.Width = width; |
|||
this.Height = height; |
|||
this.pixelsOwner = configuration.MemoryAllocator.AllocateManagedByteBuffer(width * height); |
|||
|
|||
// Copy the palette over. We want the lifetime of this frame to be independant of any palette source.
|
|||
this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(palette.Length); |
|||
palette.Span.CopyTo(this.paletteOwner.GetSpan()); |
|||
this.Palette = this.paletteOwner.Memory.Slice(0, palette.Length); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the configuration which allows altering default behaviour or extending the library.
|
|||
/// </summary>
|
|||
public Configuration Configuration { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the width of this <see cref="IndexedImageFrame{TPixel}"/>.
|
|||
/// </summary>
|
|||
public int Width { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the height of this <see cref="IndexedImageFrame{TPixel}"/>.
|
|||
/// </summary>
|
|||
public int Height { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the color palette of this <see cref="IndexedImageFrame{TPixel}"/>.
|
|||
/// </summary>
|
|||
public ReadOnlyMemory<TPixel> Palette { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the pixels of this <see cref="IndexedImageFrame{TPixel}"/>.
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="ReadOnlySpan{T}"/></returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public ReadOnlySpan<byte> GetPixelBufferSpan() => this.pixelsOwner.GetSpan(); // TODO: Buffer2D<byte>
|
|||
|
|||
/// <summary>
|
|||
/// Gets the representation of the pixels as a <see cref="ReadOnlySpan{T}"/> of contiguous memory
|
|||
/// at row <paramref name="rowIndex"/> beginning from the the first pixel on that row.
|
|||
/// </summary>
|
|||
/// <param name="rowIndex">The row index in the pixel buffer.</param>
|
|||
/// <returns>The pixel row as a <see cref="ReadOnlySpan{T}"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public ReadOnlySpan<byte> GetPixelRowSpan(int rowIndex) |
|||
=> this.GetWritablePixelRowSpanUnsafe(rowIndex); |
|||
|
|||
/// <summary>
|
|||
/// <para>
|
|||
/// 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.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// Note: Values written to this span are not sanitized against the palette length.
|
|||
/// Care should be taken during assignment to prevent out-of-bounds errors.
|
|||
/// </para>
|
|||
/// </summary>
|
|||
/// <param name="rowIndex">The row index in the pixel buffer.</param>
|
|||
/// <returns>The pixel row as a <see cref="Span{T}"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public Span<byte> GetWritablePixelRowSpanUnsafe(int rowIndex) |
|||
=> this.pixelsOwner.GetSpan().Slice(rowIndex * this.Width, this.Width); |
|||
|
|||
/// <inheritdoc/>
|
|||
public void Dispose() |
|||
{ |
|||
if (!this.isDisposed) |
|||
{ |
|||
this.isDisposed = true; |
|||
this.pixelsOwner.Dispose(); |
|||
this.paletteOwner.Dispose(); |
|||
this.pixelsOwner = null; |
|||
this.paletteOwner = null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,91 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
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 sealed class QuantizedFrame<TPixel> : IDisposable |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private IMemoryOwner<byte> pixels; |
|||
private bool isDisposed; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="QuantizedFrame{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="memoryAllocator">Used to allocated memory for image processing operations.</param>
|
|||
/// <param name="width">The image width.</param>
|
|||
/// <param name="height">The image height.</param>
|
|||
/// <param name="palette">The color palette.</param>
|
|||
internal QuantizedFrame(MemoryAllocator memoryAllocator, int width, int height, ReadOnlyMemory<TPixel> palette) |
|||
{ |
|||
Guard.MustBeGreaterThan(width, 0, nameof(width)); |
|||
Guard.MustBeGreaterThan(height, 0, nameof(height)); |
|||
|
|||
this.Width = width; |
|||
this.Height = height; |
|||
this.Palette = palette; |
|||
this.pixels = memoryAllocator.AllocateManagedByteBuffer(width * height, AllocationOptions.Clean); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the width of this <see cref="QuantizedFrame{TPixel}"/>.
|
|||
/// </summary>
|
|||
public int Width { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the height of this <see cref="QuantizedFrame{TPixel}"/>.
|
|||
/// </summary>
|
|||
public int Height { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the color palette of this <see cref="QuantizedFrame{TPixel}"/>.
|
|||
/// </summary>
|
|||
public ReadOnlyMemory<TPixel> Palette { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the pixels of this <see cref="QuantizedFrame{TPixel}"/>.
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="Span{T}"/></returns>
|
|||
[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 pixel row as a <see cref="ReadOnlySpan{T}"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public ReadOnlySpan<byte> GetRowSpan(int rowIndex) |
|||
=> this.GetPixelSpan().Slice(rowIndex * this.Width, this.Width); |
|||
|
|||
/// <inheritdoc/>
|
|||
public void Dispose() |
|||
{ |
|||
if (this.isDisposed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
this.isDisposed = true; |
|||
this.pixels?.Dispose(); |
|||
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; |
|||
} |
|||
} |
|||
@ -1 +1 @@ |
|||
Subproject commit f8a76fd3a900b90c98df67ac896574383a4d09f3 |
|||
Subproject commit 1fea1ceab89e87cc5f11376fa46164d3d27566c0 |
|||
Loading…
Reference in new issue