Browse Source

wip - pixel data only on image frames

pull/326/head
Scott Williams 9 years ago
parent
commit
07e9f23dec
  1. 2
      src/ImageSharp.Drawing/Brushes/IBrush.cs
  2. 10
      src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
  3. 4
      src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
  4. 4
      src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
  5. 4
      src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
  6. 4
      src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
  7. 4
      src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
  8. 4
      src/ImageSharp.Drawing/Processors/FillProcessor.cs
  9. 2
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  10. 21
      src/ImageSharp/Advanced/IPixelSource{TPixel}.cs
  11. 29
      src/ImageSharp/Advanced/ImageExtensions.cs
  12. 12
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  13. 4
      src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
  14. 4
      src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs
  15. 2
      src/ImageSharp/Dithering/Ordered/IOrderedDither.cs
  16. 2
      src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs
  17. 10
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  18. 8
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  19. 37
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  20. 2
      src/ImageSharp/Formats/IImageDecoder.cs
  21. 2
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
  22. 2
      src/ImageSharp/Formats/Png/PngDecoder.cs
  23. 8
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  24. 2
      src/ImageSharp/Image/ICloningImageProcessor.cs
  25. 19
      src/ImageSharp/Image/IImage.cs
  26. 28
      src/ImageSharp/Image/IImageBase.cs
  27. 34
      src/ImageSharp/Image/IImageFrame.cs
  28. 2
      src/ImageSharp/Image/IImageProcessor.cs
  29. 333
      src/ImageSharp/Image/ImageBase{TPixel}.cs
  30. 50
      src/ImageSharp/Image/ImageExtensions.cs
  31. 148
      src/ImageSharp/Image/ImageFrameCollection.cs
  32. 220
      src/ImageSharp/Image/ImageFrame{TPixel}.cs
  33. 193
      src/ImageSharp/Image/Image{TPixel}.cs
  34. 36
      src/ImageSharp/Image/PixelAccessorExtensions.cs
  35. 39
      src/ImageSharp/Image/PixelAccessor{TPixel}.cs
  36. 6
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
  37. 4
      src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs
  38. 4
      src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs
  39. 18
      src/ImageSharp/Processing/Processors/CloningImageProcessor.cs
  40. 4
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs
  41. 2
      src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs
  42. 2
      src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs
  43. 2
      src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
  44. 4
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
  45. 6
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
  46. 4
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
  47. 4
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs
  48. 10
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
  49. 4
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs
  50. 2
      src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
  51. 2
      src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs
  52. 2
      src/ImageSharp/Processing/Processors/DelegateProcessor.cs
  53. 4
      src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs
  54. 4
      src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs
  55. 4
      src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs
  56. 4
      src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs
  57. 4
      src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs
  58. 4
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs
  59. 4
      src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs
  60. 10
      src/ImageSharp/Processing/Processors/ImageProcessor.cs
  61. 4
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  62. 4
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
  63. 2
      src/ImageSharp/Processing/Processors/Transforms/AutoRotateProcessor.cs
  64. 4
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs
  65. 4
      src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs
  66. 10
      src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs
  67. 2
      src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs
  68. 4
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs
  69. 19
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  70. 20
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  71. 6
      src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
  72. 12
      src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs
  73. 6
      src/ImageSharp/Processing/Transforms/Resize.cs
  74. 2
      src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs
  75. 4
      src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs
  76. 4
      src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs
  77. 2
      src/ImageSharp/Quantizers/Quantize.cs
  78. 8
      src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
  79. 6
      src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs
  80. 4
      tests/ImageSharp.Benchmarks/Samplers/Glow.cs
  81. 4
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs
  82. 22
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs
  83. 8
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs
  84. 2
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs
  85. 14
      tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs

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

@ -32,6 +32,6 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// 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(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options);
BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options);
}
}

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

@ -21,19 +21,19 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <summary>
/// The image to paint.
/// </summary>
private readonly ImageBase<TPixel> image;
private readonly ImageFrame<TPixel> image;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrush{TPixel}"/> class.
/// </summary>
/// <param name="image">The image.</param>
public ImageBrush(ImageBase<TPixel> image)
public ImageBrush(ImageFrame<TPixel> image)
{
this.image = image;
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options)
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
{
return new ImageBrushApplicator(source, this.image, region, options);
}
@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <summary>
/// The source image.
/// </summary>
private readonly ImageBase<TPixel> source;
private readonly ImageFrame<TPixel> source;
/// <summary>
/// The y-length.
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <param name="image">The image.</param>
/// <param name="region">The region.</param>
/// <param name="options">The options</param>
public ImageBrushApplicator(ImageBase<TPixel> target, ImageBase<TPixel> image, RectangleF region, GraphicsOptions options)
public ImageBrushApplicator(ImageFrame<TPixel> target, ImageFrame<TPixel> image, RectangleF region, GraphicsOptions options)
: base(target, options)
{
this.source = image;

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

@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options)
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
{
return new PatternBrushApplicator(source, this.pattern, this.patternVector, options);
}
@ -115,7 +115,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <param name="pattern">The pattern.</param>
/// <param name="patternVector">The patternVector.</param>
/// <param name="options">The options</param>
public PatternBrushApplicator(ImageBase<TPixel> source, Fast2DArray<TPixel> pattern, Fast2DArray<Vector4> patternVector, GraphicsOptions options)
public PatternBrushApplicator(ImageFrame<TPixel> source, Fast2DArray<TPixel> pattern, Fast2DArray<Vector4> patternVector, GraphicsOptions options)
: base(source, options)
{
this.pattern = pattern;

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

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors
/// </summary>
/// <param name="target">The target.</param>
/// <param name="options">The options.</param>
internal BrushApplicator(ImageBase<TPixel> target, GraphicsOptions options)
internal BrushApplicator(ImageFrame<TPixel> target, GraphicsOptions options)
{
this.Target = target;
@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors
/// <summary>
/// Gets the destinaion
/// </summary>
protected ImageBase<TPixel> Target { get; }
protected ImageFrame<TPixel> Target { get; }
/// <summary>
/// Gets the blend percentage

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

@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
public TPixel TargeTPixel { get; }
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options)
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
{
return new RecolorBrushApplicator(source, this.SourceColor, this.TargeTPixel, this.Threshold, options);
}
@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <param name="targetColor">Color of the target.</param>
/// <param name="threshold">The threshold .</param>
/// <param name="options">The options</param>
public RecolorBrushApplicator(ImageBase<TPixel> source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options)
public RecolorBrushApplicator(ImageFrame<TPixel> source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options)
: base(source, options)
{
this.sourceColor = sourceColor.ToVector4();

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

@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
public TPixel Color => this.color;
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageBase<TPixel> source, RectangleF region, GraphicsOptions options)
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
{
return new SolidBrushApplicator(source, this.color, options);
}
@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <param name="source">The source image.</param>
/// <param name="color">The color.</param>
/// <param name="options">The options</param>
public SolidBrushApplicator(ImageBase<TPixel> source, TPixel color, GraphicsOptions options)
public SolidBrushApplicator(ImageFrame<TPixel> source, TPixel color, GraphicsOptions options)
: base(source, options)
{
this.Colors = new Buffer<TPixel>(source.Width);

4
src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs

@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
public Point Location { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
Image<TPixel> disposableImage = null;
Image<TPixel> targetImage = this.Image;
@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> background = sourcePixels.GetRowSpan(y).Slice(minX, width);

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

@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
int offsetY = y - startY;

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

@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
public GraphicsOptions Options { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
Region region = this.Region;
Rectangle rect = region.Bounds;

21
src/ImageSharp/Advanced/IPixelSource{TPixel}.cs

@ -1,21 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Allows access to the pixels as an area of contiguous memory in the given pixel format.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
internal interface IPixelSource<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets the representation of the pixels as an area of contiguous memory in the given pixel format.
/// </summary>
Span<TPixel> Span { get; }
}
}

29
src/ImageSharp/Advanced/ImageExtensions.cs

@ -22,10 +22,31 @@ namespace SixLabors.ImageSharp.Advanced
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelSpan<TPixel>(this ImageBase<TPixel> source)
public static Span<TPixel> GetPixelSpan<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> GetSpan(source);
/// <summary>
/// Gets the representation of the pixels as an area of contiguous memory in the given pixel format.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelSpan<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> GetSpan(source);
/// <summary>
/// Gets a <see cref="Span{TPixal}"/> representing the row 'y' beginning from the the first pixel on that row.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelRowSpan<TPixel>(this ImageFrame<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>
=> GetSpan(source).Slice(row * source.Width, source.Width);
/// <summary>
/// Gets a <see cref="Span{TPixal}"/> representing the row 'y' beginning from the the first pixel on that row.
/// </summary>
@ -33,7 +54,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="source">The source.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelRowSpan<TPixel>(this ImageBase<TPixel> source, int row)
public static Span<TPixel> GetPixelRowSpan<TPixel>(this Image<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>
=> GetSpan(source).Slice(row * source.Width, source.Width);
@ -43,8 +64,8 @@ namespace SixLabors.ImageSharp.Advanced
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <returns>The span retuned from Pixel source</returns>
private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source)
private static Span<TPixel> GetSpan<TPixel>(IImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.Span;
=> source.PixelBuffer.Span;
}
}

12
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -150,7 +150,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
public static Rectangle GetFilteredBoundingRectangle<TPixel>(ImageBase<TPixel> bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B)
public static Rectangle GetFilteredBoundingRectangle<TPixel>(ImageFrame<TPixel> bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B)
where TPixel : struct, IPixel<TPixel>
{
int width = bitmap.Width;
@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp
var topLeft = default(Point);
var bottomRight = default(Point);
Func<ImageBase<TPixel>, int, int, float, bool> delegateFunc;
Func<ImageFrame<TPixel>, int, int, float, bool> delegateFunc;
// Determine which channel to check against
switch (channel)
@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp
break;
}
int GetMinY(ImageBase<TPixel> pixels)
int GetMinY(ImageFrame<TPixel> pixels)
{
for (int y = 0; y < height; y++)
{
@ -196,7 +196,7 @@ namespace SixLabors.ImageSharp
return 0;
}
int GetMaxY(ImageBase<TPixel> pixels)
int GetMaxY(ImageFrame<TPixel> pixels)
{
for (int y = height - 1; y > -1; y--)
{
@ -212,7 +212,7 @@ namespace SixLabors.ImageSharp
return height;
}
int GetMinX(ImageBase<TPixel> pixels)
int GetMinX(ImageFrame<TPixel> pixels)
{
for (int x = 0; x < width; x++)
{
@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp
return 0;
}
int GetMaxX(ImageBase<TPixel> pixels)
int GetMaxX(ImageFrame<TPixel> pixels)
{
for (int x = width - 1; x > -1; x--)
{

4
src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs

@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Dithering.Base
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dither<TPixel>(ImageBase<TPixel> pixels, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
public void Dither<TPixel>(ImageFrame<TPixel> pixels, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
where TPixel : struct, IPixel<TPixel>
{
this.Dither(pixels, source, transformed, x, y, minX, minY, maxX, maxY, true);
@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Dithering.Base
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dither<TPixel>(ImageBase<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel)
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel)
where TPixel : struct, IPixel<TPixel>
{
if (replacePixel)

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

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Dithering
/// <param name="maxX">The maximum column value.</param>
/// <param name="maxY">The maximum row value.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
void Dither<TPixel>(ImageBase<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
@ -43,7 +43,7 @@ namespace SixLabors.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>(ImageBase<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel)
void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel)
where TPixel : struct, IPixel<TPixel>;
}
}

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

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Dithering
/// <param name="x">The column index.</param>
/// <param name="y">The row index.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
void Dither<TPixel>(ImageBase<TPixel> image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y)
void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y)
where TPixel : struct, IPixel<TPixel>;
}
}

2
src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs

@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Dithering.Base
}
/// <inheritdoc />
public void Dither<TPixel>(ImageBase<TPixel> image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y)
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y)
where TPixel : struct, IPixel<TPixel>
{
// TODO: This doesn't really cut it for me.

10
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -33,12 +33,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageBase{TPixel}"/>.
/// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TPixel}"/> to encode from.</param>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public void Encode<TPixel>(ImageBase<TPixel> image, Stream stream)
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(image, nameof(image));
@ -125,9 +125,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="image">
/// The <see cref="ImageBase{TPixel}"/> containing pixel data.
/// The <see cref="ImageFrame{TPixel}"/> containing pixel data.
/// </param>
private void WriteImage<TPixel>(EndianBinaryWriter writer, ImageBase<TPixel> image)
private void WriteImage<TPixel>(EndianBinaryWriter writer, IImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())

8
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -369,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
ImageFrame<TPixel> currentFrame = null;
ImageBase<TPixel> image;
ImageFrame<TPixel> image;
if (this.previousFrame == null)
{
@ -378,7 +378,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.SetFrameMetaData(this.metaData);
image = this.image;
image = (ImageFrame<TPixel>)this.image;
}
else
{
@ -471,7 +471,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
return;
}
this.previousFrame = currentFrame == null ? this.image.ToFrame() : currentFrame;
this.previousFrame = currentFrame == null ? this.image : currentFrame;
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
@ -484,7 +484,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Restores the current frame area to the background.
/// </summary>
/// <param name="frame">The frame.</param>
private void RestoreToBackground(ImageBase<TPixel> frame)
private void RestoreToBackground(ImageFrame<TPixel> frame)
{
if (this.restoreArea == null)
{

37
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(paletteSize);
this.hasFrames = image.Frames.Any();
this.hasFrames = image.Frames.Count > 1;
// Dithering when animating gifs is a bad idea as we introduce pixel tearing across frames.
var ditheredQuantizer = (IQuantizer<TPixel>)this.quantizer;
@ -114,28 +114,29 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.WriteLogicalScreenDescriptor(image, writer, index);
// Write the first frame.
this.WriteGraphicalControlExtension(image.MetaData, writer, index);
this.WriteComments(image, writer);
this.WriteImageDescriptor(image, writer);
this.WriteColorTable(quantized, writer);
this.WriteImageData(quantized, writer);
// Write additional frames.
if (this.hasFrames)
{
this.WriteApplicationExtension(writer, image.MetaData.RepeatCount, image.Frames.Count);
}
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 0; i < image.Frames.Count; i++)
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame<TPixel> frame = image.Frames[i];
if (quantized == null)
{
ImageFrame<TPixel> frame = image.Frames[i];
QuantizedImage<TPixel> quantizedFrame = ditheredQuantizer.Quantize(frame, paletteSize);
this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantizedFrame));
this.WriteImageDescriptor(frame, writer);
this.WriteColorTable(quantizedFrame, writer);
this.WriteImageData(quantizedFrame, writer);
quantized = ditheredQuantizer.Quantize(frame, paletteSize);
}
this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantized));
this.WriteImageDescriptor(frame, writer);
this.WriteColorTable(quantized, writer);
this.WriteImageData(quantized, writer);
quantized = null; // so next frame can regenerate it
}
// TODO: Write extension etc
@ -253,7 +254,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Writes the image comments to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TPixel}"/> to be encoded.</param>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to be encoded.</param>
/// <param name="writer">The stream to write to.</param>
private void WriteComments<TPixel>(Image<TPixel> image, EndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>
@ -321,9 +322,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Writes the image descriptor to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TPixel}"/> to be encoded.</param>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to be encoded.</param>
/// <param name="writer">The stream to write to.</param>
private void WriteImageDescriptor<TPixel>(ImageBase<TPixel> image, EndianBinaryWriter writer)
private void WriteImageDescriptor<TPixel>(ImageFrame<TPixel> image, EndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>
{
writer.Write(GifConstants.ImageDescriptorLabel); // 2c
@ -347,7 +348,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Writes the color table to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TPixel}"/> to encode.</param>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode.</param>
/// <param name="writer">The writer to write to the stream with.</param>
private void WriteColorTable<TPixel>(QuantizedImage<TPixel> image, EndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>

2
src/ImageSharp/Formats/IImageDecoder.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats
public interface IImageDecoder
{
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase{TPixel}"/>.
/// Decodes the image from the specified stream to the <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration for the image.</param>

2
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// (4) Packing <see cref="Image{TPixel}"/> pixels from the <see cref="Vector4"/> buffer. <br/>
/// These operations are executed in <see cref="NumberOfPostProcessorSteps"/> steps.
/// <see cref="PixelRowsPerStep"/> image rows are converted in one step,
/// which means that size of the allocated memory is limited (does not depend on <see cref="ImageBase{TPixel}.Height"/>).
/// which means that size of the allocated memory is limited (does not depend on <see cref="ImageFrame{TPixel}.Height"/>).
/// </summary>
internal class JpegImagePostProcessor : IDisposable
{

2
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Png
public Encoding TextEncoding { get; set; } = PngConstants.DefaultEncoding;
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase{TPixel}"/>.
/// Decodes the image from the specified stream to the <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration for the image.</param>

8
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TPixel}"/> to encode from.</param>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Png
// Collect the indexed pixel data
if (this.pngColorType == PngColorType.Palette)
{
this.CollectIndexedBytes(image, stream, header);
this.CollectIndexedBytes<TPixel>(image, stream, header);
}
this.WritePhysicalChunk(stream, image);
@ -304,7 +304,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="image">The image to encode.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="header">The <see cref="PngHeader"/>.</param>
private void CollectIndexedBytes<TPixel>(ImageBase<TPixel> image, Stream stream, PngHeader header)
private void CollectIndexedBytes<TPixel>(ImageFrame<TPixel> image, Stream stream, PngHeader header)
where TPixel : struct, IPixel<TPixel>
{
// Quantize the image and get the pixels.
@ -529,7 +529,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="header">The <see cref="PngHeader"/>.</param>
/// <param name="image">The image to encode.</param>
/// <returns>The <see cref="QuantizedImage{TPixel}"/></returns>
private QuantizedImage<TPixel> WritePaletteChunk<TPixel>(Stream stream, PngHeader header, ImageBase<TPixel> image)
private QuantizedImage<TPixel> WritePaletteChunk<TPixel>(Stream stream, PngHeader header, ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (this.paletteSize > 256)

2
src/ImageSharp/Image/ICloningImageProcessor.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TPixel}"/>.
/// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">

19
src/ImageSharp/Image/IImage.cs

@ -1,19 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Encapsulates the basic properties and methods required to manipulate images.
/// </summary>
internal interface IImage : IImageBase
{
/// <summary>
/// Gets the meta data of the image.
/// </summary>
ImageMetaData MetaData { get; }
}
}

28
src/ImageSharp/Image/IImageBase.cs

@ -1,28 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Encapsulates the basic properties and methods required to manipulate images.
/// </summary>
public interface IImageBase
{
/// <summary>
/// Gets the width in pixels.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height in pixels.
/// </summary>
int Height { get; }
/// <summary>
/// Gets the configuration providing initialization code which allows extending the library.
/// </summary>
Configuration Configuration { get; }
}
}

34
src/ImageSharp/Image/IImageFrame.cs

@ -1,18 +1,50 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Encapsulates the basic properties and methods required to manipulate images.
/// </summary>
internal interface IImageFrame : IImageBase
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
internal interface IImageFrame<TPixel> : IImageFrame
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets the parent.
/// </summary>
Image<TPixel> Parent { get; }
/// <summary>
/// Gets the pixel buffer.
/// </summary>
Buffer2D<TPixel> PixelBuffer { get; }
}
/// <summary>
/// Encapsulates the basic properties and methods required to manipulate images.
/// </summary>
public interface IImageFrame : IDisposable
{
/// <summary>
/// Gets the meta data of the image.
/// </summary>
ImageFrameMetaData MetaData { get; }
/// <summary>
/// Gets the width.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height.
/// </summary>
int Height { get; }
}
}

2
src/ImageSharp/Image/IImageProcessor.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TPixel}"/>.
/// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">

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

@ -1,333 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
/// <summary>
/// The base class of all images. Encapsulates the basic properties and methods required to manipulate
/// images in different pixel formats.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public abstract class ImageBase<TPixel> : IImageBase, IDisposable, IPixelSource<TPixel>
where TPixel : struct, IPixel<TPixel>
{
#pragma warning disable SA1401 // Fields must be private
/// <summary>
/// The image pixels. Not private as Buffer2D requires an array in its constructor.
/// </summary>
internal TPixel[] PixelBuffer;
#pragma warning restore SA1401 // Fields must be private
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
/// </summary>
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
/// <remarks>
/// If the entity is disposed, it must not be disposed a second time. The isDisposed field is set the first time the entity
/// is disposed. If the isDisposed field is true, then the Dispose() method will not dispose again. This help not to prolong the entity's
/// life in the Garbage Collector.
/// </remarks>
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TPixel}"/> class.
/// </summary>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
protected ImageBase(Configuration configuration)
{
this.Configuration = configuration ?? Configuration.Default;
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TPixel}"/> class.
/// </summary>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
protected ImageBase(Configuration configuration, int width, int height)
: this(configuration)
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.Width = width;
this.Height = height;
this.RentPixels();
this.ClearPixels();
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TPixel}"/> class.
/// </summary>
/// <param name="other">
/// The other <see cref="ImageBase{TPixel}"/> to create this instance from.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// Thrown if the given <see cref="ImageBase{TPixel}"/> is null.
/// </exception>
protected ImageBase(ImageBase<TPixel> other)
: this(other.Configuration)
{
Guard.NotNull(other, nameof(other), "Other image cannot be null.");
this.Width = other.Width;
this.Height = other.Height;
this.CopyProperties(other);
// Rent then copy the pixels. Unsafe.CopyBlock gives us a nice speed boost here.
this.RentPixels();
other.GetPixelSpan().CopyTo(this.GetPixelSpan());
}
/// <inheritdoc/>
Span<TPixel> IPixelSource<TPixel>.Span => new Span<TPixel>(this.PixelBuffer, 0, this.Width * this.Height);
/// <inheritdoc/>
public int Width { get; private set; }
/// <inheritdoc/>
public int Height { get; private set; }
/// <summary>
/// Gets the configuration providing initialization code which allows extending the library.
/// </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)]
internal ref TPixel GetPixelReference(int x, int y)
{
this.CheckCoordinates(x, y);
return ref this.PixelBuffer[(y * this.Width) + x];
}
/// <summary>
/// Clones the image
/// </summary>
/// <returns>A new items which is a clone of the original.</returns>
public ImageBase<TPixel> Clone()
{
return this.CloneImageBase();
}
/// <inheritdoc />
public void Dispose()
{
this.Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SuppressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
/// <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);
}
/// <summary>
/// Copies the pixels to another <see cref="PixelAccessor{TPixel}"/> of the same size.
/// </summary>
/// <param name="target">The target pixel buffer accessor.</param>
internal void CopyTo(PixelAccessor<TPixel> target)
{
SpanHelper.Copy(this.GetPixelSpan(), target.PixelBuffer.Span);
}
/// <summary>
/// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer.
/// </summary>
/// <param name="pixelSource">The pixel source.</param>
internal void SwapPixelsBuffers(PixelAccessor<TPixel> pixelSource)
{
Guard.NotNull(pixelSource, nameof(pixelSource));
int newWidth = pixelSource.Width;
int newHeight = pixelSource.Height;
// Push my memory into the accessor (which in turn unpins the old buffer ready for the images use)
TPixel[] newPixels = pixelSource.ReturnCurrentColorsAndReplaceThemInternally(this.Width, this.Height, this.PixelBuffer);
this.Width = newWidth;
this.Height = newHeight;
this.PixelBuffer = newPixels;
}
/// <summary>
/// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer.
/// </summary>
/// <param name="pixelSource">The pixel source.</param>
internal void SwapPixelsData(ImageBase<TPixel> pixelSource)
{
Guard.NotNull(pixelSource, nameof(pixelSource));
int newWidth = pixelSource.Width;
int newHeight = pixelSource.Height;
TPixel[] newPixels = pixelSource.PixelBuffer;
pixelSource.PixelBuffer = this.PixelBuffer;
pixelSource.Width = this.Width;
pixelSource.Height = this.Height;
this.Width = newWidth;
this.Height = newHeight;
this.PixelBuffer = newPixels;
}
/// <summary>
/// Clones the image
/// </summary>
/// <returns>A new items which is a clone of the original.</returns>
protected abstract ImageBase<TPixel> CloneImageBase();
/// <summary>
/// Copies the properties from the other <see cref="IImageBase"/>.
/// </summary>
/// <param name="other">
/// The other <see cref="IImageBase"/> to copy the properties from.
/// </param>
protected void CopyProperties(IImageBase other)
{
DebugGuard.NotNull(other, nameof(other));
this.Configuration = other.Configuration;
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
/// <param name="disposing">If true, the object gets disposed.</param>
protected virtual void Dispose(bool disposing)
{
if (this.isDisposed)
{
return;
}
if (disposing)
{
this.ReturnPixels();
}
// Note disposing is done.
this.isDisposed = true;
}
/// <summary>
/// Rents the pixel array from the pool.
/// </summary>
private void RentPixels()
{
this.PixelBuffer = PixelDataPool<TPixel>.Rent(this.Width * this.Height);
}
/// <summary>
/// Returns the rented pixel array back to the pool.
/// </summary>
private void ReturnPixels()
{
PixelDataPool<TPixel>.Return(this.PixelBuffer);
this.PixelBuffer = null;
}
/// <summary>
/// Clears the pixel array.
/// </summary>
private void ClearPixels()
{
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.");
}
}
}
}

50
src/ImageSharp/Image/ImageExtensions.cs

@ -24,7 +24,27 @@ namespace SixLabors.ImageSharp
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>Returns the bounds of the image</returns>
public static Rectangle Bounds<TPixel>(this ImageBase<TPixel> source)
public static Configuration Configuration<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.Parent?.Configuration ?? SixLabors.ImageSharp.Configuration.Default;
/// <summary>
/// Gets the bounds of the image.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>Returns the bounds of the image</returns>
public static Rectangle Bounds<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> new Rectangle(0, 0, source.Width, source.Height);
/// <summary>
/// Gets the bounds of the image.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>Returns the bounds of the image</returns>
public static Rectangle Bounds<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> new Rectangle(0, 0, source.Width, source.Height);
@ -34,7 +54,17 @@ namespace SixLabors.ImageSharp
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>Returns the bounds of the image</returns>
public static Size Size<TPixel>(this ImageBase<TPixel> source)
public static Size Size<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> new Size(source.Width, source.Height);
/// <summary>
/// Gets the size of the image.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>Returns the bounds of the image</returns>
public static Size Size<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> new Size(source.Width, source.Height);
@ -131,6 +161,22 @@ namespace SixLabors.ImageSharp
source.Save(stream, encoder);
}
/// <summary>
/// Saves the raw image to the given bytes.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <param name="buffer">The buffer to save the raw pixel data to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, Span<byte> buffer)
where TPixel : struct, IPixel<TPixel>
{
Span<byte> byteBuffer = source.GetPixelSpan().AsBytes();
Guard.MustBeGreaterThanOrEqualTo(buffer.Length, byteBuffer.Length, nameof(buffer));
byteBuffer.CopyTo(buffer);
}
/// <summary>
/// Saves the raw image to the given bytes.
/// </summary>

148
src/ImageSharp/Image/ImageFrameCollection.cs

@ -0,0 +1,148 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Encapsulates an imaged collection of frames.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
public sealed class ImageFrameCollection<TPixel> : IEnumerable<ImageFrame<TPixel>>
where TPixel : struct, IPixel<TPixel>
{
private IList<ImageFrame<TPixel>> frames = new List<ImageFrame<TPixel>>();
private readonly Image<TPixel> parent;
internal ImageFrameCollection(Image<TPixel> parent)
{
this.parent = parent;
}
/// <summary>
/// Gets the count.
/// </summary>
public int Count { get => this.frames.Count; }
/// <summary>
/// Gets or sets the <see cref="ImageFrame{TPixel}"/> at the specified index.
/// </summary>
/// <value>
/// The <see cref="ImageFrame{TPixel}"/>.
/// </value>
/// <param name="index">The index.</param>
/// <returns>The <see cref="ImageFrame{TPixel}"/> at the specified index.</returns>
public ImageFrame<TPixel> this[int index]
{
get
{
return this.frames[index];
}
set
{
this.ValidateFrameSize(value);
this.frames[index] = value;
}
}
/// <summary>
/// Determines the index of a specific <paramref name="frame"/> in the <seealso cref="Image{TPixel}"/>.
/// </summary>
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to locate in the <seealso cref="Image{TPixel}"/>.</param>
/// <returns>The index of item if found in the list; otherwise, -1.</returns>
public int IndexOf(ImageFrame<TPixel> frame) => this.frames.IndexOf(frame);
/// <summary>
/// Inserts the <paramref name="frame"/> to the <seealso cref="Image{TPixel}"/> at the specified <paramref name="index"/>.
/// </summary>
/// <param name="index"> The zero-based index at which item should be inserted..</param>
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to insert into the <seealso cref="Image{TPixel}"/>.</param>
public void Insert(int index, ImageFrame<TPixel> frame)
{
this.ValidateFrameSize(frame);
this.frames.Insert(index, frame);
}
/// <summary>
/// Removes the <seealso cref="ImageFrame{TPixel}"/> from the <seealso cref="Image{TPixel}"/> at the specified index.
/// </summary>
/// <param name="index">The zero-based index of the item to remove.</param>
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
public void RemoveAt(int index)
{
if (index > 0 || this.frames.Count > 1)
{
this.frames.RemoveAt(index);
}
throw new InvalidOperationException("Cannot remove last frame.");
}
/// <summary>
/// Adds the specified frame.
/// </summary>
/// <param name="frame">The frame.</param>
/// <exception cref="ArgumentException">Frame must have the same dimensions as the image - frame</exception>
public void Add(ImageFrame<TPixel> frame)
{
this.ValidateFrameSize(frame);
this.frames.Add(frame);
}
private void ValidateFrameSize(ImageFrame<TPixel> frame)
{
if (this.parent.Width != frame.Width || this.parent.Height != frame.Height)
{
throw new ArgumentException("Frame must have the same dimensions as the image", nameof(frame));
}
}
/// <summary>
/// Determines whether the <seealso cref="Image{TPixel}"/> contains the <paramref name="frame"/>.
/// </summary>
/// <param name="frame">The frame.</param>
/// <returns>
/// <c>true</c> if the <seealso cref="Image{TPixel}"/> the specified frame; otherwise, <c>false</c>.
/// </returns>
public bool Contains(ImageFrame<TPixel> frame)
{
return this.frames.Contains(frame);
}
/// <summary>
/// Removes the specified frame.
/// </summary>
/// <param name="frame">The frame.</param>
/// <returns>true if item is found in the <seealso cref="Image{TPixel}"/>; otherwise,</returns>
/// <exception cref="InvalidOperationException">Cannot remove last frame</exception>
public bool Remove(ImageFrame<TPixel> frame)
{
if (this.frames.Count == 1 && this.frames.Contains(frame))
{
throw new InvalidOperationException("Cannot remove last frame");
}
return this.frames.Remove(frame);
}
/// <inheritdoc/>
IEnumerator<ImageFrame<TPixel>> IEnumerable<ImageFrame<TPixel>>.GetEnumerator() => this.frames.GetEnumerator();
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.frames).GetEnumerator();
}
}

220
src/ImageSharp/Image/ImageFrame{TPixel}.cs

@ -2,8 +2,12 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
@ -13,74 +17,194 @@ namespace SixLabors.ImageSharp
/// Represents a single frame in a animation.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed class ImageFrame<TPixel> : ImageBase<TPixel>, IImageFrame
public sealed class ImageFrame<TPixel> : IImageFrame<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}"/> class.
/// The image pixels. Not private as Buffer2D requires an array in its constructor.
/// </summary>
private Buffer2D<TPixel> pixelBuffer;
private bool isDisposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
public ImageFrame(Configuration configuration, int width, int height)
: base(configuration, width, height)
internal ImageFrame(int width, int height)
: this(width, height, new ImageFrameMetaData())
{
this.MetaData = new ImageFrameMetaData();
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}"/> class.
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The metadata of the frame.</param>
public ImageFrame(Configuration configuration, int width, int height, ImageFrameMetaData metadata)
: base(configuration, width, height)
/// <param name="metaData">The meta data.</param>
internal ImageFrame(int width, int height, ImageFrameMetaData metaData)
{
Guard.NotNull(metadata, nameof(metadata));
this.MetaData = metadata;
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
Guard.NotNull(metaData, nameof(metaData));
this.Width = width;
this.Height = height;
this.pixelBuffer = Buffer2D<TPixel>.CreateClean(width, height);
this.MetaData = metaData;
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}"/> class.
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
public ImageFrame(int width, int height)
: this(null, width, height)
/// <param name="source">The source.</param>
internal ImageFrame(ImageFrame<TPixel> source)
{
this.Width = source.Width;
this.Height = source.Height;
this.pixelBuffer = new Buffer2D<TPixel>(source.Width, source.Height);
source.pixelBuffer.Span.CopyTo(this.pixelBuffer.Span);
this.MetaData = source.MetaData.Clone();
}
/// <inheritdoc/>
Buffer2D<TPixel> IImageFrame<TPixel>.PixelBuffer => this.pixelBuffer;
/// <inheritdoc/>
public int Width { get; private set; }
/// <inheritdoc/>
public int Height { get; private set; }
/// <summary>
/// Gets the configuration providing initialization code which allows extending the library.
/// </summary>
public Image<TPixel> Parent { get; private set; }
/// <summary>
/// Gets the meta data of the frame.
/// </summary>
public ImageFrameMetaData MetaData { 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]
{
this.MetaData = new ImageFrameMetaData();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.pixelBuffer[x, y];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.pixelBuffer[x, y] = value;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}"/> class.
/// Performs an explicit conversion from <see cref="Image{TPixel}"/> to <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <param name="image">The image to create the frame from.</param>
internal ImageFrame(ImageBase<TPixel> image)
: base(image)
/// <param name="image">The image.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator ImageFrame<TPixel>(Image<TPixel> image) => image.Frames[0];
/// <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)]
internal ref TPixel GetPixelReference(int x, int y)
{
this.MetaData = new ImageFrameMetaData();
return ref this.pixelBuffer[x, y];
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}"/> class.
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <param name="image">The image to create the frame from.</param>
private ImageFrame(ImageFrame<TPixel> image)
: base(image)
/// <returns>The <see cref="PixelAccessor{TPixel}"/></returns>
internal PixelAccessor<TPixel> Lock()
{
this.CopyProperties(image);
return new PixelAccessor<TPixel>(this);
}
/// <summary>
/// Gets the meta data of the frame.
/// Copies the pixels to another <see cref="PixelAccessor{TPixel}"/> of the same size.
/// </summary>
public ImageFrameMetaData MetaData { get; private set; }
/// <param name="target">The target pixel buffer accessor.</param>
internal void CopyTo(PixelAccessor<TPixel> target)
{
SpanHelper.Copy(this.GetPixelSpan(), target.PixelBuffer.Span);
}
/// <summary>
/// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer.
/// </summary>
/// <param name="pixelSource">The pixel source.</param>
internal void SwapPixelsBuffers(PixelAccessor<TPixel> pixelSource)
{
Guard.NotNull(pixelSource, nameof(pixelSource));
int newWidth = pixelSource.Width;
int newHeight = pixelSource.Height;
// Push my memory into the accessor (which in turn unpins the old buffer ready for the images use)
var newPixels = pixelSource.SwapBufferOwnership(this.pixelBuffer);
this.Width = newWidth;
this.Height = newHeight;
this.pixelBuffer = newPixels;
}
/// <summary>
/// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer.
/// </summary>
/// <param name="pixelSource">The pixel source.</param>
internal void SwapPixelsBuffers(ImageFrame<TPixel> pixelSource)
{
Guard.NotNull(pixelSource, nameof(pixelSource));
int newWidth = pixelSource.Width;
int newHeight = pixelSource.Height;
var newPixels = pixelSource.pixelBuffer;
pixelSource.pixelBuffer = this.pixelBuffer;
pixelSource.Width = this.Width;
pixelSource.Height = this.Height;
this.Width = newWidth;
this.Height = newHeight;
this.pixelBuffer = newPixels;
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
public void Dispose()
{
if (this.isDisposed)
{
return;
}
this.pixelBuffer?.Dispose();
this.pixelBuffer = null;
// Note disposing is done.
this.isDisposed = true;
}
/// <inheritdoc/>
public override string ToString()
@ -103,8 +227,7 @@ namespace SixLabors.ImageSharp
Func<Vector4, Vector4> scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TPixel, TPixel2>();
var target = new ImageFrame<TPixel2>(this.Configuration, this.Width, this.Height);
target.CopyProperties(this);
var target = new ImageFrame<TPixel2>(this.Width, this.Height, this.MetaData.Clone());
using (PixelAccessor<TPixel> pixels = this.Lock())
using (PixelAccessor<TPixel2> targetPixels = target.Lock())
@ -112,7 +235,7 @@ namespace SixLabors.ImageSharp
Parallel.For(
0,
target.Height,
this.Configuration.ParallelOptions,
this.Configuration().ParallelOptions,
y =>
{
for (int x = 0; x < target.Width; x++)
@ -131,28 +254,9 @@ namespace SixLabors.ImageSharp
/// Clones the current instance.
/// </summary>
/// <returns>The <see cref="ImageFrame{TPixel}"/></returns>
public new ImageFrame<TPixel> Clone()
public ImageFrame<TPixel> Clone()
{
return new ImageFrame<TPixel>(this);
}
/// <inheritdoc/>
protected override ImageBase<TPixel> CloneImageBase()
{
return this.Clone();
}
/// <summary>
/// Copies the properties from the other <see cref="IImageFrame"/>.
/// </summary>
/// <param name="other">
/// The other <see cref="IImageFrame"/> to copy the properties from.
/// </param>
private void CopyProperties(IImageFrame other)
{
base.CopyProperties(other);
this.MetaData = new ImageFrameMetaData(other.MetaData);
}
}
}

193
src/ImageSharp/Image/Image{TPixel}.cs

@ -2,13 +2,17 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
@ -20,7 +24,7 @@ namespace SixLabors.ImageSharp
/// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed class Image<TPixel> : ImageBase<TPixel>, IImage
public sealed partial class Image<TPixel> : IImageFrame<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
@ -59,41 +63,78 @@ namespace SixLabors.ImageSharp
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param>
internal Image(Configuration configuration, int width, int height, ImageMetaData metadata)
: base(configuration, width, height)
: this(configuration, width, height, metadata, null)
{
this.MetaData = metadata ?? new ImageMetaData();
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// by making a copy from another image.
/// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer.
/// </summary>
/// <param name="other">The other image, where the clone should be made from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception>
private Image(Image<TPixel> other)
: base(other)
/// <param name="pixelSource">The pixel source.</param>
internal void SwapPixelsBuffers(Image<TPixel> pixelSource)
{
foreach (ImageFrame<TPixel> frame in other.Frames)
Guard.NotNull(pixelSource, nameof(pixelSource));
this.Width = pixelSource.Width;
this.Height = pixelSource.Height;
int newHeight = pixelSource.Height;
for (int i = 0; i < this.Frames.Count; i++)
{
this.Frames[i].SwapPixelsBuffers(pixelSource.Frames[i]);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}" /> class
/// with the height and the width of the image.
/// </summary>
/// <param name="configuration">The configuration providing initialization code which allows extending the library.</param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param>
/// <param name="frames">The frames that will be owned by this image instance.</param>
internal Image(Configuration configuration, int width, int height, ImageMetaData metadata, IEnumerable<ImageFrame<TPixel>> frames)
{
this.Configuration = configuration ?? Configuration.Default;
this.Width = width;
this.Height = height;
this.MetaData = metadata ?? new ImageMetaData();
this.Frames = new ImageFrameCollection<TPixel>(this);
if (frames != null)
{
if (frame != null)
foreach (ImageFrame<TPixel> f in frames)
{
this.Frames.Add(frame.Clone());
this.Frames.Add(f);
}
}
this.CopyProperties(other);
if (this.Frames.Count == 0)
{
this.Frames.Add(new ImageFrame<TPixel>(this.Width, this.Height));
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// by making a copy from another image.
/// Gets the configuration.
/// </summary>
/// <param name="other">The other image, where the clone should be made from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception>
private Image(ImageBase<TPixel> other)
: base(other)
{
}
/// <value>
/// The configuration.
/// </value>
public Configuration Configuration { get; }
/// <summary>
/// Gets the width.
/// </summary>
public int Width { get; private set; }
/// <summary>
/// Gets the height.
/// </summary>
public int Height { get; private set; }
/// <summary>
/// Gets the meta data of the image.
@ -101,10 +142,42 @@ namespace SixLabors.ImageSharp
public ImageMetaData MetaData { get; private set; } = new ImageMetaData();
/// <summary>
/// Gets the other frames associated with this image.
/// Gets the frames.
/// </summary>
public ImageFrameCollection<TPixel> Frames { get; private set; }
/// <summary>
/// Gets the root frame.
/// </summary>
private IImageFrame<TPixel> RootFrame => this.Frames[0];
/// <inheritdoc/>
Buffer2D<TPixel> IImageFrame<TPixel>.PixelBuffer => this.RootFrame.PixelBuffer;
/// <inheritdoc/>
ImageFrameMetaData IImageFrame.MetaData => this.RootFrame.MetaData;
/// <inheritdoc/>
Image<TPixel> IImageFrame<TPixel>.Parent => this.RootFrame.Parent;
/// <summary>
/// Gets or sets the pixel at the specified position.
/// </summary>
/// <value>The list of frame images.</value>
public IList<ImageFrame<TPixel>> Frames { get; } = new List<ImageFrame<TPixel>>();
/// <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]
{
get
{
return this.RootFrame.PixelBuffer[x, y];
}
set
{
this.RootFrame.PixelBuffer[x, y] = value;
}
}
/// <summary>
/// Saves the image to the given stream using the given image encoder.
@ -124,9 +197,11 @@ namespace SixLabors.ImageSharp
/// Clones the current image
/// </summary>
/// <returns>Returns a new image with all the same metadata as the original.</returns>
public new Image<TPixel> Clone()
public Image<TPixel> Clone()
{
return new Image<TPixel>(this);
IEnumerable<ImageFrame<TPixel>> frames = this.Frames.Select(x => x.Clone()).ToArray();
return new Image<TPixel>(this.Configuration, this.Width, this.Height, this.MetaData.Clone(), frames);
}
/// <inheritdoc/>
@ -143,79 +218,21 @@ namespace SixLabors.ImageSharp
public Image<TPixel2> CloneAs<TPixel2>()
where TPixel2 : struct, IPixel<TPixel2>
{
if (typeof(TPixel2) == typeof(TPixel))
{
// short circuit when same pixel types
return this.Clone() as Image<TPixel2>;
}
Func<Vector4, Vector4> scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TPixel, TPixel2>();
var target = new Image<TPixel2>(this.Configuration, this.Width, this.Height);
target.CopyProperties(this);
using (PixelAccessor<TPixel> pixels = this.Lock())
using (PixelAccessor<TPixel2> targetPixels = target.Lock())
{
Parallel.For(
0,
target.Height,
this.Configuration.ParallelOptions,
y =>
{
for (int x = 0; x < target.Width; x++)
{
var color = default(TPixel2);
color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4()));
targetPixels[x, y] = color;
}
});
}
for (int i = 0; i < this.Frames.Count; i++)
{
target.Frames.Add(this.Frames[i].CloneAs<TPixel2>());
}
IEnumerable<ImageFrame<TPixel2>> frames = this.Frames.Select(x => x.CloneAs<TPixel2>()).ToArray();
var target = new Image<TPixel2>(this.Configuration, this.Width, this.Height, this.MetaData, frames);
return target;
}
/// <summary>
/// Creates a new <see cref="ImageFrame{TPixel}"/> from this instance
/// Releases managed resources.
/// </summary>
/// <returns>The <see cref="ImageFrame{TPixel}"/></returns>
internal ImageFrame<TPixel> ToFrame()
public void Dispose()
{
return new ImageFrame<TPixel>(this);
}
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 0; i < this.Frames.Count; i++)
{
this.Frames[i].Dispose();
}
base.Dispose(disposing);
}
/// <inheritdoc/>
protected override ImageBase<TPixel> CloneImageBase()
{
return this.Clone();
}
/// <summary>
/// Copies the properties from the other <see cref="IImage"/>.
/// </summary>
/// <param name="other">
/// The other <see cref="IImage"/> to copy the properties from.
/// </param>
private void CopyProperties(IImage other)
{
this.MetaData = new ImageMetaData(other.MetaData);
}
}
}

36
src/ImageSharp/Image/PixelAccessorExtensions.cs

@ -0,0 +1,36 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using Unsafe = System.Runtime.CompilerServices.Unsafe;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Helper methods fro acccess pixel accessors
/// </summary>
internal static class PixelAccessorExtensions
{
/// <summary>
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="frame">The frame.</param>
/// <returns>
/// The <see cref="PixelAccessor{TPixel}" />
/// </returns>
internal static PixelAccessor<TPixel> Lock<TPixel>(this IImageFrame<TPixel> frame)
where TPixel : struct, IPixel<TPixel>
{
return new PixelAccessor<TPixel>(frame);
}
}
}

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

@ -23,6 +23,7 @@ namespace SixLabors.ImageSharp
/// The <see cref="Buffer{T}"/> containing the pixel data.
/// </summary>
internal Buffer2D<TPixel> PixelBuffer;
private bool ownedBuffer;
#pragma warning restore SA1401 // Fields must be private
/// <summary>
@ -40,14 +41,15 @@ namespace SixLabors.ImageSharp
/// Initializes a new instance of the <see cref="PixelAccessor{TPixel}"/> class.
/// </summary>
/// <param name="image">The image to provide pixel access for.</param>
public PixelAccessor(ImageBase<TPixel> image)
public PixelAccessor(IImageFrame<TPixel> image)
{
Guard.NotNull(image, nameof(image));
Guard.MustBeGreaterThan(image.Width, 0, "image width");
Guard.MustBeGreaterThan(image.Height, 0, "image height");
this.SetPixelBufferUnsafe(image.Width, image.Height, image.PixelBuffer);
this.ParallelOptions = image.Configuration.ParallelOptions;
this.SetPixelBufferUnsafe(image.PixelBuffer, false);
Configuration config = image.Parent?.Configuration ?? Configuration.Default;
this.ParallelOptions = config.ParallelOptions;
}
/// <summary>
@ -56,7 +58,7 @@ namespace SixLabors.ImageSharp
/// <param name="width">The width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param>
public PixelAccessor(int width, int height)
: this(width, height, Buffer2D<TPixel>.CreateClean(width, height))
: this(width, height, Buffer2D<TPixel>.CreateClean(width, height), true)
{
}
@ -66,13 +68,14 @@ namespace SixLabors.ImageSharp
/// <param name="width">The width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param>
/// <param name="pixels">The pixel buffer.</param>
private PixelAccessor(int width, int height, Buffer2D<TPixel> pixels)
/// <param name="ownedBuffer">if set to <c>true</c> [owned buffer].</param>
private PixelAccessor(int width, int height, Buffer2D<TPixel> pixels, bool ownedBuffer)
{
Guard.NotNull(pixels, nameof(pixels));
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.SetPixelBufferUnsafe(width, height, pixels);
this.SetPixelBufferUnsafe(pixels, ownedBuffer);
this.ParallelOptions = Configuration.Default.ParallelOptions;
}
@ -148,7 +151,7 @@ namespace SixLabors.ImageSharp
/// </summary>
public void Dispose()
{
if (this.isDisposed)
if (this.isDisposed || !this.ownedBuffer)
{
return;
}
@ -236,15 +239,13 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!!
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="pixels">The pixels.</param>
/// <returns>Returns the old pixel data thats has gust been replaced.</returns>
/// <remarks>If <see cref="M:PixelAccessor.PooledMemory"/> is true then caller is responsible for ensuring <see cref="M:PixelDataPool.Return()"/> is called.</remarks>
internal TPixel[] ReturnCurrentColorsAndReplaceThemInternally(int width, int height, TPixel[] pixels)
internal Buffer2D<TPixel> SwapBufferOwnership(Buffer2D<TPixel> pixels)
{
TPixel[] oldPixels = this.PixelBuffer.TakeArrayOwnership();
this.SetPixelBufferUnsafe(width, height, pixels);
var oldPixels = this.PixelBuffer;
this.SetPixelBufferUnsafe(pixels, this.ownedBuffer);
return oldPixels;
}
@ -412,23 +413,17 @@ namespace SixLabors.ImageSharp
}
}
private void SetPixelBufferUnsafe(int width, int height, TPixel[] pixels)
{
this.SetPixelBufferUnsafe(width, height, new Buffer2D<TPixel>(pixels, width, height));
}
/// <summary>
/// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!!
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="pixels">The pixel buffer</param>
private void SetPixelBufferUnsafe(int width, int height, Buffer2D<TPixel> pixels)
private void SetPixelBufferUnsafe(Buffer2D<TPixel> pixels, bool ownedBuffer)
{
this.PixelBuffer = pixels;
this.ownedBuffer = ownedBuffer;
this.Width = width;
this.Height = height;
this.Width = pixels.Width;
this.Height = pixels.Height;
this.PixelSize = Unsafe.SizeOf<TPixel>();
this.RowStride = this.Width * this.PixelSize;
}

6
src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs

@ -48,13 +48,13 @@ namespace SixLabors.ImageSharp.Processing.Processors
public TPixel LowerColor { get; set; }
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle);
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
float threshold = this.Threshold;
TPixel upper = this.UpperColor;
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);

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

@ -54,13 +54,13 @@ namespace SixLabors.ImageSharp.Processing.Processors
public TPixel LowerColor { get; set; }
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle);
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
int startY = interest.Y;

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

@ -62,13 +62,13 @@ namespace SixLabors.ImageSharp.Processing.Processors
public TPixel LowerColor { get; set; }
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle);
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
int startY = interest.Y;

18
src/ImageSharp/Processing/Processors/CloningImageProcessor.cs

@ -29,10 +29,6 @@ namespace SixLabors.ImageSharp.Processing
this.BeforeImageApply(source, clone, sourceRectangle);
this.BeforeApply(source, clone, sourceRectangle);
this.OnApply(source, clone, sourceRectangle);
this.AfterApply(source, clone, sourceRectangle);
for (int i = 0; i < source.Frames.Count; i++)
{
ImageFrame<TPixel> sourceFrame = source.Frames[i];
@ -71,11 +67,7 @@ namespace SixLabors.ImageSharp.Processing
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. The processor changed the number of frames.");
}
source.SwapPixelsData(cloned);
for (int i = 0; i < source.Frames.Count; i++)
{
source.Frames[i].SwapPixelsData(cloned.Frames[i]);
}
source.SwapPixelsBuffers(cloned);
}
}
@ -110,12 +102,12 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void BeforeApply(ImageBase<TPixel> source, ImageBase<TPixel> destination, Rectangle sourceRectangle)
protected virtual void BeforeApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle)
{
}
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TPixel}"/> at the specified location
/// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/> at the specified location
/// and with the specified size.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
@ -123,7 +115,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected abstract void OnApply(ImageBase<TPixel> source, ImageBase<TPixel> destination, Rectangle sourceRectangle);
protected abstract void OnApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle);
/// <summary>
/// This method is called after the process is applied to prepare the processor.
@ -133,7 +125,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void AfterApply(ImageBase<TPixel> source, ImageBase<TPixel> destination, Rectangle sourceRectangle)
protected virtual void AfterApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle)
{
}

4
src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public virtual bool Compand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);

2
src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs

@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
};
/// <inheritdoc/>
protected override void AfterApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void AfterApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
new VignetteProcessor<TPixel>(VeryDarkGreen, this.options).Apply(source, sourceRectangle);
}

2
src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs

@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
};
/// <inheritdoc/>
protected override void AfterApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void AfterApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
new VignetteProcessor<TPixel>(VeryDarkOrange, this.options).Apply(source, sourceRectangle);
new GlowProcessor<TPixel>(LightOrange, source.Width / 4F, this.options).Apply(source, sourceRectangle);

2
src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle);
}

4
src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int kernelYHeight = this.KernelY.Height;
int kernelYWidth = this.KernelY.Width;
@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
startY,
endY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);

6
src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs

@ -39,11 +39,11 @@ namespace SixLabors.ImageSharp.Processing.Processors
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int width = source.Width;
int height = source.Height;
ParallelOptions parallelOptions = source.Configuration.ParallelOptions;
ParallelOptions parallelOptions = source.Configuration().ParallelOptions;
using (var firstPassPixels = new PixelAccessor<TPixel>(width, height))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TPixel}"/> at the specified location
/// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/> at the specified location
/// and with the specified size.
/// </summary>
/// <param name="targetPixels">The target pixels to apply the process to.</param>

4
src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public Fast2DArray<float> KernelXY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int kernelLength = this.KernelXY.Height;
int radius = kernelLength >> 1;
@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
startY,
endY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);

4
src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs

@ -40,13 +40,13 @@ namespace SixLabors.ImageSharp.Processing.Processors
public bool Grayscale { get; set; }
/// <inheritdoc />
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
new Convolution2DProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle);
}
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
if (this.Grayscale)
{

10
src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs

@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public bool Grayscale { get; set; }
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
if (this.Grayscale)
{
@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <inheritdoc />
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
Fast2DArray<float>[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast };
@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
int maxY = Math.Min(source.Height, endY);
// we need a clean copy for each pass to start from
using (ImageBase<TPixel> cleanCopy = source.Clone())
using (ImageFrame<TPixel> cleanCopy = source.Clone())
{
new ConvolutionProcessor<TPixel>(kernels[0]).Apply(source, sourceRectangle);
@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 1; i < kernels.Length; i++)
{
using (ImageBase<TPixel> pass = cleanCopy.Clone())
using (ImageFrame<TPixel> pass = cleanCopy.Clone())
{
new ConvolutionProcessor<TPixel>(kernels[i]).Apply(pass, sourceRectangle);
@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
int offsetY = y - shiftY;

4
src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public Fast2DArray<float> KernelXY { get; }
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
if (this.Grayscale)
{
@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
new ConvolutionProcessor<TPixel>(this.KernelXY).Apply(source, sourceRectangle);
}

2
src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs

@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle);
}

2
src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs

@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public Fast2DArray<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle);
}

2
src/ImageSharp/Processing/Processors/DelegateProcessor.cs

@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
// NOP, we did all we wanted to do inside BeforeImageApply
}

4
src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs

@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public float Value { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);

4
src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs

@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public TPixel Value { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width);

4
src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs

@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public int Value { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
float brightness = this.Value / 100F;
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);

4
src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs

@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public int Value { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
float contrast = (100F + this.Value) / 100F;
@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);

4
src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);

4
src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs

@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public int BrushSize { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
if (this.BrushSize <= 0 || this.BrushSize > source.Height || this.BrushSize > source.Width)
{
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
startY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);

4
src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs

@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public int Size { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width)
{
@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.ForEach(
range,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
int offsetY = y - startY;

10
src/ImageSharp/Processing/Processors/ImageProcessor.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
/// <param name="source">the source image</param>
/// <param name="sourceRectangle">the target</param>
public void Apply(ImageBase<TPixel> source, Rectangle sourceRectangle)
public void Apply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
try
{
@ -91,19 +91,19 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected virtual void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
}
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TPixel}"/> at the specified location
/// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/> at the specified location
/// and with the specified size.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected abstract void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle);
protected abstract void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle);
/// <summary>
/// This method is called after the process is applied to prepare the processor.
@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void AfterApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected virtual void AfterApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
}

4
src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs

@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public ValueSize Radius { get; set; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
using (var amounts = new Buffer<float>(width))

4
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs

@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public ValueSize RadiusY { get; set; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
using (var amounts = new Buffer<float>(width))

2
src/ImageSharp/Processing/Processors/Transforms/AutoRotateProcessor.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> sourceBase, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> sourceBase, Rectangle sourceRectangle)
{
// can only apply to the origional image
var source = sourceBase as Image<TPixel>;

4
src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public Rectangle CropRectangle { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
if (this.CropRectangle == sourceRectangle)
{
@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(minX);

4
src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs

@ -34,9 +34,9 @@ namespace SixLabors.ImageSharp.Processing.Processors
public float Threshold { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
using (ImageBase<TPixel> temp = source.Clone())
using (ImageFrame<TPixel> temp = source.Clone())
{
// Detect the edges.
new SobelProcessor<TPixel>().Apply(temp, sourceRectangle);

10
src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public FlipType FlipType { get; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
switch (this.FlipType)
{
@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// at half the height of the image.
/// </summary>
/// <param name="source">The source image to apply the process to.</param>
private void FlipX(ImageBase<TPixel> source)
private void FlipX(ImageFrame<TPixel> source)
{
int width = source.Width;
int height = source.Height;
@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
halfHeight,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
int newY = height - y - 1;
@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// at half of the width of the image.
/// </summary>
/// <param name="source">The source image to apply the process to.</param>
private void FlipY(ImageBase<TPixel> source)
private void FlipY(ImageFrame<TPixel> source)
{
int width = source.Width;
int height = source.Height;
@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);

2
src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <returns>
/// The <see cref="Matrix3x2"/>.
/// </returns>
protected Matrix3x2 GetCenteredMatrix(ImageBase<TPixel> source, Matrix3x2 matrix)
protected Matrix3x2 GetCenteredMatrix(ImageFrame<TPixel> source, Matrix3x2 matrix)
{
var translationToTargetCenter = Matrix3x2.CreateTranslation(-this.CanvasRectangle.Width * .5F, -this.CanvasRectangle.Height * .5F);
var translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F);

4
src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs

@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, ImageBase<TPixel> destination, Rectangle sourceRectangle)
protected override void BeforeApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle)
{
if (!(this.Sampler is NearestNeighborResampler))
{
@ -152,7 +152,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <inheritdoc />
protected override void AfterApply(ImageBase<TPixel> source, ImageBase<TPixel> destination, Rectangle sourceRectangle)
protected override void AfterApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle)
{
base.AfterApply(source, destination, sourceRectangle);
this.HorizontalWeights?.Dispose();

19
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
@ -55,20 +56,14 @@ namespace SixLabors.ImageSharp.Processing.Processors
// ------------
// For resize we know we are going to populate every pixel with fresh data and we want a different target size so
// let's manually clone an empty set of images at the correct target and then have the base class processs them in turn.
var image = new Image<TPixel>(source.Configuration, this.Width, this.Height, source.MetaData.Clone());
// Now 'clone' the ImageFrames
foreach (ImageFrame<TPixel> sourceFrame in source.Frames)
{
var targetFrame = new ImageFrame<TPixel>(sourceFrame.Configuration, this.Width, this.Height, sourceFrame.MetaData.Clone());
image.Frames.Add(targetFrame);
}
var frames = source.Frames.Select(x => new ImageFrame<TPixel>(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders
var image = new Image<TPixel>(source.Configuration, this.Width, this.Height, source.MetaData.Clone(), frames); // base the place holder images in to prevet a extra frame being added
return image;
}
/// <inheritdoc/>
protected override unsafe void OnApply(ImageBase<TPixel> source, ImageBase<TPixel> cloned, Rectangle sourceRectangle)
protected override unsafe void OnApply(ImageFrame<TPixel> source, ImageFrame<TPixel> cloned, Rectangle sourceRectangle)
{
// Jump out, we'll deal with that later.
if (source.Width == cloned.Width && source.Height == cloned.Height && sourceRectangle == this.ResizeRectangle)
@ -101,7 +96,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
// Y coordinates of source points
@ -130,7 +125,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
sourceRectangle.Bottom,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
// TODO: Without Parallel.For() this buffer object could be reused:
@ -163,7 +158,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
// Ensure offsets are normalised for cropping and padding.

20
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public bool Expand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
if (this.OptimizedApply(source))
{
@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> targetRow = targetPixels.GetRowSpan(y);
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
if (MathF.Abs(this.Angle) < Constants.Epsilon || MathF.Abs(this.Angle - 90) < Constants.Epsilon || MathF.Abs(this.Angle - 180) < Constants.Epsilon || MathF.Abs(this.Angle - 270) < Constants.Epsilon)
{
@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// </summary>
/// <param name="source">The source image.</param>
/// <returns>The <see cref="bool"/></returns>
private bool OptimizedApply(ImageBase<TPixel> source)
private bool OptimizedApply(ImageFrame<TPixel> source)
{
if (MathF.Abs(this.Angle) < Constants.Epsilon)
{
@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Rotates the image 270 degrees clockwise at the centre point.
/// </summary>
/// <param name="source">The source image.</param>
private void Rotate270(ImageBase<TPixel> source)
private void Rotate270(ImageFrame<TPixel> source)
{
int width = source.Width;
int height = source.Height;
@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
for (int x = 0; x < width; x++)
@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Rotates the image 180 degrees clockwise at the centre point.
/// </summary>
/// <param name="source">The source image.</param>
private void Rotate180(ImageBase<TPixel> source)
private void Rotate180(ImageFrame<TPixel> source)
{
int width = source.Width;
int height = source.Height;
@ -167,7 +167,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Rotates the image 90 degrees clockwise at the centre point.
/// </summary>
/// <param name="source">The source image.</param>
private void Rotate90(ImageBase<TPixel> source)
private void Rotate90(ImageFrame<TPixel> source)
{
int width = source.Width;
int height = source.Height;
@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);

6
src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public bool Expand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int height = this.CanvasRectangle.Height;
int width = this.CanvasRectangle.Width;
@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
Span<TPixel> targetRow = targetPixels.GetRowSpan(y);
@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
this.processMatrix = Matrix3x2Extensions.CreateSkewDegrees(-this.AngleX, -this.AngleY, new Point(0, 0));
if (this.Expand)

12
src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
public static Rectangle CalculateTargetLocationAndBounds<TPixel>(ImageBase<TPixel> source, ResizeOptions options)
public static Rectangle CalculateTargetLocationAndBounds<TPixel>(ImageFrame<TPixel> source, ResizeOptions options)
where TPixel : struct, IPixel<TPixel>
{
switch (options.Mode)
@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
private static Rectangle CalculateCropRectangle<TPixel>(ImageBase<TPixel> source, ResizeOptions options)
private static Rectangle CalculateCropRectangle<TPixel>(ImageFrame<TPixel> source, ResizeOptions options)
where TPixel : struct, IPixel<TPixel>
{
int width = options.Size.Width;
@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
private static Rectangle CalculatePadRectangle<TPixel>(ImageBase<TPixel> source, ResizeOptions options)
private static Rectangle CalculatePadRectangle<TPixel>(ImageFrame<TPixel> source, ResizeOptions options)
where TPixel : struct, IPixel<TPixel>
{
int width = options.Size.Width;
@ -253,7 +253,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
private static Rectangle CalculateBoxPadRectangle<TPixel>(ImageBase<TPixel> source, ResizeOptions options)
private static Rectangle CalculateBoxPadRectangle<TPixel>(ImageFrame<TPixel> source, ResizeOptions options)
where TPixel : struct, IPixel<TPixel>
{
int width = options.Size.Width;
@ -340,7 +340,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
private static Rectangle CalculateMaxRectangle<TPixel>(ImageBase<TPixel> source, ResizeOptions options)
private static Rectangle CalculateMaxRectangle<TPixel>(ImageFrame<TPixel> source, ResizeOptions options)
where TPixel : struct, IPixel<TPixel>
{
int width = options.Size.Width;
@ -381,7 +381,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
private static Rectangle CalculateMinRectangle<TPixel>(ImageBase<TPixel> source, ResizeOptions options)
private static Rectangle CalculateMinRectangle<TPixel>(ImageFrame<TPixel> source, ResizeOptions options)
where TPixel : struct, IPixel<TPixel>
{
int width = options.Size.Width;

6
src/ImageSharp/Processing/Transforms/Resize.cs

@ -38,14 +38,14 @@ namespace SixLabors.ImageSharp
options.Size = new Size(options.Size.Width, (int)MathF.Round(img.Height * options.Size.Width / (float)img.Width));
}
Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds(img, options);
Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds<TPixel>(img, options);
img.Mutate(x => Resize(x, options.Size.Width, options.Size.Height, options.Sampler, targetRectangle, options.Compand));
});
}
/// <summary>
/// Resizes an image to the given <see cref="Size"/>.
/// Resizes an image to the given <see cref="SixLabors.Primitives.Size"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to resize.</param>
@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Resizes an image to the given <see cref="Size"/>.
/// Resizes an image to the given <see cref="SixLabors.Primitives.Size"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to resize.</param>

2
src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Quantizers
/// <returns>
/// A <see cref="T:QuantizedImage"/> representing a quantized version of the image pixels.
/// </returns>
QuantizedImage<TPixel> Quantize(ImageBase<TPixel> image, int maxColors);
QuantizedImage<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors);
}
/// <summary>

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

@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Quantizers
}
/// <inheritdoc/>
public override QuantizedImage<TPixel> Quantize(ImageBase<TPixel> image, int maxColors)
public override QuantizedImage<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)
{
this.colors = (byte)maxColors.Clamp(1, 255);
this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors));
@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Quantizers
}
/// <inheritdoc/>
protected override void SecondPass(ImageBase<TPixel> source, byte[] output, int width, int height)
protected override void SecondPass(ImageFrame<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.

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

@ -55,14 +55,14 @@ namespace SixLabors.ImageSharp.Quantizers
}
/// <inheritdoc/>
public override QuantizedImage<TPixel> Quantize(ImageBase<TPixel> image, int maxColors)
public override QuantizedImage<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)
{
Array.Resize(ref this.colors, maxColors.Clamp(1, 255));
return base.Quantize(image, maxColors);
}
/// <inheritdoc/>
protected override void SecondPass(ImageBase<TPixel> source, byte[] output, int width, int height)
protected override void SecondPass(ImageFrame<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.

2
src/ImageSharp/Quantizers/Quantize.cs

@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp
}
});
img.SwapPixelsBuffers(pixels);
img.Frames[0].SwapPixelsBuffers(pixels);
}
});
}

8
src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs

@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base
public IErrorDiffuser DitherType { get; set; } = new SierraLiteDiffuser();
/// <inheritdoc/>
public virtual QuantizedImage<TPixel> Quantize(ImageBase<TPixel> image, int maxColors)
public virtual QuantizedImage<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)
{
Guard.NotNull(image, nameof(image));
@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base
if (this.Dither)
{
// We clone the image as we don't want to alter the original.
using (ImageBase<TPixel> clone = image.Clone())
using (ImageFrame<TPixel> clone = image.Clone())
{
this.SecondPass(clone, quantizedPixels, width, height);
}
@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base
/// <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(ImageBase<TPixel> source, int width, int height)
protected virtual void FirstPass(ImageFrame<TPixel> source, int width, int height)
{
// Loop through each row
for (int y = 0; y < height; y++)
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base
/// <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(ImageBase<TPixel> source, byte[] output, int width, int height);
protected abstract void SecondPass(ImageFrame<TPixel> source, byte[] output, int width, int height);
/// <summary>
/// Override this to process the pixel in the first pass of the algorithm

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

@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Quantizers
}
/// <inheritdoc/>
public override QuantizedImage<TPixel> Quantize(ImageBase<TPixel> image, int maxColors)
public override QuantizedImage<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)
{
Guard.NotNull(image, nameof(image));
@ -221,7 +221,7 @@ namespace SixLabors.ImageSharp.Quantizers
}
/// <inheritdoc/>
protected override void FirstPass(ImageBase<TPixel> source, int width, int height)
protected override void FirstPass(ImageFrame<TPixel> source, int width, int height)
{
// Build up the 3-D color histogram
// Loop through each row
@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Quantizers
}
/// <inheritdoc/>
protected override void SecondPass(ImageBase<TPixel> source, byte[] output, int width, int height)
protected override void SecondPass(ImageFrame<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.

4
tests/ImageSharp.Benchmarks/Samplers/Glow.cs

@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Benchmarks
public float Radius { get; set; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Benchmarks
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
source.Configuration().ParallelOptions,
y =>
{
int offsetY = y - startY;

4
tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs

@ -12,8 +12,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
public static ExactImageComparer Instance { get; } = new ExactImageComparer();
public override ImageSimilarityReport CompareImagesOrFrames<TPixelA, TPixelB>(
ImageBase<TPixelA> expected,
ImageBase<TPixelB> actual)
ImageFrame<TPixelA> expected,
ImageFrame<TPixelB> actual)
{
if (expected.Size() != actual.Size())
{

22
tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs

@ -23,13 +23,22 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
}
public abstract ImageSimilarityReport CompareImagesOrFrames<TPixelA, TPixelB>(
ImageBase<TPixelA> expected,
ImageBase<TPixelB> actual)
ImageFrame<TPixelA> expected,
ImageFrame<TPixelB> actual)
where TPixelA : struct, IPixel<TPixelA> where TPixelB : struct, IPixel<TPixelB>;
}
public static class ImageComparerExtensions
{
public static ImageSimilarityReport CompareImagesOrFrames<TPixelA, TPixelB>(
this ImageComparer comparer,
Image<TPixelA> expected,
Image<TPixelB> actual)
where TPixelA : struct, IPixel<TPixelA> where TPixelB : struct, IPixel<TPixelB>
{
return comparer.CompareImagesOrFrames((ImageFrame<TPixelA>)expected, (ImageFrame<TPixelB>)actual);
}
public static IEnumerable<ImageSimilarityReport> CompareImages<TPixelA, TPixelB>(
this ImageComparer comparer,
Image<TPixelA> expected,
@ -37,12 +46,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
where TPixelA : struct, IPixel<TPixelA> where TPixelB : struct, IPixel<TPixelB>
{
var result = new List<ImageSimilarityReport>();
ImageSimilarityReport report = comparer.CompareImagesOrFrames(expected, actual);
if (!report.IsEmpty)
{
result.Add(report);
}
if (expected.Frames.Count != actual.Frames.Count)
{
@ -50,12 +53,13 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
}
for (int i = 0; i < expected.Frames.Count; i++)
{
report = comparer.CompareImagesOrFrames(expected.Frames[i], actual.Frames[i]);
ImageSimilarityReport report = comparer.CompareImagesOrFrames(expected.Frames[i], actual.Frames[i]);
if (!report.IsEmpty)
{
result.Add(report);
}
}
return result;
}

8
tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs

@ -8,8 +8,8 @@
public class ImageSimilarityReport
{
public ImageSimilarityReport(
IImageBase expectedImage,
IImageBase actualImage,
IImageFrame expectedImage,
IImageFrame actualImage,
IEnumerable<PixelDifference> differences,
float? totalNormalizedDifference = null)
{
@ -29,9 +29,9 @@
? $"{this.TotalNormalizedDifference.Value * 100:0.0000}%"
: "?";
public IImageBase ExpectedImage { get; }
public IImageFrame ExpectedImage { get; }
public IImageBase ActualImage { get; }
public IImageFrame ActualImage { get; }
public PixelDifference[] Differences { get; }

2
tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs

@ -45,7 +45,7 @@
/// </summary>
public int PerPixelManhattanThreshold { get; }
public override ImageSimilarityReport CompareImagesOrFrames<TPixelA, TPixelB>(ImageBase<TPixelA> expected, ImageBase<TPixelB> actual)
public override ImageSimilarityReport CompareImagesOrFrames<TPixelA, TPixelB>(ImageFrame<TPixelA> expected, ImageFrame<TPixelB> actual)
{
if (expected.Size() != actual.Size())
{

14
tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests
/// The name of the test case (by default)
/// </summary>
public string TestName { get; set; } = string.Empty;
private string GetTestOutputFileNameImpl(string extension, string details, bool appendPixelTypeToFileName)
{
string fn = string.Empty;
@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Tests
{
string path = this.GetTestOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName);
encoder = encoder ?? TestEnvironment.GetReferenceEncoder(path);
using (FileStream stream = File.OpenWrite(path))
{
image.Save(stream, encoder);
@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Tests
// pngEncoder.PngColorType = PngColorType.Grayscale;
// }
// }
// return encoder;
//}
@ -197,8 +197,14 @@ namespace SixLabors.ImageSharp.Tests
return TestEnvironment.CreateOutputDirectory(testGroupName);
}
public static void ModifyPixel<TPixel>(ImageBase<TPixel> img, int x, int y, byte perChannelChange)
public static void ModifyPixel<TPixel>(Image<TPixel> img, int x, int y, byte perChannelChange)
where TPixel : struct, IPixel<TPixel>
{
ModifyPixel((ImageFrame<TPixel>)img, x, y, perChannelChange);
}
public static void ModifyPixel<TPixel>(ImageFrame<TPixel> img, int x, int y, byte perChannelChange)
where TPixel : struct, IPixel<TPixel>
{
TPixel pixel = img[x, y];
var rgbaPixel = default(Rgba32);

Loading…
Cancel
Save