mirror of https://github.com/SixLabors/ImageSharp
19 changed files with 303 additions and 9 deletions
@ -0,0 +1,119 @@ |
|||
// <copyright file="ErrorDiffusionDitherProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Processing.Processors |
|||
{ |
|||
using System; |
|||
using System.Buffers; |
|||
|
|||
using ImageSharp.Dithering; |
|||
|
|||
/// <summary>
|
|||
/// An <see cref="IImageProcessor{TColor}"/> that dithers an image using error diffusion.
|
|||
/// </summary>
|
|||
/// <typeparam name="TColor">The pixel format.</typeparam>
|
|||
public class OrderedDitherProcessor<TColor> : ImageProcessor<TColor> |
|||
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ErrorDiffusionDitherProcessor{TColor}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="dither">The ordered ditherer.</param>
|
|||
/// <param name="index">The component index to test the threshold against. Must range from 0 to 3.</param>
|
|||
public OrderedDitherProcessor(IOrderedDither dither, int index) |
|||
{ |
|||
Guard.NotNull(dither, nameof(dither)); |
|||
Guard.MustBeBetweenOrEqualTo(index, 0, 3, nameof(index)); |
|||
|
|||
// Alpha8 only stores the pixel data in the alpha channel.
|
|||
if (typeof(TColor) == typeof(Alpha8)) |
|||
{ |
|||
index = 3; |
|||
} |
|||
|
|||
this.Dither = dither; |
|||
this.Index = index; |
|||
|
|||
// Default to white/black for upper/lower.
|
|||
TColor upper = default(TColor); |
|||
upper.PackFromBytes(255, 255, 255, 255); |
|||
this.UpperColor = upper; |
|||
|
|||
TColor lower = default(TColor); |
|||
lower.PackFromBytes(0, 0, 0, 255); |
|||
this.LowerColor = lower; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the ditherer.
|
|||
/// </summary>
|
|||
public IOrderedDither Dither { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the component index to test the threshold against.
|
|||
/// </summary>
|
|||
public int Index { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the color to use for pixels that are above the threshold.
|
|||
/// </summary>
|
|||
public TColor UpperColor { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the color to use for pixels that fall below the threshold.
|
|||
/// </summary>
|
|||
public TColor LowerColor { get; set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void BeforeApply(ImageBase<TColor> source, Rectangle sourceRectangle) |
|||
{ |
|||
new GrayscaleBt709Processor<TColor>().Apply(source, sourceRectangle); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle) |
|||
{ |
|||
int startY = sourceRectangle.Y; |
|||
int endY = sourceRectangle.Bottom; |
|||
int startX = sourceRectangle.X; |
|||
int endX = sourceRectangle.Right; |
|||
|
|||
// Align start/end positions.
|
|||
int minX = Math.Max(0, startX); |
|||
int maxX = Math.Min(source.Width, endX); |
|||
int minY = Math.Max(0, startY); |
|||
int maxY = Math.Min(source.Height, endY); |
|||
|
|||
// Reset offset if necessary.
|
|||
if (minX > 0) |
|||
{ |
|||
startX = 0; |
|||
} |
|||
|
|||
if (minY > 0) |
|||
{ |
|||
startY = 0; |
|||
} |
|||
|
|||
using (PixelAccessor<TColor> sourcePixels = source.Lock()) |
|||
{ |
|||
for (int y = minY; y < maxY; y++) |
|||
{ |
|||
int offsetY = y - startY; |
|||
byte[] bytes = ArrayPool<byte>.Shared.Rent(4); |
|||
|
|||
for (int x = minX; x < maxX; x++) |
|||
{ |
|||
int offsetX = x - startX; |
|||
TColor sourceColor = sourcePixels[offsetX, offsetY]; |
|||
this.Dither.Dither(sourcePixels, sourceColor, this.UpperColor, this.LowerColor, bytes, this.Index, offsetX, offsetY, maxX, maxY); |
|||
} |
|||
|
|||
ArrayPool<byte>.Shared.Return(bytes); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
// <copyright file="Bayer.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Dithering.Ordered |
|||
{ |
|||
using System; |
|||
|
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the 4x4 Bayer dithering matrix.
|
|||
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
|
|||
/// </summary>
|
|||
public class Bayer : IOrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// The threshold matrix.
|
|||
/// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1
|
|||
/// </summary>
|
|||
private static readonly byte[,] ThresholdMatrix = { |
|||
{ 15, 143, 47, 175 }, |
|||
{ 207, 79, 239, 111 }, |
|||
{ 63, 191, 31, 159 }, |
|||
{ 255, 127, 223, 95 } |
|||
}; |
|||
|
|||
/// <inheritdoc />
|
|||
public byte[,] Matrix { get; } = ThresholdMatrix; |
|||
|
|||
/// <inheritdoc />
|
|||
public void Dither<TColor>(PixelAccessor<TColor> pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) |
|||
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|||
{ |
|||
source.ToXyzwBytes(bytes, 0); |
|||
pixels[x, y] = ThresholdMatrix[x % 3, y % 3] >= bytes[index] ? upper : lower; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
// <copyright file="IOrderedDither.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Dithering |
|||
{ |
|||
using System; |
|||
|
|||
/// <summary>
|
|||
/// Encapsulates properties and methods required to perfom ordered dithering on an image.
|
|||
/// </summary>
|
|||
public interface IOrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the dithering matrix
|
|||
/// </summary>
|
|||
byte[,] Matrix { get; } |
|||
|
|||
/// <summary>
|
|||
/// Transforms the image applying the dither matrix. This method alters the input pixels array
|
|||
/// </summary>
|
|||
/// <param name="pixels">The pixel accessor </param>
|
|||
/// <param name="source">The source pixel</param>
|
|||
/// <param name="upper">The color to apply to the pixels above the threshold.</param>
|
|||
/// <param name="lower">The color to apply to the pixels below the threshold.</param>
|
|||
/// <param name="bytes">The byte array to pack/unpack to. Must have a length of 4. Bytes are unpacked to Xyzw order.</param>
|
|||
/// <param name="index">The component index to test the threshold against. Must range from 0 to 3.</param>
|
|||
/// <param name="x">The column index.</param>
|
|||
/// <param name="y">The row index.</param>
|
|||
/// <param name="width">The image width.</param>
|
|||
/// <param name="height">The image height.</param>
|
|||
/// <typeparam name="TColor">The pixel format.</typeparam>
|
|||
void Dither<TColor>(PixelAccessor<TColor> pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) |
|||
where TColor : struct, IPackedPixel, IEquatable<TColor>; |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
// <copyright file="Ordered.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Dithering.Ordered |
|||
{ |
|||
using System; |
|||
|
|||
/// <summary>
|
|||
/// Applies error diffusion based dithering using the 4x4 ordered dithering matrix.
|
|||
/// <see href="https://en.wikipedia.org/wiki/Ordered_dithering"/>
|
|||
/// </summary>
|
|||
public class Ordered : IOrderedDither |
|||
{ |
|||
/// <summary>
|
|||
/// The threshold matrix.
|
|||
/// This is calculated by multiplying each value in the original matrix by 16
|
|||
/// </summary>
|
|||
private static readonly byte[,] ThresholdMatrix = { |
|||
{ 0, 128, 32, 160 }, |
|||
{ 192, 64, 224, 96 }, |
|||
{ 48, 176, 16, 144 }, |
|||
{ 240, 112, 208, 80 } |
|||
}; |
|||
|
|||
/// <inheritdoc />
|
|||
public byte[,] Matrix { get; } = ThresholdMatrix; |
|||
|
|||
/// <inheritdoc />
|
|||
public void Dither<TColor>(PixelAccessor<TColor> pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) |
|||
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|||
{ |
|||
source.ToXyzwBytes(bytes, 0); |
|||
pixels[x, y] = ThresholdMatrix[x % 3, y % 3] >= bytes[index] ? upper : lower; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue