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> /// <summary>
/// Creates the applicator for this brush. /// Creates the applicator for this brush.
/// </summary> /// </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="region">The region the brush will be applied to.</param>
/// <param name="options">The graphic options</param> /// <param name="options">The graphic options</param>
/// <returns> /// <returns>
@ -32,6 +32,6 @@ namespace ImageSharp.Drawing
/// The <paramref name="region" /> when being applied to things like shapes would usually be the /// 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 /// bounding box of the shape not necessarily the bounds of the whole image
/// </remarks> /// </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 namespace ImageSharp.Drawing.Brushes
{ {
using System; using System;
using System.Numerics;
using ImageSharp.Memory; using ImageSharp.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
@ -22,21 +21,21 @@ namespace ImageSharp.Drawing.Brushes
/// <summary> /// <summary>
/// The image to paint. /// The image to paint.
/// </summary> /// </summary>
private readonly IImageBase<TPixel> image; private readonly ImageBase<TPixel> image;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageBrush{TPixel}"/> class. /// Initializes a new instance of the <see cref="ImageBrush{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="image">The image.</param> /// <param name="image">The image.</param>
public ImageBrush(IImageBase<TPixel> image) public ImageBrush(ImageBase<TPixel> image)
{ {
this.image = image; this.image = image;
} }
/// <inheritdoc /> /// <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> /// <summary>
@ -45,9 +44,9 @@ namespace ImageSharp.Drawing.Brushes
private class ImageBrushApplicator : BrushApplicator<TPixel> private class ImageBrushApplicator : BrushApplicator<TPixel>
{ {
/// <summary> /// <summary>
/// The source pixel accessor. /// The source image.
/// </summary> /// </summary>
private readonly PixelAccessor<TPixel> source; private readonly ImageBase<TPixel> source;
/// <summary> /// <summary>
/// The y-length. /// The y-length.
@ -72,20 +71,14 @@ namespace ImageSharp.Drawing.Brushes
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageBrushApplicator"/> class. /// Initializes a new instance of the <see cref="ImageBrushApplicator"/> class.
/// </summary> /// </summary>
/// <param name="image"> /// <param name="target">The target image.</param>
/// The image. /// <param name="image">The image.</param>
/// </param> /// <param name="region">The region.</param>
/// <param name="region">
/// The region.
/// </param>
/// <param name="options">The options</param> /// <param name="options">The options</param>
/// <param name="sourcePixels"> public ImageBrushApplicator(ImageBase<TPixel> target, ImageBase<TPixel> image, RectangleF region, GraphicsOptions options)
/// The sourcePixels. : base(target, options)
/// </param>
public ImageBrushApplicator(PixelAccessor<TPixel> sourcePixels, IImageBase<TPixel> image, RectangleF region, GraphicsOptions options)
: base(sourcePixels, options)
{ {
this.source = image.Lock(); this.source = image;
this.xLength = image.Width; this.xLength = image.Width;
this.yLength = image.Height; this.yLength = image.Height;
this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0); this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0);
@ -119,9 +112,9 @@ namespace ImageSharp.Drawing.Brushes
/// <inheritdoc /> /// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y) internal override void Apply(Span<float> scanline, int x, int y)
{ {
// create a span for colors // Create a span for colors
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length)) using (var amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length)) using (var overlay = new Buffer<TPixel>(scanline.Length))
{ {
int sourceY = (y - this.offsetY) % this.yLength; int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX; 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> /// <param name="pattern">The pattern.</param>
internal PatternBrush(TPixel foreColor, TPixel backColor, Fast2DArray<bool> pattern) internal PatternBrush(TPixel foreColor, TPixel backColor, Fast2DArray<bool> pattern)
{ {
Vector4 foreColorVector = foreColor.ToVector4(); var foreColorVector = foreColor.ToVector4();
Vector4 backColorVector = backColor.ToVector4(); var backColorVector = backColor.ToVector4();
this.pattern = new Fast2DArray<TPixel>(pattern.Width, pattern.Height); this.pattern = new Fast2DArray<TPixel>(pattern.Width, pattern.Height);
this.patternVector = new Fast2DArray<Vector4>(pattern.Width, pattern.Height); this.patternVector = new Fast2DArray<Vector4>(pattern.Width, pattern.Height);
for (int i = 0; i < pattern.Data.Length; i++) for (int i = 0; i < pattern.Data.Length; i++)
@ -92,9 +92,9 @@ namespace ImageSharp.Drawing.Brushes
} }
/// <inheritdoc /> /// <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> /// <summary>
@ -111,12 +111,12 @@ namespace ImageSharp.Drawing.Brushes
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PatternBrushApplicator" /> class. /// Initializes a new instance of the <see cref="PatternBrushApplicator" /> class.
/// </summary> /// </summary>
/// <param name="sourcePixels">The sourcePixels.</param> /// <param name="source">The source image.</param>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="patternVector">The patternVector.</param> /// <param name="patternVector">The patternVector.</param>
/// <param name="options">The options</param> /// <param name="options">The options</param>
public PatternBrushApplicator(PixelAccessor<TPixel> sourcePixels, Fast2DArray<TPixel> pattern, Fast2DArray<Vector4> patternVector, GraphicsOptions options) public PatternBrushApplicator(ImageBase<TPixel> source, Fast2DArray<TPixel> pattern, Fast2DArray<Vector4> patternVector, GraphicsOptions options)
: base(sourcePixels, options) : base(source, options)
{ {
this.pattern = pattern; this.pattern = pattern;
this.patternVector = patternVector; this.patternVector = patternVector;
@ -152,8 +152,8 @@ namespace ImageSharp.Drawing.Brushes
internal override void Apply(Span<float> scanline, int x, int y) internal override void Apply(Span<float> scanline, int x, int y)
{ {
int patternY = y % this.pattern.Height; int patternY = y % this.pattern.Height;
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length)) using (var amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length)) using (var overlay = new Buffer<TPixel>(scanline.Length))
{ {
for (int i = 0; i < scanline.Length; i++) for (int i = 0; i < scanline.Length; i++)
{ {

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

@ -6,7 +6,6 @@
namespace ImageSharp.Drawing.Processors namespace ImageSharp.Drawing.Processors
{ {
using System; using System;
using System.Numerics;
using ImageSharp.Memory; using ImageSharp.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
@ -24,7 +23,7 @@ namespace ImageSharp.Drawing.Processors
/// </summary> /// </summary>
/// <param name="target">The target.</param> /// <param name="target">The target.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
internal BrushApplicator(PixelAccessor<TPixel> target, GraphicsOptions options) internal BrushApplicator(ImageBase<TPixel> target, GraphicsOptions options)
{ {
this.Target = target; this.Target = target;
@ -41,7 +40,7 @@ namespace ImageSharp.Drawing.Processors
/// <summary> /// <summary>
/// Gets the destinaion /// Gets the destinaion
/// </summary> /// </summary>
protected PixelAccessor<TPixel> Target { get; } protected ImageBase<TPixel> Target { get; }
/// <summary> /// <summary>
/// Gets the blend percentage /// 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> /// <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) internal virtual void Apply(Span<float> scanline, int x, int y)
{ {
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length)) using (var amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length)) using (var overlay = new Buffer<TPixel>(scanline.Length))
{ {
for (int i = 0; i < scanline.Length; i++) 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; } public TPixel TargeTPixel { get; }
/// <inheritdoc /> /// <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> /// <summary>
@ -87,22 +87,22 @@ namespace ImageSharp.Drawing.Brushes
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RecolorBrushApplicator" /> class. /// Initializes a new instance of the <see cref="RecolorBrushApplicator" /> class.
/// </summary> /// </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="sourceColor">Color of the source.</param>
/// <param name="targetColor">Color of the target.</param> /// <param name="targetColor">Color of the target.</param>
/// <param name="threshold">The threshold .</param> /// <param name="threshold">The threshold .</param>
/// <param name="options">The options</param> /// <param name="options">The options</param>
public RecolorBrushApplicator(PixelAccessor<TPixel> sourcePixels, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) public RecolorBrushApplicator(ImageBase<TPixel> source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options)
: base(sourcePixels, options) : base(source, options)
{ {
this.sourceColor = sourceColor.ToVector4(); this.sourceColor = sourceColor.ToVector4();
this.targetColor = targetColor.ToVector4(); this.targetColor = targetColor.ToVector4();
this.targetColorPixel = targetColor; 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 :) // Lets hack a min max extreams for a color space by letting the IPackedPixel clamp our values to something in the correct spaces :)
TPixel maxColor = default(TPixel); var maxColor = default(TPixel);
maxColor.PackFromVector4(new Vector4(float.MaxValue)); maxColor.PackFromVector4(new Vector4(float.MaxValue));
TPixel minColor = default(TPixel); var minColor = default(TPixel);
minColor.PackFromVector4(new Vector4(float.MinValue)); minColor.PackFromVector4(new Vector4(float.MinValue));
this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold; 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) // Offset the requested pixel by the value in the rectangle (the shapes position)
TPixel result = this.Target[x, y]; TPixel result = this.Target[x, y];
Vector4 background = result.ToVector4(); var background = result.ToVector4();
float distance = Vector4.DistanceSquared(background, this.sourceColor); float distance = Vector4.DistanceSquared(background, this.sourceColor);
if (distance <= this.threshold) if (distance <= this.threshold)
{ {
@ -144,8 +144,8 @@ namespace ImageSharp.Drawing.Brushes
/// <inheritdoc /> /// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y) internal override void Apply(Span<float> scanline, int x, int y)
{ {
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length)) using (var amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length)) using (var overlay = new Buffer<TPixel>(scanline.Length))
{ {
for (int i = 0; i < scanline.Length; i++) 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; public TPixel Color => this.color;
/// <inheritdoc /> /// <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> /// <summary>
@ -55,13 +55,13 @@ namespace ImageSharp.Drawing.Brushes
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SolidBrushApplicator"/> class. /// Initializes a new instance of the <see cref="SolidBrushApplicator"/> class.
/// </summary> /// </summary>
/// <param name="source">The source image.</param>
/// <param name="color">The color.</param> /// <param name="color">The color.</param>
/// <param name="options">The options</param> /// <param name="options">The options</param>
/// <param name="sourcePixels">The sourcePixels.</param> public SolidBrushApplicator(ImageBase<TPixel> source, TPixel color, GraphicsOptions options)
public SolidBrushApplicator(PixelAccessor<TPixel> sourcePixels, TPixel color, GraphicsOptions options) : base(source, options)
: base(sourcePixels, options)
{ {
this.Colors = new Buffer<TPixel>(sourcePixels.Width); this.Colors = new Buffer<TPixel>(source.Width);
for (int i = 0; i < this.Colors.Length; i++) for (int i = 0; i < this.Colors.Length; i++)
{ {
this.Colors[i] = color; this.Colors[i] = color;
@ -94,7 +94,7 @@ namespace ImageSharp.Drawing.Brushes
{ {
Span<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); 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++) for (int i = 0; i < scanline.Length; i++)
{ {

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

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

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

@ -56,8 +56,7 @@ namespace ImageSharp.Drawing.Processors
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle) protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
{ {
using (PixelAccessor<TPixel> sourcePixels = source.Lock()) using (PenApplicator<TPixel> applicator = this.Pen.CreateApplicator(source, this.Path.Bounds, this.Options))
using (PenApplicator<TPixel> applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds, this.Options))
{ {
Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion); Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion);
@ -99,8 +98,8 @@ namespace ImageSharp.Drawing.Processors
{ {
int offsetY = y - polyStartY; int offsetY = y - polyStartY;
using (Buffer<float> amount = new Buffer<float>(width)) using (var amount = new Buffer<float>(width))
using (Buffer<TPixel> colors = new Buffer<TPixel>(width)) using (var colors = new Buffer<TPixel>(width))
{ {
for (int i = 0; i < width; i++) for (int i = 0; i < width; i++)
{ {
@ -112,7 +111,7 @@ namespace ImageSharp.Drawing.Processors
colors[i] = color.Color; 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); 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; 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 // for example If brush is SolidBrush<TPixel> then we could just get the color upfront
// and skip using the IBrushApplicator<TPixel>?. // and skip using the IBrushApplicator<TPixel>?.
using (PixelAccessor<TPixel> sourcePixels = source.Lock()) using (var amount = new Buffer<float>(width))
using (Buffer<float> amount = new Buffer<float>(width)) using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle, this.options))
{ {
for (int i = 0; i < width; i++) 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;
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices;
using Drawing; using Drawing;
using ImageSharp.Memory; using ImageSharp.Memory;
@ -89,12 +90,11 @@ namespace ImageSharp.Drawing.Processors
} }
} }
using (PixelAccessor<TPixel> sourcePixels = source.Lock()) using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(sourcePixels, rect, this.Options))
{ {
float[] buffer = arrayPool.Rent(maxIntersections); float[] buffer = arrayPool.Rent(maxIntersections);
int scanlineWidth = maxX - minX; int scanlineWidth = maxX - minX;
using (Buffer<float> scanline = new Buffer<float>(scanlineWidth)) using (var scanline = new Buffer<float>(scanlineWidth))
{ {
try try
{ {
@ -193,6 +193,7 @@ namespace ImageSharp.Drawing.Processors
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Swap(float[] data, int left, int right) private static void Swap(float[] data, int left, int right)
{ {
float tmp = data[left]; float tmp = data[left];

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

@ -71,7 +71,7 @@ namespace ImageSharp.Dithering
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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> where TPixel : struct, IPixel<TPixel>
{ {
this.Dither(pixels, source, transformed, x, y, width, height, true); this.Dither(pixels, source, transformed, x, y, width, height, true);
@ -79,13 +79,13 @@ namespace ImageSharp.Dithering
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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> where TPixel : struct, IPixel<TPixel>
{ {
if (replacePixel) if (replacePixel)
{ {
// Assign the transformed pixel to the array. // Assign the transformed pixel to the array.
pixels[x, y] = transformed; image[x, y] = transformed;
} }
// Calculate the error // Calculate the error
@ -111,14 +111,14 @@ namespace ImageSharp.Dithering
continue; continue;
} }
Vector4 coefficientVector = new Vector4(coefficient); var coefficientVector = new Vector4(coefficient);
Vector4 offsetColor = pixels[matrixX, matrixY].ToVector4(); var offsetColor = image[matrixX, matrixY].ToVector4();
Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor; Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor;
result.W = offsetColor.W; result.W = offsetColor.W;
TPixel packed = default(TPixel); var packed = default(TPixel);
packed.PackFromVector4(result); 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> /// <summary>
/// Transforms the image applying the dither matrix. This method alters the input pixels array /// Transforms the image applying the dither matrix. This method alters the input pixels array
/// </summary> /// </summary>
/// <param name="pixels">The pixel accessor </param> /// <param name="image">The image</param>
/// <param name="source">The source pixel</param> /// <param name="source">The source pixel</param>
/// <param name="transformed">The transformed pixel</param> /// <param name="transformed">The transformed pixel</param>
/// <param name="x">The column index.</param> /// <param name="x">The column index.</param>
@ -23,13 +23,13 @@ namespace ImageSharp.Dithering
/// <param name="width">The image width.</param> /// <param name="width">The image width.</param>
/// <param name="height">The image height.</param> /// <param name="height">The image height.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <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>; where TPixel : struct, IPixel<TPixel>;
/// <summary> /// <summary>
/// Transforms the image applying the dither matrix. This method alters the input pixels array /// Transforms the image applying the dither matrix. This method alters the input pixels array
/// </summary> /// </summary>
/// <param name="pixels">The pixel accessor </param> /// <param name="image">The image</param>
/// <param name="source">The source pixel</param> /// <param name="source">The source pixel</param>
/// <param name="transformed">The transformed pixel</param> /// <param name="transformed">The transformed pixel</param>
/// <param name="x">The column index.</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. /// Generally this would be true for standard two-color dithering but when used in conjunction with color quantization this should be false.
/// </param> /// </param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <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>; where TPixel : struct, IPixel<TPixel>;
} }
} }

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

@ -15,7 +15,7 @@ namespace ImageSharp.Dithering
/// <summary> /// <summary>
/// Transforms the image applying the dither matrix. This method alters the input pixels array /// Transforms the image applying the dither matrix. This method alters the input pixels array
/// </summary> /// </summary>
/// <param name="pixels">The pixel accessor </param> /// <param name="image">The image</param>
/// <param name="source">The source pixel</param> /// <param name="source">The source pixel</param>
/// <param name="upper">The color to apply to the pixels above the threshold.</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="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="width">The image width.</param>
/// <param name="height">The image height.</param> /// <param name="height">The image height.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <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>; where TPixel : struct, IPixel<TPixel>;
} }
} }

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

@ -28,14 +28,14 @@ namespace ImageSharp.Dithering.Ordered
} }
/// <inheritdoc /> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
// TODO: This doesn't really cut it for me. // 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. // before we can do that as the vectors all cover different ranges.
source.ToXyzwBytes(bytes, 0); 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. /// of the array for calculations. Use Width * Height.
/// </summary> /// </summary>
TPixel[] Pixels { get; } 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;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices;
using ImageSharp.Memory; using ImageSharp.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
@ -36,6 +37,11 @@ namespace ImageSharp
/// </summary> /// </summary>
private TPixel[] pixelBuffer; private TPixel[] pixelBuffer;
/// <summary>
/// The span representing the pixel buffer
/// </summary>
private Span<TPixel> span;
/// <summary> /// <summary>
/// A value indicating whether this instance of the given entity has been disposed. /// A value indicating whether this instance of the given entity has been disposed.
/// </summary> /// </summary>
@ -129,6 +135,67 @@ namespace ImageSharp
/// </summary> /// </summary>
public Configuration Configuration { get; private set; } 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> /// <summary>
/// Applies the processor. /// Applies the processor.
/// </summary> /// </summary>
@ -152,8 +219,14 @@ namespace ImageSharp
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
/// <inheritdoc/> /// <summary>
public PixelAccessor<TPixel> Lock() /// 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); return new PixelAccessor<TPixel>(this);
} }
@ -174,6 +247,7 @@ namespace ImageSharp
this.Width = newWidth; this.Width = newWidth;
this.Height = newHeight; this.Height = newHeight;
this.pixelBuffer = newPixels; this.pixelBuffer = newPixels;
this.span = new Span<TPixel>(this.pixelBuffer);
} }
/// <summary> /// <summary>
@ -225,6 +299,7 @@ namespace ImageSharp
private void RentPixels() private void RentPixels()
{ {
this.pixelBuffer = PixelDataPool<TPixel>.Rent(this.Width * this.Height); this.pixelBuffer = PixelDataPool<TPixel>.Rent(this.Width * this.Height);
this.span = new Span<TPixel>(this.pixelBuffer);
} }
/// <summary> /// <summary>
@ -234,6 +309,7 @@ namespace ImageSharp
{ {
PixelDataPool<TPixel>.Return(this.pixelBuffer); PixelDataPool<TPixel>.Return(this.pixelBuffer);
this.pixelBuffer = null; this.pixelBuffer = null;
this.span = null;
} }
/// <summary> /// <summary>
@ -243,5 +319,43 @@ namespace ImageSharp
{ {
Array.Clear(this.pixelBuffer, 0, this.Width * this.Height); 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. /// Provides per-pixel access to generic <see cref="Image{TPixel}"/> pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
@ -128,12 +128,14 @@ namespace ImageSharp
/// <returns>The <see typeparam="TPixel"/> at the specified position.</returns> /// <returns>The <see typeparam="TPixel"/> at the specified position.</returns>
public TPixel this[int x, int y] public TPixel this[int x, int y]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get get
{ {
this.CheckCoordinates(x, y); this.CheckCoordinates(x, y);
return this.PixelArray[(y * this.Width) + x]; return this.PixelArray[(y * this.Width) + x];
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set set
{ {
this.CheckCoordinates(x, y); this.CheckCoordinates(x, y);

2
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -29,7 +29,7 @@ namespace ImageSharp.Memory
} }
/// <summary> /// <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> /// </summary>
/// <param name="buffer">The buffer</param> /// <param name="buffer">The buffer</param>
/// <param name="y">The y (row) coordinate</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; 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; int offsetX = x - startX;
for (int x = minX; x < maxX; x++) TPixel sourceColor = source[offsetX, offsetY];
{ TPixel transformedColor = sourceColor.ToVector4().X >= this.Threshold ? this.UpperColor : this.LowerColor;
int offsetX = x - startX; this.Diffuser.Dither(source, sourceColor, transformedColor, offsetX, offsetY, maxX, maxY);
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);
}
} }
} }
} }

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

@ -93,21 +93,16 @@ namespace ImageSharp.Processing.Processors
startY = 0; 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;
{
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);
}
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/> /// <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 // 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. // 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(); byte b = (this.blue / this.pixelCount).ToByte();
// And set the color of the palette entry // And set the color of the palette entry
TPixel pixel = default(TPixel); var pixel = default(TPixel);
pixel.PackFromBytes(r, g, b, 255); pixel.PackFromBytes(r, g, b, 255);
palette[index] = pixel; 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++) for (int i = 0; i < constants.Length; i++)
{ {
constants[i].ToXyzwBytes(this.pixelBuffer, 0); 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]); packed.PackFromBytes(this.pixelBuffer[0], this.pixelBuffer[1], this.pixelBuffer[2], this.pixelBuffer[3]);
safe[i] = packed; safe[i] = packed;
} }
@ -72,7 +72,7 @@ namespace ImageSharp.Quantizers
} }
/// <inheritdoc/> /// <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 // 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. // 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 height = image.Height;
int width = image.Width; int width = image.Width;
byte[] quantizedPixels = new byte[width * height]; 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. this.FirstPass(image, width, height);
// 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);
}
// Collect the palette. Required before the second pass runs. // Collect the palette. Required before the second pass runs.
colorPalette = this.GetPalette(); TPixel[] colorPalette = this.GetPalette();
if (this.Dither) if (this.Dither)
{ {
// We clone the image as we don't want to alter the original. // We clone the image as we don't want to alter the original.
using (Image<TPixel> clone = new Image<TPixel>(image)) using (var clone = new Image<TPixel>(image))
using (PixelAccessor<TPixel> clonedPixels = clone.Lock())
{
this.SecondPass(clonedPixels, quantizedPixels, width, height);
}
}
else
{ {
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); return new QuantizedImage<TPixel>(width, height, colorPalette, quantizedPixels);
} }
@ -93,7 +88,7 @@ namespace ImageSharp.Quantizers
/// <param name="source">The source data</param> /// <param name="source">The source data</param>
/// <param name="width">The width in pixels of the image.</param> /// <param name="width">The width in pixels of the image.</param>
/// <param name="height">The height 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 // Loop through each row
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
@ -114,7 +109,7 @@ namespace ImageSharp.Quantizers
/// <param name="output">The output pixel array</param> /// <param name="output">The output pixel array</param>
/// <param name="width">The width in pixels of the image</param> /// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height 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> /// <summary>
/// Override this to process the pixel in the first pass of the algorithm /// 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. // Not found - loop through the palette and find the nearest match.
byte colorIndex = 0; byte colorIndex = 0;
float leastDistance = int.MaxValue; float leastDistance = int.MaxValue;
Vector4 vector = pixel.ToVector4(); var vector = pixel.ToVector4();
for (int index = 0; index < colorPalette.Length; index++) 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 b = Volume(this.colorCube[k], this.vmb) / weight;
float a = Volume(this.colorCube[k], this.vma) / 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); color.PackFromVector4(new Vector4(r, g, b, a) / 255F);
this.palette[k] = color; this.palette[k] = color;
} }
@ -221,7 +221,7 @@ namespace ImageSharp.Quantizers
} }
/// <inheritdoc/> /// <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 // Build up the 3-D color histogram
// Loop through each row // Loop through each row
@ -240,7 +240,7 @@ namespace ImageSharp.Quantizers
} }
/// <inheritdoc/> /// <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 // 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. // pass of the algorithm by avoiding transforming rows of identical color.

Loading…
Cancel
Save