Browse Source

Internalize PixelAccessor

af/merge-core
James Jackson-South 9 years ago
parent
commit
e768b09198
  1. 4
      src/ImageSharp.Drawing/Brushes/IBrush.cs
  2. 37
      src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
  3. 18
      src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
  4. 9
      src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
  5. 22
      src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
  6. 14
      src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
  7. 4
      src/ImageSharp.Drawing/Pens/IPen.cs
  8. 18
      src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs
  9. 9
      src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
  10. 7
      src/ImageSharp.Drawing/Processors/FillProcessor.cs
  11. 7
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  12. 14
      src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs
  13. 8
      src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs
  14. 4
      src/ImageSharp/Dithering/Ordered/IOrderedDither.cs
  15. 6
      src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs
  16. 9
      src/ImageSharp/Image/IImageBase{TPixel}.cs
  17. 118
      src/ImageSharp/Image/ImageBase{TPixel}.cs
  18. 4
      src/ImageSharp/Image/PixelAccessor{TPixel}.cs
  19. 2
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  20. 17
      src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs
  21. 21
      src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs
  22. 4
      src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs
  23. 4
      src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs
  24. 45
      src/ImageSharp/Quantizers/Quantizer{TPixel}.cs
  25. 6
      src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs

4
src/ImageSharp.Drawing/Brushes/IBrush.cs

@ -22,7 +22,7 @@ namespace ImageSharp.Drawing
/// <summary>
/// Creates the applicator for this brush.
/// </summary>
/// <param name="pixelSource">The pixel source.</param>
/// <param name="source">The source image.</param>
/// <param name="region">The region the brush will be applied to.</param>
/// <param name="options">The graphic options</param>
/// <returns>
@ -32,6 +32,6 @@ namespace ImageSharp.Drawing
/// The <paramref name="region" /> when being applied to things like shapes would usually be the
/// bounding box of the shape not necessarily the bounds of the whole image
/// </remarks>
BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> pixelSource, RectangleF region, GraphicsOptions options);
BrushApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options);
}
}

37
src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs

@ -6,7 +6,6 @@
namespace ImageSharp.Drawing.Brushes
{
using System;
using System.Numerics;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
@ -22,21 +21,21 @@ namespace ImageSharp.Drawing.Brushes
/// <summary>
/// The image to paint.
/// </summary>
private readonly IImageBase<TPixel> image;
private readonly ImageBase<TPixel> image;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrush{TPixel}"/> class.
/// </summary>
/// <param name="image">The image.</param>
public ImageBrush(IImageBase<TPixel> image)
public ImageBrush(ImageBase<TPixel> image)
{
this.image = image;
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region, GraphicsOptions options)
public BrushApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options)
{
return new ImageBrushApplicator(sourcePixels, this.image, region, options);
return new ImageBrushApplicator(source, this.image, region, options);
}
/// <summary>
@ -45,9 +44,9 @@ namespace ImageSharp.Drawing.Brushes
private class ImageBrushApplicator : BrushApplicator<TPixel>
{
/// <summary>
/// The source pixel accessor.
/// The source image.
/// </summary>
private readonly PixelAccessor<TPixel> source;
private readonly ImageBase<TPixel> source;
/// <summary>
/// The y-length.
@ -72,20 +71,14 @@ namespace ImageSharp.Drawing.Brushes
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrushApplicator"/> class.
/// </summary>
/// <param name="image">
/// The image.
/// </param>
/// <param name="region">
/// The region.
/// </param>
/// <param name="target">The target image.</param>
/// <param name="image">The image.</param>
/// <param name="region">The region.</param>
/// <param name="options">The options</param>
/// <param name="sourcePixels">
/// The sourcePixels.
/// </param>
public ImageBrushApplicator(PixelAccessor<TPixel> sourcePixels, IImageBase<TPixel> image, RectangleF region, GraphicsOptions options)
: base(sourcePixels, options)
public ImageBrushApplicator(ImageBase<TPixel> target, ImageBase<TPixel> image, RectangleF region, GraphicsOptions options)
: base(target, options)
{
this.source = image.Lock();
this.source = image;
this.xLength = image.Width;
this.yLength = image.Height;
this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0);
@ -119,9 +112,9 @@ namespace ImageSharp.Drawing.Brushes
/// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y)
{
// create a span for colors
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length))
// Create a span for colors
using (var amountBuffer = new Buffer<float>(scanline.Length))
using (var overlay = new Buffer<TPixel>(scanline.Length))
{
int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX;

18
src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs

@ -62,8 +62,8 @@ namespace ImageSharp.Drawing.Brushes
/// <param name="pattern">The pattern.</param>
internal PatternBrush(TPixel foreColor, TPixel backColor, Fast2DArray<bool> pattern)
{
Vector4 foreColorVector = foreColor.ToVector4();
Vector4 backColorVector = backColor.ToVector4();
var foreColorVector = foreColor.ToVector4();
var backColorVector = backColor.ToVector4();
this.pattern = new Fast2DArray<TPixel>(pattern.Width, pattern.Height);
this.patternVector = new Fast2DArray<Vector4>(pattern.Width, pattern.Height);
for (int i = 0; i < pattern.Data.Length; i++)
@ -92,9 +92,9 @@ namespace ImageSharp.Drawing.Brushes
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region, GraphicsOptions options)
public BrushApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options)
{
return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector, options);
return new PatternBrushApplicator(source, this.pattern, this.patternVector, options);
}
/// <summary>
@ -111,12 +111,12 @@ namespace ImageSharp.Drawing.Brushes
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrushApplicator" /> class.
/// </summary>
/// <param name="sourcePixels">The sourcePixels.</param>
/// <param name="source">The source image.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="patternVector">The patternVector.</param>
/// <param name="options">The options</param>
public PatternBrushApplicator(PixelAccessor<TPixel> sourcePixels, Fast2DArray<TPixel> pattern, Fast2DArray<Vector4> patternVector, GraphicsOptions options)
: base(sourcePixels, options)
public PatternBrushApplicator(ImageBase<TPixel> source, Fast2DArray<TPixel> pattern, Fast2DArray<Vector4> patternVector, GraphicsOptions options)
: base(source, options)
{
this.pattern = pattern;
this.patternVector = patternVector;
@ -152,8 +152,8 @@ namespace ImageSharp.Drawing.Brushes
internal override void Apply(Span<float> scanline, int x, int y)
{
int patternY = y % this.pattern.Height;
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length))
using (var amountBuffer = new Buffer<float>(scanline.Length))
using (var overlay = new Buffer<TPixel>(scanline.Length))
{
for (int i = 0; i < scanline.Length; i++)
{

9
src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs

@ -6,7 +6,6 @@
namespace ImageSharp.Drawing.Processors
{
using System;
using System.Numerics;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
@ -24,7 +23,7 @@ namespace ImageSharp.Drawing.Processors
/// </summary>
/// <param name="target">The target.</param>
/// <param name="options">The options.</param>
internal BrushApplicator(PixelAccessor<TPixel> target, GraphicsOptions options)
internal BrushApplicator(ImageBase<TPixel> target, GraphicsOptions options)
{
this.Target = target;
@ -41,7 +40,7 @@ namespace ImageSharp.Drawing.Processors
/// <summary>
/// Gets the destinaion
/// </summary>
protected PixelAccessor<TPixel> Target { get; }
protected ImageBase<TPixel> Target { get; }
/// <summary>
/// Gets the blend percentage
@ -68,8 +67,8 @@ namespace ImageSharp.Drawing.Processors
/// <remarks>scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.</remarks>
internal virtual void Apply(Span<float> scanline, int x, int y)
{
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length))
using (var amountBuffer = new Buffer<float>(scanline.Length))
using (var overlay = new Buffer<TPixel>(scanline.Length))
{
for (int i = 0; i < scanline.Length; i++)
{

22
src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs

@ -57,9 +57,9 @@ namespace ImageSharp.Drawing.Brushes
public TPixel TargeTPixel { get; }
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region, GraphicsOptions options)
public BrushApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options)
{
return new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargeTPixel, this.Threshold, options);
return new RecolorBrushApplicator(source, this.SourceColor, this.TargeTPixel, this.Threshold, options);
}
/// <summary>
@ -87,22 +87,22 @@ namespace ImageSharp.Drawing.Brushes
/// <summary>
/// Initializes a new instance of the <see cref="RecolorBrushApplicator" /> class.
/// </summary>
/// <param name="sourcePixels">The source pixels.</param>
/// <param name="source">The source image.</param>
/// <param name="sourceColor">Color of the source.</param>
/// <param name="targetColor">Color of the target.</param>
/// <param name="threshold">The threshold .</param>
/// <param name="options">The options</param>
public RecolorBrushApplicator(PixelAccessor<TPixel> sourcePixels, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options)
: base(sourcePixels, options)
public RecolorBrushApplicator(ImageBase<TPixel> source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options)
: base(source, options)
{
this.sourceColor = sourceColor.ToVector4();
this.targetColor = targetColor.ToVector4();
this.targetColorPixel = targetColor;
// Lets hack a min max extreams for a color space by letteing the IPackedPixel clamp our values to something in the correct spaces :)
TPixel maxColor = default(TPixel);
// Lets hack a min max extreams for a color space by letting the IPackedPixel clamp our values to something in the correct spaces :)
var maxColor = default(TPixel);
maxColor.PackFromVector4(new Vector4(float.MaxValue));
TPixel minColor = default(TPixel);
var minColor = default(TPixel);
minColor.PackFromVector4(new Vector4(float.MinValue));
this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold;
}
@ -121,7 +121,7 @@ namespace ImageSharp.Drawing.Brushes
{
// Offset the requested pixel by the value in the rectangle (the shapes position)
TPixel result = this.Target[x, y];
Vector4 background = result.ToVector4();
var background = result.ToVector4();
float distance = Vector4.DistanceSquared(background, this.sourceColor);
if (distance <= this.threshold)
{
@ -144,8 +144,8 @@ namespace ImageSharp.Drawing.Brushes
/// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y)
{
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length))
using (var amountBuffer = new Buffer<float>(scanline.Length))
using (var overlay = new Buffer<TPixel>(scanline.Length))
{
for (int i = 0; i < scanline.Length; i++)
{

14
src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs

@ -42,9 +42,9 @@ namespace ImageSharp.Drawing.Brushes
public TPixel Color => this.color;
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region, GraphicsOptions options)
public BrushApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options)
{
return new SolidBrushApplicator(sourcePixels, this.color, options);
return new SolidBrushApplicator(source, this.color, options);
}
/// <summary>
@ -55,13 +55,13 @@ namespace ImageSharp.Drawing.Brushes
/// <summary>
/// Initializes a new instance of the <see cref="SolidBrushApplicator"/> class.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="color">The color.</param>
/// <param name="options">The options</param>
/// <param name="sourcePixels">The sourcePixels.</param>
public SolidBrushApplicator(PixelAccessor<TPixel> sourcePixels, TPixel color, GraphicsOptions options)
: base(sourcePixels, options)
public SolidBrushApplicator(ImageBase<TPixel> source, TPixel color, GraphicsOptions options)
: base(source, options)
{
this.Colors = new Buffer<TPixel>(sourcePixels.Width);
this.Colors = new Buffer<TPixel>(source.Width);
for (int i = 0; i < this.Colors.Length; i++)
{
this.Colors[i] = color;
@ -94,7 +94,7 @@ namespace ImageSharp.Drawing.Brushes
{
Span<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length))
using (var amountBuffer = new Buffer<float>(scanline.Length))
{
for (int i = 0; i < scanline.Length; i++)
{

4
src/ImageSharp.Drawing/Pens/IPen.cs

@ -18,7 +18,7 @@ namespace ImageSharp.Drawing.Pens
/// <summary>
/// Creates the applicator for applying this pen to an Image
/// </summary>
/// <param name="pixelSource">The pixel source.</param>
/// <param name="source">The source image.</param>
/// <param name="region">The region the pen will be applied to.</param>
/// <param name="options">The currently active graphic options.</param>
/// <returns>
@ -27,6 +27,6 @@ namespace ImageSharp.Drawing.Pens
/// <remarks>
/// The <paramref name="region" /> when being applied to things like shapes would usually be the bounding box of the shape not necessarily the shape of the whole image.
/// </remarks>
PenApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> pixelSource, RectangleF region, GraphicsOptions options);
PenApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options);
}
}

18
src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs

@ -101,7 +101,7 @@ namespace ImageSharp.Drawing.Pens
/// <summary>
/// Creates the applicator for applying this pen to an Image
/// </summary>
/// <param name="sourcePixels">The source pixels.</param>
/// <param name="source">The source image.</param>
/// <param name="region">The region the pen will be applied to.</param>
/// <param name="options">The Graphics options</param>
/// <returns>
@ -111,16 +111,16 @@ namespace ImageSharp.Drawing.Pens
/// The <paramref name="region" /> when being applied to things like shapes would ussually be the
/// bounding box of the shape not necorserrally the shape of the whole image
/// </remarks>
public PenApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region, GraphicsOptions options)
public PenApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options)
{
if (this.pattern == null || this.pattern.Length < 2)
{
// if there is only one item in the pattern then 100% of it will
// be solid so use the quicker applicator
return new SolidPenApplicator(sourcePixels, this.Brush, region, this.Width, options);
return new SolidPenApplicator(source, this.Brush, region, this.Width, options);
}
return new PatternPenApplicator(sourcePixels, this.Brush, region, this.Width, this.pattern, options);
return new PatternPenApplicator(source, this.Brush, region, this.Width, this.pattern, options);
}
private class SolidPenApplicator : PenApplicator<TPixel>
@ -128,7 +128,7 @@ namespace ImageSharp.Drawing.Pens
private readonly BrushApplicator<TPixel> brush;
private readonly float halfWidth;
public SolidPenApplicator(PixelAccessor<TPixel> sourcePixels, IBrush<TPixel> brush, RectangleF region, float width, GraphicsOptions options)
public SolidPenApplicator(ImageBase<TPixel> sourcePixels, IBrush<TPixel> brush, RectangleF region, float width, GraphicsOptions options)
{
this.brush = brush.CreateApplicator(sourcePixels, region, options);
this.halfWidth = width / 2;
@ -147,7 +147,7 @@ namespace ImageSharp.Drawing.Pens
public override ColoredPointInfo<TPixel> GetColor(int x, int y, PointInfo info)
{
ColoredPointInfo<TPixel> result = default(ColoredPointInfo<TPixel>);
var result = default(ColoredPointInfo<TPixel>);
result.Color = this.brush[x, y];
if (info.DistanceFromPath < this.halfWidth)
@ -171,9 +171,9 @@ namespace ImageSharp.Drawing.Pens
private readonly float[] pattern;
private readonly float totalLength;
public PatternPenApplicator(PixelAccessor<TPixel> sourcePixels, IBrush<TPixel> brush, RectangleF region, float width, float[] pattern, GraphicsOptions options)
public PatternPenApplicator(ImageBase<TPixel> source, IBrush<TPixel> brush, RectangleF region, float width, float[] pattern, GraphicsOptions options)
{
this.brush = brush.CreateApplicator(sourcePixels, region, options);
this.brush = brush.CreateApplicator(source, region, options);
this.halfWidth = width / 2;
this.totalLength = 0;
@ -200,7 +200,7 @@ namespace ImageSharp.Drawing.Pens
public override ColoredPointInfo<TPixel> GetColor(int x, int y, PointInfo info)
{
ColoredPointInfo<TPixel> infoResult = default(ColoredPointInfo<TPixel>);
var infoResult = default(ColoredPointInfo<TPixel>);
infoResult.DistanceFromElement = float.MaxValue; // is really outside the element
float length = info.DistanceAlongPath % this.totalLength;

9
src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs

@ -56,8 +56,7 @@ namespace ImageSharp.Drawing.Processors
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
{
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
using (PenApplicator<TPixel> applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds, this.Options))
using (PenApplicator<TPixel> applicator = this.Pen.CreateApplicator(source, this.Path.Bounds, this.Options))
{
Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion);
@ -99,8 +98,8 @@ namespace ImageSharp.Drawing.Processors
{
int offsetY = y - polyStartY;
using (Buffer<float> amount = new Buffer<float>(width))
using (Buffer<TPixel> colors = new Buffer<TPixel>(width))
using (var amount = new Buffer<float>(width))
using (var colors = new Buffer<TPixel>(width))
{
for (int i = 0; i < width; i++)
{
@ -112,7 +111,7 @@ namespace ImageSharp.Drawing.Processors
colors[i] = color.Color;
}
Span<TPixel> destination = sourcePixels.GetRowSpan(offsetY).Slice(minX - startX, width);
Span<TPixel> destination = source.GetRowSpan(offsetY).Slice(minX - startX, width);
blender.Blend(destination, destination, colors, amount);
}
});

7
src/ImageSharp.Drawing/Processors/FillProcessor.cs

@ -66,12 +66,11 @@ namespace ImageSharp.Drawing.Processors
int width = maxX - minX;
// we could possibly do some optermising by having knowledge about the individual brushes operate
// We could possibly do some optimization by having knowledge about the individual brushes operate
// for example If brush is SolidBrush<TPixel> then we could just get the color upfront
// and skip using the IBrushApplicator<TPixel>?.
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
using (Buffer<float> amount = new Buffer<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle, this.options))
using (var amount = new Buffer<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options))
{
for (int i = 0; i < width; i++)
{

7
src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs

@ -7,6 +7,7 @@ namespace ImageSharp.Drawing.Processors
{
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using Drawing;
using ImageSharp.Memory;
@ -89,12 +90,11 @@ namespace ImageSharp.Drawing.Processors
}
}
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(sourcePixels, rect, this.Options))
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
{
float[] buffer = arrayPool.Rent(maxIntersections);
int scanlineWidth = maxX - minX;
using (Buffer<float> scanline = new Buffer<float>(scanlineWidth))
using (var scanline = new Buffer<float>(scanlineWidth))
{
try
{
@ -193,6 +193,7 @@ namespace ImageSharp.Drawing.Processors
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(float[] data, int left, int right)
{
float tmp = data[left];

14
src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs

@ -71,7 +71,7 @@ namespace ImageSharp.Dithering
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dither<TPixel>(PixelAccessor<TPixel> pixels, TPixel source, TPixel transformed, int x, int y, int width, int height)
public void Dither<TPixel>(ImageBase<TPixel> pixels, TPixel source, TPixel transformed, int x, int y, int width, int height)
where TPixel : struct, IPixel<TPixel>
{
this.Dither(pixels, source, transformed, x, y, width, height, true);
@ -79,13 +79,13 @@ namespace ImageSharp.Dithering
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dither<TPixel>(PixelAccessor<TPixel> pixels, TPixel source, TPixel transformed, int x, int y, int width, int height, bool replacePixel)
public void Dither<TPixel>(ImageBase<TPixel> image, TPixel source, TPixel transformed, int x, int y, int width, int height, bool replacePixel)
where TPixel : struct, IPixel<TPixel>
{
if (replacePixel)
{
// Assign the transformed pixel to the array.
pixels[x, y] = transformed;
image[x, y] = transformed;
}
// Calculate the error
@ -111,14 +111,14 @@ namespace ImageSharp.Dithering
continue;
}
Vector4 coefficientVector = new Vector4(coefficient);
Vector4 offsetColor = pixels[matrixX, matrixY].ToVector4();
var coefficientVector = new Vector4(coefficient);
var offsetColor = image[matrixX, matrixY].ToVector4();
Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor;
result.W = offsetColor.W;
TPixel packed = default(TPixel);
var packed = default(TPixel);
packed.PackFromVector4(result);
pixels[matrixX, matrixY] = packed;
image[matrixX, matrixY] = packed;
}
}
}

8
src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs

@ -15,7 +15,7 @@ namespace ImageSharp.Dithering
/// <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="image">The image</param>
/// <param name="source">The source pixel</param>
/// <param name="transformed">The transformed pixel</param>
/// <param name="x">The column index.</param>
@ -23,13 +23,13 @@ namespace ImageSharp.Dithering
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
void Dither<TPixel>(PixelAccessor<TPixel> pixels, TPixel source, TPixel transformed, int x, int y, int width, int height)
void Dither<TPixel>(ImageBase<TPixel> image, TPixel source, TPixel transformed, int x, int y, int width, int height)
where TPixel : struct, IPixel<TPixel>;
/// <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="image">The image</param>
/// <param name="source">The source pixel</param>
/// <param name="transformed">The transformed pixel</param>
/// <param name="x">The column index.</param>
@ -41,7 +41,7 @@ namespace ImageSharp.Dithering
/// Generally this would be true for standard two-color dithering but when used in conjunction with color quantization this should be false.
/// </param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
void Dither<TPixel>(PixelAccessor<TPixel> pixels, TPixel source, TPixel transformed, int x, int y, int width, int height, bool replacePixel)
void Dither<TPixel>(ImageBase<TPixel> image, TPixel source, TPixel transformed, int x, int y, int width, int height, bool replacePixel)
where TPixel : struct, IPixel<TPixel>;
}
}

4
src/ImageSharp/Dithering/Ordered/IOrderedDither.cs

@ -15,7 +15,7 @@ namespace ImageSharp.Dithering
/// <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="image">The image</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>
@ -26,7 +26,7 @@ namespace ImageSharp.Dithering
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
void Dither<TPixel>(PixelAccessor<TPixel> pixels, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y, int width, int height)
void Dither<TPixel>(ImageBase<TPixel> image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y, int width, int height)
where TPixel : struct, IPixel<TPixel>;
}
}

6
src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs

@ -28,14 +28,14 @@ namespace ImageSharp.Dithering.Ordered
}
/// <inheritdoc />
public void Dither<TPixel>(PixelAccessor<TPixel> pixels, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y, int width, int height)
public void Dither<TPixel>(ImageBase<TPixel> image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y, int width, int height)
where TPixel : struct, IPixel<TPixel>
{
// TODO: This doesn't really cut it for me.
// I'd rather be using float but we need to add some sort of movalization vector methods to all IPixel implementations
// I'd rather be using float but we need to add some sort of normalization vector methods to all IPixel implementations
// before we can do that as the vectors all cover different ranges.
source.ToXyzwBytes(bytes, 0);
pixels[x, y] = this.matrix[y % 3, x % 3] >= bytes[index] ? lower : upper;
image[x, y] = this.matrix[y % 3, x % 3] >= bytes[index] ? lower : upper;
}
}
}

9
src/ImageSharp/Image/IImageBase{TPixel}.cs

@ -21,14 +21,5 @@ namespace ImageSharp
/// of the array for calculations. Use Width * Height.
/// </summary>
TPixel[] Pixels { get; }
/// <summary>
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <returns>The <see cref="PixelAccessor{TPixel}"/></returns>
PixelAccessor<TPixel> Lock();
}
}

118
src/ImageSharp/Image/ImageBase{TPixel}.cs

@ -7,6 +7,7 @@ namespace ImageSharp
{
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
@ -36,6 +37,11 @@ namespace ImageSharp
/// </summary>
private TPixel[] pixelBuffer;
/// <summary>
/// The span representing the pixel buffer
/// </summary>
private Span<TPixel> span;
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
/// </summary>
@ -129,6 +135,67 @@ namespace ImageSharp
/// </summary>
public Configuration Configuration { get; private set; }
/// <summary>
/// Gets or sets the pixel at the specified position.
/// </summary>
/// <param name="x">The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image.</param>
/// <param name="y">The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image.</param>
/// <returns>The <see typeparam="TPixel"/> at the specified position.</returns>
public TPixel this[int x, int y]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
this.CheckCoordinates(x, y);
return this.pixelBuffer[(y * this.Width) + x];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.CheckCoordinates(x, y);
this.pixelBuffer[(y * this.Width) + x] = value;
}
}
/// <summary>
/// Gets a reference to the pixel at the specified position.
/// </summary>
/// <param name="x">The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image.</param>
/// <param name="y">The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image.</param>
/// <returns>The <see typeparam="TPixel"/> at the specified position.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TPixel GetPixelReference(int x, int y)
{
this.CheckCoordinates(x, y);
return ref this.pixelBuffer[(y * this.Width) + x];
}
/// <summary>
/// Gets a <see cref="Span{TPixal}"/> representing the row 'y' beginning from the the first pixel on that row.
/// </summary>
/// <param name="y">The y-coordinate of the pixel row. Must be greater than or equal to zero and less than the height of the image.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<TPixel> GetRowSpan(int y)
{
this.CheckCoordinates(y);
return this.span.Slice(y * this.Width, this.Width);
}
/// <summary>
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// </summary>
/// <param name="x">The x coordinate (position in the row)</param>
/// <param name="y">The y (row) coordinate</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<TPixel> GetRowSpan(int x, int y)
{
this.CheckCoordinates(x, y);
return this.span.Slice((y * this.Width) + x, this.Width - x);
}
/// <summary>
/// Applies the processor.
/// </summary>
@ -152,8 +219,14 @@ namespace ImageSharp
GC.SuppressFinalize(this);
}
/// <inheritdoc/>
public PixelAccessor<TPixel> Lock()
/// <summary>
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <returns>The <see cref="PixelAccessor{TPixel}"/></returns>
internal PixelAccessor<TPixel> Lock()
{
return new PixelAccessor<TPixel>(this);
}
@ -174,6 +247,7 @@ namespace ImageSharp
this.Width = newWidth;
this.Height = newHeight;
this.pixelBuffer = newPixels;
this.span = new Span<TPixel>(this.pixelBuffer);
}
/// <summary>
@ -225,6 +299,7 @@ namespace ImageSharp
private void RentPixels()
{
this.pixelBuffer = PixelDataPool<TPixel>.Rent(this.Width * this.Height);
this.span = new Span<TPixel>(this.pixelBuffer);
}
/// <summary>
@ -234,6 +309,7 @@ namespace ImageSharp
{
PixelDataPool<TPixel>.Return(this.pixelBuffer);
this.pixelBuffer = null;
this.span = null;
}
/// <summary>
@ -243,5 +319,43 @@ namespace ImageSharp
{
Array.Clear(this.pixelBuffer, 0, this.Width * this.Height);
}
/// <summary>
/// Checks the coordinates to ensure they are within bounds.
/// </summary>
/// <param name="y">The y-coordinate of the pixel. Must be greater than zero and less than the height of the image.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the coordinates are not within the bounds of the image.
/// </exception>
[Conditional("DEBUG")]
private void CheckCoordinates(int y)
{
if (y < 0 || y >= this.Height)
{
throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the image bounds.");
}
}
/// <summary>
/// Checks the coordinates to ensure they are within bounds.
/// </summary>
/// <param name="x">The x-coordinate of the pixel. Must be greater than zero and less than the width of the image.</param>
/// <param name="y">The y-coordinate of the pixel. Must be greater than zero and less than the height of the image.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the coordinates are not within the bounds of the image.
/// </exception>
[Conditional("DEBUG")]
private void CheckCoordinates(int x, int y)
{
if (x < 0 || x >= this.Width)
{
throw new ArgumentOutOfRangeException(nameof(x), x, $"{x} is outwith the image bounds.");
}
if (y < 0 || y >= this.Height)
{
throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the image bounds.");
}
}
}
}

4
src/ImageSharp/Image/PixelAccessor{TPixel}.cs

@ -17,7 +17,7 @@ namespace ImageSharp
/// Provides per-pixel access to generic <see cref="Image{TPixel}"/> pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed class PixelAccessor<TPixel> : IDisposable, IBuffer2D<TPixel>
internal sealed class PixelAccessor<TPixel> : IDisposable, IBuffer2D<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
@ -128,12 +128,14 @@ namespace ImageSharp
/// <returns>The <see typeparam="TPixel"/> at the specified position.</returns>
public TPixel this[int x, int y]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
this.CheckCoordinates(x, y);
return this.PixelArray[(y * this.Width) + x];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.CheckCoordinates(x, y);

2
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -29,7 +29,7 @@ namespace ImageSharp.Memory
}
/// <summary>
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="y">The y (row) coordinate</param>

17
src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs

@ -85,18 +85,15 @@ namespace ImageSharp.Processing.Processors
startY = 0;
}
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
for (int y = minY; y < maxY; y++)
{
for (int y = minY; y < maxY; y++)
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetX = x - startX;
TPixel sourceColor = sourcePixels[offsetX, offsetY];
TPixel transformedColor = sourceColor.ToVector4().X >= this.Threshold ? this.UpperColor : this.LowerColor;
this.Diffuser.Dither(sourcePixels, sourceColor, transformedColor, offsetX, offsetY, maxX, maxY);
}
int offsetX = x - startX;
TPixel sourceColor = source[offsetX, offsetY];
TPixel transformedColor = sourceColor.ToVector4().X >= this.Threshold ? this.UpperColor : this.LowerColor;
this.Diffuser.Dither(source, sourceColor, transformedColor, offsetX, offsetY, maxX, maxY);
}
}
}

21
src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs

@ -93,21 +93,16 @@ namespace ImageSharp.Processing.Processors
startY = 0;
}
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
byte[] bytes = new byte[4];
for (int y = minY; y < maxY; y++)
{
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;
TPixel sourceColor = sourcePixels[offsetX, offsetY];
this.Dither.Dither(sourcePixels, sourceColor, this.UpperColor, this.LowerColor, bytes, this.Index, offsetX, offsetY, maxX, maxY);
}
int offsetY = y - startY;
ArrayPool<byte>.Shared.Return(bytes);
for (int x = minX; x < maxX; x++)
{
int offsetX = x - startX;
TPixel sourceColor = source[offsetX, offsetY];
this.Dither.Dither(source, sourceColor, this.UpperColor, this.LowerColor, bytes, this.Index, offsetX, offsetY, maxX, maxY);
}
}
}

4
src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs

@ -66,7 +66,7 @@ namespace ImageSharp.Quantizers
}
/// <inheritdoc/>
protected override void SecondPass(PixelAccessor<TPixel> source, byte[] output, int width, int height)
protected override void SecondPass(ImageBase<TPixel> source, byte[] output, int width, int height)
{
// Load up the values for the first pixel. We can use these to speed up the second
// pass of the algorithm by avoiding transforming rows of identical color.
@ -490,7 +490,7 @@ namespace ImageSharp.Quantizers
byte b = (this.blue / this.pixelCount).ToByte();
// And set the color of the palette entry
TPixel pixel = default(TPixel);
var pixel = default(TPixel);
pixel.PackFromBytes(r, g, b, 255);
palette[index] = pixel;

4
src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs

@ -51,7 +51,7 @@ namespace ImageSharp.Quantizers
for (int i = 0; i < constants.Length; i++)
{
constants[i].ToXyzwBytes(this.pixelBuffer, 0);
TPixel packed = default(TPixel);
var packed = default(TPixel);
packed.PackFromBytes(this.pixelBuffer[0], this.pixelBuffer[1], this.pixelBuffer[2], this.pixelBuffer[3]);
safe[i] = packed;
}
@ -72,7 +72,7 @@ namespace ImageSharp.Quantizers
}
/// <inheritdoc/>
protected override void SecondPass(PixelAccessor<TPixel> source, byte[] output, int width, int height)
protected override void SecondPass(ImageBase<TPixel> source, byte[] output, int width, int height)
{
// Load up the values for the first pixel. We can use these to speed up the second
// pass of the algorithm by avoiding transforming rows of identical color.

45
src/ImageSharp/Quantizers/Quantizer{TPixel}.cs

@ -54,35 +54,30 @@ namespace ImageSharp.Quantizers
int height = image.Height;
int width = image.Width;
byte[] quantizedPixels = new byte[width * height];
TPixel[] colorPalette;
using (PixelAccessor<TPixel> pixels = image.Lock())
// 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)
{
// 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(pixels, width, height);
}
this.FirstPass(image, width, height);
}
// Collect the palette. Required before the second pass runs.
colorPalette = this.GetPalette();
// Collect the palette. Required before the second pass runs.
TPixel[] colorPalette = this.GetPalette();
if (this.Dither)
{
// We clone the image as we don't want to alter the original.
using (Image<TPixel> clone = new Image<TPixel>(image))
using (PixelAccessor<TPixel> clonedPixels = clone.Lock())
{
this.SecondPass(clonedPixels, quantizedPixels, width, height);
}
}
else
if (this.Dither)
{
// We clone the image as we don't want to alter the original.
using (var clone = new Image<TPixel>(image))
{
this.SecondPass(pixels, quantizedPixels, width, height);
this.SecondPass(clone, quantizedPixels, width, height);
}
}
else
{
this.SecondPass(image, quantizedPixels, width, height);
}
return new QuantizedImage<TPixel>(width, height, colorPalette, quantizedPixels);
}
@ -93,7 +88,7 @@ namespace ImageSharp.Quantizers
/// <param name="source">The source data</param>
/// <param name="width">The width in pixels of the image.</param>
/// <param name="height">The height in pixels of the image.</param>
protected virtual void FirstPass(PixelAccessor<TPixel> source, int width, int height)
protected virtual void FirstPass(ImageBase<TPixel> source, int width, int height)
{
// Loop through each row
for (int y = 0; y < height; y++)
@ -114,7 +109,7 @@ namespace ImageSharp.Quantizers
/// <param name="output">The output pixel array</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(PixelAccessor<TPixel> source, byte[] output, int width, int height);
protected abstract void SecondPass(ImageBase<TPixel> source, byte[] output, int width, int height);
/// <summary>
/// Override this to process the pixel in the first pass of the algorithm
@ -155,7 +150,7 @@ namespace ImageSharp.Quantizers
// Not found - loop through the palette and find the nearest match.
byte colorIndex = 0;
float leastDistance = int.MaxValue;
Vector4 vector = pixel.ToVector4();
var vector = pixel.ToVector4();
for (int index = 0; index < colorPalette.Length; index++)
{

6
src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs

@ -183,7 +183,7 @@ namespace ImageSharp.Quantizers
float b = Volume(this.colorCube[k], this.vmb) / weight;
float a = Volume(this.colorCube[k], this.vma) / weight;
TPixel color = default(TPixel);
var color = default(TPixel);
color.PackFromVector4(new Vector4(r, g, b, a) / 255F);
this.palette[k] = color;
}
@ -221,7 +221,7 @@ namespace ImageSharp.Quantizers
}
/// <inheritdoc/>
protected override void FirstPass(PixelAccessor<TPixel> source, int width, int height)
protected override void FirstPass(ImageBase<TPixel> source, int width, int height)
{
// Build up the 3-D color histogram
// Loop through each row
@ -240,7 +240,7 @@ namespace ImageSharp.Quantizers
}
/// <inheritdoc/>
protected override void SecondPass(PixelAccessor<TPixel> source, byte[] output, int width, int height)
protected override void SecondPass(ImageBase<TPixel> source, byte[] output, int width, int height)
{
// Load up the values for the first pixel. We can use these to speed up the second
// pass of the algorithm by avoiding transforming rows of identical color.

Loading…
Cancel
Save