mirror of https://github.com/SixLabors/ImageSharp
51 changed files with 990 additions and 1170 deletions
@ -1,34 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Atkinson image dithering algorithm.
|
|||
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
|
|||
/// </summary>
|
|||
public sealed class AtkinsonDither : ErrorDither |
|||
{ |
|||
private const float Divisor = 8F; |
|||
private const int Offset = 1; |
|||
|
|||
/// <summary>
|
|||
/// The diffusion matrix
|
|||
/// </summary>
|
|||
private static readonly DenseMatrix<float> AtkinsonMatrix = |
|||
new float[,] |
|||
{ |
|||
{ 0, 0, 1 / Divisor, 1 / Divisor }, |
|||
{ 1 / Divisor, 1 / Divisor, 1 / Divisor, 0 }, |
|||
{ 0, 1 / Divisor, 0, 0 } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AtkinsonDither"/> class.
|
|||
/// </summary>
|
|||
public AtkinsonDither() |
|||
: base(AtkinsonMatrix, Offset) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies order dithering using the 2x2 Bayer dithering matrix.
|
|||
/// </summary>
|
|||
public sealed class BayerDither2x2 : OrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BayerDither2x2"/> class.
|
|||
/// </summary>
|
|||
public BayerDither2x2() |
|||
: base(2) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies order dithering using the 4x4 Bayer dithering matrix.
|
|||
/// </summary>
|
|||
public sealed class BayerDither4x4 : OrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BayerDither4x4"/> class.
|
|||
/// </summary>
|
|||
public BayerDither4x4() |
|||
: base(4) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies order dithering using the 8x8 Bayer dithering matrix.
|
|||
/// </summary>
|
|||
public sealed class BayerDither8x8 : OrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BayerDither8x8"/> class.
|
|||
/// </summary>
|
|||
public BayerDither8x8() |
|||
: base(8) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Burks image dithering algorithm.
|
|||
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
|
|||
/// </summary>
|
|||
public sealed class BurksDither : ErrorDither |
|||
{ |
|||
private const float Divisor = 32F; |
|||
private const int Offset = 2; |
|||
|
|||
/// <summary>
|
|||
/// The diffusion matrix
|
|||
/// </summary>
|
|||
private static readonly DenseMatrix<float> BurksMatrix = |
|||
new float[,] |
|||
{ |
|||
{ 0, 0, 0, 8 / Divisor, 4 / Divisor }, |
|||
{ 2 / Divisor, 4 / Divisor, 8 / Divisor, 4 / Divisor, 2 / Divisor } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BurksDither"/> class.
|
|||
/// </summary>
|
|||
public BurksDither() |
|||
: base(BurksMatrix, Offset) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the possible dithering algorithm transform behaviors.
|
|||
/// </summary>
|
|||
public enum DitherType |
|||
{ |
|||
/// <summary>
|
|||
/// Error diffusion. Spreads the difference between source and quanized color values as distributed error.
|
|||
/// </summary>
|
|||
ErrorDiffusion, |
|||
|
|||
/// <summary>
|
|||
/// Ordered dithering. Applies thresholding matrices agains the source to determine the quantized color.
|
|||
/// </summary>
|
|||
OrderedDither |
|||
} |
|||
} |
|||
@ -0,0 +1,188 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// An error diffusion dithering implementation.
|
|||
/// </summary>
|
|||
public readonly partial struct ErrorDither |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Atkinson image dithering algorithm.
|
|||
/// </summary>
|
|||
public static ErrorDither Atkinson = CreateAtkinson(); |
|||
|
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Burks image dithering algorithm.
|
|||
/// </summary>
|
|||
public static ErrorDither Burkes = CreateBurks(); |
|||
|
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Floyd–Steinberg image dithering algorithm.
|
|||
/// </summary>
|
|||
public static ErrorDither FloydSteinberg = CreateFloydSteinberg(); |
|||
|
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Jarvis, Judice, Ninke image dithering algorithm.
|
|||
/// </summary>
|
|||
public static ErrorDither JarvisJudiceNinke = CreateJarvisJudiceNinke(); |
|||
|
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Sierra2 image dithering algorithm.
|
|||
/// </summary>
|
|||
public static ErrorDither Sierra2 = CreateSierra2(); |
|||
|
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Sierra3 image dithering algorithm.
|
|||
/// </summary>
|
|||
public static ErrorDither Sierra3 = CreateSierra3(); |
|||
|
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Sierra Lite image dithering algorithm.
|
|||
/// </summary>
|
|||
public static ErrorDither SierraLite = CreateSierraLite(); |
|||
|
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Stevenson-Arce image dithering algorithm.
|
|||
/// </summary>
|
|||
public static ErrorDither StevensonArce = CreateStevensonArce(); |
|||
|
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Stucki image dithering algorithm.
|
|||
/// </summary>
|
|||
public static ErrorDither Stucki = CreateStucki(); |
|||
|
|||
private static ErrorDither CreateAtkinson() |
|||
{ |
|||
const float Divisor = 8F; |
|||
const int Offset = 1; |
|||
|
|||
var matrix = new float[,] |
|||
{ |
|||
{ 0, 0, 1 / Divisor, 1 / Divisor }, |
|||
{ 1 / Divisor, 1 / Divisor, 1 / Divisor, 0 }, |
|||
{ 0, 1 / Divisor, 0, 0 } |
|||
}; |
|||
|
|||
return new ErrorDither(matrix, Offset); |
|||
} |
|||
|
|||
private static ErrorDither CreateBurks() |
|||
{ |
|||
const float Divisor = 32F; |
|||
const int Offset = 2; |
|||
|
|||
var matrix = new float[,] |
|||
{ |
|||
{ 0, 0, 0, 8 / Divisor, 4 / Divisor }, |
|||
{ 2 / Divisor, 4 / Divisor, 8 / Divisor, 4 / Divisor, 2 / Divisor } |
|||
}; |
|||
|
|||
return new ErrorDither(matrix, Offset); |
|||
} |
|||
|
|||
private static ErrorDither CreateFloydSteinberg() |
|||
{ |
|||
const float Divisor = 16F; |
|||
const int Offset = 1; |
|||
|
|||
var matrix = new float[,] |
|||
{ |
|||
{ 0, 0, 7 / Divisor }, |
|||
{ 3 / Divisor, 5 / Divisor, 1 / Divisor } |
|||
}; |
|||
|
|||
return new ErrorDither(matrix, Offset); |
|||
} |
|||
|
|||
private static ErrorDither CreateJarvisJudiceNinke() |
|||
{ |
|||
const float Divisor = 48F; |
|||
const int Offset = 2; |
|||
|
|||
var matrix = new float[,] |
|||
{ |
|||
{ 0, 0, 0, 7 / Divisor, 5 / Divisor }, |
|||
{ 3 / Divisor, 5 / Divisor, 7 / Divisor, 5 / Divisor, 3 / Divisor }, |
|||
{ 1 / Divisor, 3 / Divisor, 5 / Divisor, 3 / Divisor, 1 / Divisor } |
|||
}; |
|||
|
|||
return new ErrorDither(matrix, Offset); |
|||
} |
|||
|
|||
private static ErrorDither CreateSierra2() |
|||
{ |
|||
const float Divisor = 16F; |
|||
const int Offset = 2; |
|||
|
|||
var matrix = new float[,] |
|||
{ |
|||
{ 0, 0, 0, 4 / Divisor, 3 / Divisor }, |
|||
{ 1 / Divisor, 2 / Divisor, 3 / Divisor, 2 / Divisor, 1 / Divisor } |
|||
}; |
|||
|
|||
return new ErrorDither(matrix, Offset); |
|||
} |
|||
|
|||
private static ErrorDither CreateSierra3() |
|||
{ |
|||
const float Divisor = 32F; |
|||
const int Offset = 2; |
|||
|
|||
var matrix = new float[,] |
|||
{ |
|||
{ 0, 0, 0, 5 / Divisor, 3 / Divisor }, |
|||
{ 2 / Divisor, 4 / Divisor, 5 / Divisor, 4 / Divisor, 2 / Divisor }, |
|||
{ 0, 2 / Divisor, 3 / Divisor, 2 / Divisor, 0 } |
|||
}; |
|||
|
|||
return new ErrorDither(matrix, Offset); |
|||
} |
|||
|
|||
private static ErrorDither CreateSierraLite() |
|||
{ |
|||
const float Divisor = 4F; |
|||
const int Offset = 1; |
|||
|
|||
var matrix = new float[,] |
|||
{ |
|||
{ 0, 0, 2 / Divisor }, |
|||
{ 1 / Divisor, 1 / Divisor, 0 } |
|||
}; |
|||
|
|||
return new ErrorDither(matrix, Offset); |
|||
} |
|||
|
|||
private static ErrorDither CreateStevensonArce() |
|||
{ |
|||
const float Divisor = 200F; |
|||
const int Offset = 3; |
|||
|
|||
var matrix = new float[,] |
|||
{ |
|||
{ 0, 0, 0, 0, 0, 32 / Divisor, 0 }, |
|||
{ 12 / Divisor, 0, 26 / Divisor, 0, 30 / Divisor, 0, 16 / Divisor }, |
|||
{ 0, 12 / Divisor, 0, 26 / Divisor, 0, 12 / Divisor, 0 }, |
|||
{ 5 / Divisor, 0, 12 / Divisor, 0, 12 / Divisor, 0, 5 / Divisor } |
|||
}; |
|||
|
|||
return new ErrorDither(matrix, Offset); |
|||
} |
|||
|
|||
private static ErrorDither CreateStucki() |
|||
{ |
|||
const float Divisor = 42F; |
|||
const int Offset = 2; |
|||
|
|||
var matrix = new float[,] |
|||
{ |
|||
{ 0, 0, 0, 8 / Divisor, 4 / Divisor }, |
|||
{ 2 / Divisor, 4 / Divisor, 8 / Divisor, 4 / Divisor, 2 / Divisor }, |
|||
{ 1 / Divisor, 2 / Divisor, 4 / Divisor, 2 / Divisor, 1 / Divisor } |
|||
}; |
|||
|
|||
return new ErrorDither(matrix, Offset); |
|||
} |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Floyd–Steinberg image dithering algorithm.
|
|||
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
|
|||
/// </summary>
|
|||
public sealed class FloydSteinbergDither : ErrorDither |
|||
{ |
|||
private const float Divisor = 16F; |
|||
private const int Offset = 1; |
|||
|
|||
/// <summary>
|
|||
/// The diffusion matrix
|
|||
/// </summary>
|
|||
private static readonly DenseMatrix<float> FloydSteinbergMatrix = |
|||
new float[,] |
|||
{ |
|||
{ 0, 0, 7 / Divisor }, |
|||
{ 3 / Divisor, 5 / Divisor, 1 / Divisor } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="FloydSteinbergDither"/> class.
|
|||
/// </summary>
|
|||
public FloydSteinbergDither() |
|||
: base(FloydSteinbergMatrix, Offset) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,34 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the JarvisJudiceNinke image dithering algorithm.
|
|||
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
|
|||
/// </summary>
|
|||
public sealed class JarvisJudiceNinkeDither : ErrorDither |
|||
{ |
|||
private const float Divisor = 48F; |
|||
private const int Offset = 2; |
|||
|
|||
/// <summary>
|
|||
/// The diffusion matrix
|
|||
/// </summary>
|
|||
private static readonly DenseMatrix<float> JarvisJudiceNinkeMatrix = |
|||
new float[,] |
|||
{ |
|||
{ 0, 0, 0, 7 / Divisor, 5 / Divisor }, |
|||
{ 3 / Divisor, 5 / Divisor, 7 / Divisor, 5 / Divisor, 3 / Divisor }, |
|||
{ 1 / Divisor, 3 / Divisor, 5 / Divisor, 3 / Divisor, 1 / Divisor } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="JarvisJudiceNinkeDither"/> class.
|
|||
/// </summary>
|
|||
public JarvisJudiceNinkeDither() |
|||
: base(JarvisJudiceNinkeMatrix, Offset) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <content>
|
|||
/// An ordered dithering matrix with equal sides of arbitrary length
|
|||
/// </content>
|
|||
public readonly partial struct OrderedDither : IDither |
|||
{ |
|||
/// <summary>
|
|||
/// Applies order dithering using the 2x2 Bayer dithering matrix.
|
|||
/// </summary>
|
|||
public static OrderedDither Bayer2x2 = new OrderedDither(2); |
|||
|
|||
/// <summary>
|
|||
/// Applies order dithering using the 4x4 Bayer dithering matrix.
|
|||
/// </summary>
|
|||
public static OrderedDither Bayer4x4 = new OrderedDither(4); |
|||
|
|||
/// <summary>
|
|||
/// Applies order dithering using the 8x8 Bayer dithering matrix.
|
|||
/// </summary>
|
|||
public static OrderedDither Bayer8x8 = new OrderedDither(8); |
|||
|
|||
/// <summary>
|
|||
/// Applies order dithering using the 3x3 ordered dithering matrix.
|
|||
/// </summary>
|
|||
public static OrderedDither Ordered3x3 = new OrderedDither(3); |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies order dithering using the 3x3 dithering matrix.
|
|||
/// </summary>
|
|||
public sealed class OrderedDither3x3 : OrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="OrderedDither3x3"/> class.
|
|||
/// </summary>
|
|||
public OrderedDither3x3() |
|||
: base(3) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,48 +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.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a composite pair of pixels. Used for caching color distance lookups.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
internal readonly struct PixelPair<TPixel> : IEquatable<PixelPair<TPixel>> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PixelPair{TPixel}"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="first">The first pixel color</param>
|
|||
/// <param name="second">The second pixel color</param>
|
|||
public PixelPair(TPixel first, TPixel second) |
|||
{ |
|||
this.First = first; |
|||
this.Second = second; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the first pixel color
|
|||
/// </summary>
|
|||
public TPixel First { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the second pixel color
|
|||
/// </summary>
|
|||
public TPixel Second { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(PixelPair<TPixel> other) |
|||
=> this.First.Equals(other.First) && this.Second.Equals(other.Second); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
=> obj is PixelPair<TPixel> other && this.First.Equals(other.First) && this.Second.Equals(other.Second); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() => HashCode.Combine(this.First, this.Second); |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Sierra2 image dithering algorithm.
|
|||
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
|
|||
/// </summary>
|
|||
public sealed class Sierra2Dither : ErrorDither |
|||
{ |
|||
private const float Divisor = 16F; |
|||
private const int Offset = 2; |
|||
|
|||
/// <summary>
|
|||
/// The diffusion matrix
|
|||
/// </summary>
|
|||
private static readonly DenseMatrix<float> Sierra2Matrix = |
|||
new float[,] |
|||
{ |
|||
{ 0, 0, 0, 4 / Divisor, 3 / Divisor }, |
|||
{ 1 / Divisor, 2 / Divisor, 3 / Divisor, 2 / Divisor, 1 / Divisor } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Sierra2Dither"/> class.
|
|||
/// </summary>
|
|||
public Sierra2Dither() |
|||
: base(Sierra2Matrix, Offset) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,34 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Sierra3 image dithering algorithm.
|
|||
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
|
|||
/// </summary>
|
|||
public sealed class Sierra3Dither : ErrorDither |
|||
{ |
|||
private const float Divisor = 32F; |
|||
private const int Offset = 2; |
|||
|
|||
/// <summary>
|
|||
/// The diffusion matrix
|
|||
/// </summary>
|
|||
private static readonly DenseMatrix<float> Sierra3Matrix = |
|||
new float[,] |
|||
{ |
|||
{ 0, 0, 0, 5 / Divisor, 3 / Divisor }, |
|||
{ 2 / Divisor, 4 / Divisor, 5 / Divisor, 4 / Divisor, 2 / Divisor }, |
|||
{ 0, 2 / Divisor, 3 / Divisor, 2 / Divisor, 0 } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Sierra3Dither"/> class.
|
|||
/// </summary>
|
|||
public Sierra3Dither() |
|||
: base(Sierra3Matrix, Offset) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the SierraLite image dithering algorithm.
|
|||
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
|
|||
/// </summary>
|
|||
public sealed class SierraLiteDither : ErrorDither |
|||
{ |
|||
private const float Divisor = 4F; |
|||
private const int Offset = 1; |
|||
|
|||
/// <summary>
|
|||
/// The diffusion matrix
|
|||
/// </summary>
|
|||
private static readonly DenseMatrix<float> SierraLiteMatrix = |
|||
new float[,] |
|||
{ |
|||
{ 0, 0, 2 / Divisor }, |
|||
{ 1 / Divisor, 1 / Divisor, 0 } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="SierraLiteDither"/> class.
|
|||
/// </summary>
|
|||
public SierraLiteDither() |
|||
: base(SierraLiteMatrix, Offset) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,34 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Stevenson-Arce image dithering algorithm.
|
|||
/// </summary>
|
|||
public sealed class StevensonArceDither : ErrorDither |
|||
{ |
|||
private const float Divisor = 200F; |
|||
private const int Offset = 3; |
|||
|
|||
/// <summary>
|
|||
/// The diffusion matrix
|
|||
/// </summary>
|
|||
private static readonly DenseMatrix<float> StevensonArceMatrix = |
|||
new float[,] |
|||
{ |
|||
{ 0, 0, 0, 0, 0, 32 / Divisor, 0 }, |
|||
{ 12 / Divisor, 0, 26 / Divisor, 0, 30 / Divisor, 0, 16 / Divisor }, |
|||
{ 0, 12 / Divisor, 0, 26 / Divisor, 0, 12 / Divisor, 0 }, |
|||
{ 5 / Divisor, 0, 12 / Divisor, 0, 12 / Divisor, 0, 5 / Divisor } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="StevensonArceDither"/> class.
|
|||
/// </summary>
|
|||
public StevensonArceDither() |
|||
: base(StevensonArceMatrix, Offset) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,34 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|||
{ |
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the Stucki image dithering algorithm.
|
|||
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
|
|||
/// </summary>
|
|||
public sealed class StuckiDither : ErrorDither |
|||
{ |
|||
private const float Divisor = 42F; |
|||
private const int Offset = 2; |
|||
|
|||
/// <summary>
|
|||
/// The diffusion matrix
|
|||
/// </summary>
|
|||
private static readonly DenseMatrix<float> StuckiMatrix = |
|||
new float[,] |
|||
{ |
|||
{ 0, 0, 0, 8 / Divisor, 4 / Divisor }, |
|||
{ 2 / Divisor, 4 / Divisor, 8 / Divisor, 4 / Divisor, 2 / Divisor }, |
|||
{ 1 / Divisor, 2 / Divisor, 4 / Divisor, 2 / Divisor, 1 / Divisor } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="StuckiDither"/> class.
|
|||
/// </summary>
|
|||
public StuckiDither() |
|||
: base(StuckiMatrix, Offset) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Processing.Processors.Dithering; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|||
{ |
|||
/// <summary>
|
|||
/// Contains extension methods for frame quantizers.
|
|||
/// </summary>
|
|||
public static class FrameQuantizerExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Quantizes an image frame and return the resulting output pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TFrameQuantizer">The type of frame quantizer.</typeparam>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="quantizer">The frame </param>
|
|||
/// <param name="source">The source image frame to quantize.</param>
|
|||
/// <param name="bounds">The bounds within the frame to quantize.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="QuantizedFrame{TPixel}"/> representing a quantized version of the source frame pixels.
|
|||
/// </returns>
|
|||
public static QuantizedFrame<TPixel> QuantizeFrame<TFrameQuantizer, TPixel>( |
|||
ref TFrameQuantizer quantizer, |
|||
ImageFrame<TPixel> source, |
|||
Rectangle bounds) |
|||
where TFrameQuantizer : struct, IFrameQuantizer<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
Guard.NotNull(source, nameof(source)); |
|||
var interest = Rectangle.Intersect(source.Bounds(), bounds); |
|||
|
|||
// Collect the palette. Required before the second pass runs.
|
|||
ReadOnlyMemory<TPixel> palette = quantizer.BuildPalette(source, interest); |
|||
MemoryAllocator memoryAllocator = quantizer.Configuration.MemoryAllocator; |
|||
|
|||
var quantizedFrame = new QuantizedFrame<TPixel>(memoryAllocator, interest.Width, interest.Height, palette); |
|||
Memory<byte> output = quantizedFrame.GetWritablePixelMemory(); |
|||
|
|||
if (quantizer.Options.Dither is null) |
|||
{ |
|||
SecondPass(ref quantizer, source, interest, output, palette); |
|||
} |
|||
else |
|||
{ |
|||
// We clone the image as we don't want to alter the original via error diffusion based dithering.
|
|||
using (ImageFrame<TPixel> clone = source.Clone()) |
|||
{ |
|||
SecondPass(ref quantizer, clone, interest, output, palette); |
|||
} |
|||
} |
|||
|
|||
return quantizedFrame; |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
private static void SecondPass<TFrameQuantizer, TPixel>( |
|||
ref TFrameQuantizer quantizer, |
|||
ImageFrame<TPixel> source, |
|||
Rectangle bounds, |
|||
Memory<byte> output, |
|||
ReadOnlyMemory<TPixel> palette) |
|||
where TFrameQuantizer : struct, IFrameQuantizer<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
IDither dither = quantizer.Options.Dither; |
|||
|
|||
if (dither is null) |
|||
{ |
|||
var operation = new RowIntervalOperation<TFrameQuantizer, TPixel>(quantizer, source, output, bounds, palette); |
|||
ParallelRowIterator.IterateRows( |
|||
quantizer.Configuration, |
|||
bounds, |
|||
in operation); |
|||
|
|||
return; |
|||
} |
|||
|
|||
dither.ApplyQuantizationDither(ref quantizer, palette, source, output, bounds); |
|||
} |
|||
|
|||
private readonly struct RowIntervalOperation<TFrameQuantizer, TPixel> : IRowIntervalOperation |
|||
where TFrameQuantizer : struct, IFrameQuantizer<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
private readonly TFrameQuantizer quantizer; |
|||
private readonly ImageFrame<TPixel> source; |
|||
private readonly Memory<byte> output; |
|||
private readonly Rectangle bounds; |
|||
private readonly ReadOnlyMemory<TPixel> palette; |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public RowIntervalOperation( |
|||
in TFrameQuantizer quantizer, |
|||
ImageFrame<TPixel> source, |
|||
Memory<byte> output, |
|||
Rectangle bounds, |
|||
ReadOnlyMemory<TPixel> palette) |
|||
{ |
|||
this.quantizer = quantizer; |
|||
this.source = source; |
|||
this.output = output; |
|||
this.bounds = bounds; |
|||
this.palette = palette; |
|||
} |
|||
|
|||
[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; |
|||
|
|||
for (int y = rows.Min; y < rows.Max; y++) |
|||
{ |
|||
Span<TPixel> row = this.source.GetPixelRowSpan(y); |
|||
int rowStart = (y - offsetY) * width; |
|||
|
|||
// TODO: This can be a bulk operation.
|
|||
for (int x = this.bounds.Left; x < this.bounds.Right; x++) |
|||
{ |
|||
outputSpan[rowStart + x - offsetX] = this.quantizer.GetQuantizedColor(row[x], paletteSpan, out TPixel _); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,308 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Processing.Processors.Dithering; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|||
{ |
|||
/// <summary>
|
|||
/// The base class for all <see cref="IFrameQuantizer{TPixel}"/> implementations
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
public abstract class FrameQuantizer<TPixel> : IFrameQuantizer<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
private readonly bool singlePass; |
|||
private EuclideanPixelMap<TPixel> pixelMap; |
|||
private bool isDisposed; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="FrameQuantizer{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
|
|||
/// <param name="options">The quantizer options defining quantization rules.</param>
|
|||
/// <param name="singlePass">
|
|||
/// If <see langword="true"/>, the quantization process only needs to loop through the source pixels once.
|
|||
/// </param>
|
|||
/// <remarks>
|
|||
/// If you construct this class with a <value>true</value> for <paramref name="singlePass"/>, then the code will
|
|||
/// only call the <see cref="SecondPass(ImageFrame{TPixel}, Rectangle, Memory{byte}, ReadOnlyMemory{TPixel})"/> method.
|
|||
/// If two passes are required, the code will also call <see cref="FirstPass(ImageFrame{TPixel}, Rectangle)"/>.
|
|||
/// </remarks>
|
|||
protected FrameQuantizer(Configuration configuration, QuantizerOptions options, bool singlePass) |
|||
{ |
|||
Guard.NotNull(configuration, nameof(configuration)); |
|||
Guard.NotNull(options, nameof(options)); |
|||
|
|||
this.Configuration = configuration; |
|||
this.Options = options; |
|||
this.IsDitheringQuantizer = options.Dither != null; |
|||
this.singlePass = singlePass; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public QuantizerOptions Options { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the configuration which allows altering default behaviour or extending the library.
|
|||
/// </summary>
|
|||
protected Configuration Configuration { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the frame quantizer utilizes a dithering method.
|
|||
/// </summary>
|
|||
protected bool IsDitheringQuantizer { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public void Dispose() |
|||
{ |
|||
this.Dispose(true); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public IQuantizedFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> image, Rectangle bounds) |
|||
{ |
|||
Guard.NotNull(image, nameof(image)); |
|||
var interest = Rectangle.Intersect(image.Bounds(), bounds); |
|||
|
|||
// Call the FirstPass function if not a single pass algorithm.
|
|||
// For something like an Octree quantizer, this will run through
|
|||
// all image pixels, build a data structure, and create a palette.
|
|||
if (!this.singlePass) |
|||
{ |
|||
this.FirstPass(image, interest); |
|||
} |
|||
|
|||
// Collect the palette. Required before the second pass runs.
|
|||
ReadOnlyMemory<TPixel> palette = this.GenerateQuantizedPalette(); |
|||
MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator; |
|||
this.pixelMap = new EuclideanPixelMap<TPixel>(palette); |
|||
|
|||
var quantizedFrame = new QuantizedFrame<TPixel>(memoryAllocator, interest.Width, interest.Height, palette); |
|||
|
|||
Memory<byte> output = quantizedFrame.GetWritablePixelMemory(); |
|||
if (this.Options.Dither is null) |
|||
{ |
|||
this.SecondPass(image, interest, output, palette); |
|||
} |
|||
else |
|||
{ |
|||
// We clone the image as we don't want to alter the original via error diffusion based dithering.
|
|||
using (ImageFrame<TPixel> clone = image.Clone()) |
|||
{ |
|||
this.SecondPass(clone, interest, output, palette); |
|||
} |
|||
} |
|||
|
|||
return quantizedFrame; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes the object and frees resources for the Garbage Collector.
|
|||
/// </summary>
|
|||
/// <param name="disposing">Whether to dispose managed and unmanaged objects.</param>
|
|||
protected virtual void Dispose(bool disposing) |
|||
{ |
|||
if (this.isDisposed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
this.isDisposed = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Execute the first pass through the pixels in the image to create the palette.
|
|||
/// </summary>
|
|||
/// <param name="source">The source data.</param>
|
|||
/// <param name="bounds">The bounds within the source image to quantize.</param>
|
|||
protected virtual void FirstPass(ImageFrame<TPixel> source, Rectangle bounds) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Execute a second pass through the image to assign the pixels to a palette entry.
|
|||
/// </summary>
|
|||
/// <param name="source">The source image.</param>
|
|||
/// <param name="bounds">The bounds within the source image to quantize.</param>
|
|||
/// <param name="output">The output pixel array.</param>
|
|||
/// <param name="palette">The output color palette.</param>
|
|||
protected virtual void SecondPass( |
|||
ImageFrame<TPixel> source, |
|||
Rectangle bounds, |
|||
Memory<byte> output, |
|||
ReadOnlyMemory<TPixel> palette) |
|||
{ |
|||
ReadOnlySpan<TPixel> paletteSpan = palette.Span; |
|||
IDither dither = this.Options.Dither; |
|||
|
|||
if (dither is null) |
|||
{ |
|||
var operation = new RowIntervalOperation(source, output, bounds, this, palette); |
|||
ParallelRowIterator.IterateRows( |
|||
this.Configuration, |
|||
bounds, |
|||
in operation); |
|||
|
|||
return; |
|||
} |
|||
|
|||
// Error diffusion.
|
|||
// The difference between the source and transformed color is spread to neighboring pixels.
|
|||
// TODO: Investigate parallel strategy.
|
|||
Span<byte> outputSpan = output.Span; |
|||
|
|||
int bitDepth = ImageMaths.GetBitsNeededForColorDepth(paletteSpan.Length); |
|||
if (dither.DitherType == DitherType.ErrorDiffusion) |
|||
{ |
|||
float ditherScale = this.Options.DitherScale; |
|||
int width = bounds.Width; |
|||
int offsetY = bounds.Top; |
|||
int offsetX = bounds.Left; |
|||
for (int y = bounds.Top; y < bounds.Bottom; y++) |
|||
{ |
|||
Span<TPixel> row = source.GetPixelRowSpan(y); |
|||
int rowStart = (y - offsetY) * width; |
|||
|
|||
for (int x = bounds.Left; x < bounds.Right; x++) |
|||
{ |
|||
TPixel sourcePixel = row[x]; |
|||
outputSpan[rowStart + x - offsetX] = this.GetQuantizedColor(sourcePixel, paletteSpan, out TPixel transformed); |
|||
dither.Dither(source, bounds, sourcePixel, transformed, x, y, bitDepth, ditherScale); |
|||
} |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
// Ordered dithering. We are only operating on a single pixel so we can work in parallel.
|
|||
var ditherOperation = new DitherRowIntervalOperation(source, output, bounds, this, palette, bitDepth); |
|||
ParallelRowIterator.IterateRows( |
|||
this.Configuration, |
|||
bounds, |
|||
in ditherOperation); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the index and color from the quantized palette corresponding to the give to the given color.
|
|||
/// </summary>
|
|||
/// <param name="color">The color to match.</param>
|
|||
/// <param name="palette">The output color palette.</param>
|
|||
/// <param name="match">The matched color.</param>
|
|||
/// <returns>The <see cref="byte"/> index.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
protected virtual byte GetQuantizedColor(TPixel color, ReadOnlySpan<TPixel> palette, out TPixel match) |
|||
=> this.pixelMap.GetClosestColor(color, out match); |
|||
|
|||
/// <summary>
|
|||
/// Generates the palette for the quantized image.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// <see cref="ReadOnlyMemory{TPixel}"/>
|
|||
/// </returns>
|
|||
protected abstract ReadOnlyMemory<TPixel> GenerateQuantizedPalette(); |
|||
|
|||
private readonly struct RowIntervalOperation : IRowIntervalOperation |
|||
{ |
|||
private readonly ImageFrame<TPixel> source; |
|||
private readonly Memory<byte> output; |
|||
private readonly Rectangle bounds; |
|||
private readonly FrameQuantizer<TPixel> quantizer; |
|||
private readonly ReadOnlyMemory<TPixel> palette; |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public RowIntervalOperation( |
|||
ImageFrame<TPixel> source, |
|||
Memory<byte> output, |
|||
Rectangle bounds, |
|||
FrameQuantizer<TPixel> quantizer, |
|||
ReadOnlyMemory<TPixel> palette) |
|||
{ |
|||
this.source = source; |
|||
this.output = output; |
|||
this.bounds = bounds; |
|||
this.quantizer = quantizer; |
|||
this.palette = palette; |
|||
} |
|||
|
|||
[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; |
|||
|
|||
for (int y = rows.Min; y < rows.Max; y++) |
|||
{ |
|||
Span<TPixel> row = this.source.GetPixelRowSpan(y); |
|||
int rowStart = (y - offsetY) * width; |
|||
|
|||
for (int x = this.bounds.Left; x < this.bounds.Right; x++) |
|||
{ |
|||
outputSpan[rowStart + x - offsetX] = this.quantizer.GetQuantizedColor(row[x], paletteSpan, out TPixel _); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private readonly struct DitherRowIntervalOperation : IRowIntervalOperation |
|||
{ |
|||
private readonly ImageFrame<TPixel> source; |
|||
private readonly Memory<byte> output; |
|||
private readonly Rectangle bounds; |
|||
private readonly FrameQuantizer<TPixel> quantizer; |
|||
private readonly ReadOnlyMemory<TPixel> palette; |
|||
private readonly int bitDepth; |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public DitherRowIntervalOperation( |
|||
ImageFrame<TPixel> source, |
|||
Memory<byte> output, |
|||
Rectangle bounds, |
|||
FrameQuantizer<TPixel> quantizer, |
|||
ReadOnlyMemory<TPixel> palette, |
|||
int bitDepth) |
|||
{ |
|||
this.source = source; |
|||
this.output = output; |
|||
this.bounds = bounds; |
|||
this.quantizer = quantizer; |
|||
this.palette = palette; |
|||
this.bitDepth = bitDepth; |
|||
} |
|||
|
|||
[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; |
|||
IDither dither = this.quantizer.Options.Dither; |
|||
float scale = this.quantizer.Options.DitherScale; |
|||
TPixel transformed = default; |
|||
|
|||
for (int y = rows.Min; y < rows.Max; y++) |
|||
{ |
|||
Span<TPixel> row = this.source.GetPixelRowSpan(y); |
|||
int rowStart = (y - offsetY) * width; |
|||
|
|||
for (int x = this.bounds.Left; x < this.bounds.Right; x++) |
|||
{ |
|||
TPixel dithered = dither.Dither(this.source, this.bounds, row[x], transformed, x, y, this.bitDepth, scale); |
|||
outputSpan[rowStart + x - offsetX] = this.quantizer.GetQuantizedColor(dithered, paletteSpan, out TPixel _); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// 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 : struct, 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); |
|||
} |
|||
} |
|||
@ -1,38 +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>
|
|||
/// 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(); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// 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); |
|||
} |
|||
} |
|||
@ -1 +1 @@ |
|||
Subproject commit e027069e57948c94964d0948c5f6a79ace6c601a |
|||
Subproject commit 2d1505d7087d91cd83d0cda409aee213de7841ab |
|||
Loading…
Reference in new issue