Browse Source

Merge pull request #326 from SixLabors/image-frames

API Image frames refactor
pull/299/head
Scott Williams 9 years ago
committed by GitHub
parent
commit
6f4144cff1
  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. 6
      src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
  8. 8
      src/ImageSharp.Drawing/Processors/FillProcessor.cs
  9. 2
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  10. 16
      src/ImageSharp/Advanced/IConfigurable.cs
  11. 11
      src/ImageSharp/Advanced/IPixelSource.cs
  12. 85
      src/ImageSharp/Advanced/ImageExtensions.cs
  13. 9
      src/ImageSharp/ApplyProcessors.cs
  14. 12
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  15. 4
      src/ImageSharp/DefaultInternalImageProcessorContext.cs
  16. 4
      src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
  17. 4
      src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs
  18. 2
      src/ImageSharp/Dithering/Ordered/IOrderedDither.cs
  19. 2
      src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs
  20. 12
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  21. 3
      src/ImageSharp/Formats/Bmp/ImageExtensions.cs
  22. 8
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  23. 2
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  24. 43
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  25. 3
      src/ImageSharp/Formats/Gif/ImageExtensions.cs
  26. 2
      src/ImageSharp/Formats/IImageDecoder.cs
  27. 9
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
  28. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
  29. 3
      src/ImageSharp/Formats/Jpeg/ImageExtensions.cs
  30. 14
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  31. 3
      src/ImageSharp/Formats/Png/ImageExtensions.cs
  32. 2
      src/ImageSharp/Formats/Png/PngDecoder.cs
  33. 10
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  34. 12
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  35. 61
      src/ImageSharp/Helpers/ImageExtensions.cs
  36. 2
      src/ImageSharp/ICloningImageProcessor.cs
  37. 2
      src/ImageSharp/IImageProcessor.cs
  38. 19
      src/ImageSharp/Image/IImage.cs
  39. 28
      src/ImageSharp/Image/IImageBase.cs
  40. 18
      src/ImageSharp/Image/IImageFrame.cs
  41. 84
      src/ImageSharp/Image/IImageFrameCollection.cs
  42. 2
      src/ImageSharp/Image/Image.LoadPixelData.cs
  43. 333
      src/ImageSharp/Image/ImageBase{TPixel}.cs
  44. 91
      src/ImageSharp/Image/ImageExtensions.cs
  45. 52
      src/ImageSharp/Image/ImageFrame.LoadPixelData.cs
  46. 166
      src/ImageSharp/Image/ImageFrameCollection.cs
  47. 202
      src/ImageSharp/Image/ImageFrame{TPixel}.cs
  48. 164
      src/ImageSharp/Image/Image{TPixel}.cs
  49. 54
      src/ImageSharp/Image/PixelAccessorExtensions.cs
  50. 63
      src/ImageSharp/Image/PixelAccessor{TPixel}.cs
  51. 8
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
  52. 7
      src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs
  53. 7
      src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs
  54. 43
      src/ImageSharp/Processing/Processors/CloningImageProcessor.cs
  55. 4
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs
  56. 4
      src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs
  57. 6
      src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs
  58. 4
      src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
  59. 4
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
  60. 8
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
  61. 4
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
  62. 8
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs
  63. 17
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
  64. 8
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs
  65. 4
      src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
  66. 4
      src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs
  67. 2
      src/ImageSharp/Processing/Processors/DelegateProcessor.cs
  68. 4
      src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs
  69. 4
      src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs
  70. 4
      src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs
  71. 4
      src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs
  72. 4
      src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs
  73. 4
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs
  74. 4
      src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs
  75. 52
      src/ImageSharp/Processing/Processors/ImageProcessor.cs
  76. 5
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  77. 5
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
  78. 88
      src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs
  79. 4
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs
  80. 10
      src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs
  81. 16
      src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs
  82. 2
      src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs
  83. 6
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs
  84. 22
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  85. 37
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  86. 8
      src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
  87. 12
      src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs
  88. 6
      src/ImageSharp/Processing/Transforms/Resize.cs
  89. 2
      src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs
  90. 4
      src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs
  91. 4
      src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs
  92. 5
      src/ImageSharp/Quantizers/Quantize.cs
  93. 8
      src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
  94. 6
      src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs
  95. 4
      tests/ImageSharp.Benchmarks/Image/CopyPixels.cs
  96. 10
      tests/ImageSharp.Benchmarks/Samplers/Glow.cs
  97. 6
      tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs
  98. 219
      tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs
  99. 2
      tests/ImageSharp.Tests/Image/ImageLoadTests.cs
  100. 5
      tests/ImageSharp.Tests/Image/ImageRotationTests.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);

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

@ -4,6 +4,8 @@
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
@ -58,7 +60,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, Configuration configuration)
{
Image<TPixel> disposableImage = null;
Image<TPixel> targetImage = this.Image;
@ -94,7 +96,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
Span<TPixel> background = sourcePixels.GetRowSpan(y).Slice(minX, width);

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

@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Drawing.Brushes;
using SixLabors.ImageSharp.Drawing.Brushes.Processors;
@ -39,7 +40,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, Configuration configuration)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
@ -65,9 +66,6 @@ namespace SixLabors.ImageSharp.Drawing.Processors
int width = maxX - minX;
// We could possibly do some optimization by having knowledge about the individual brushes operate
// for example If brush is SolidBrush<TPixel> then we could just get the color upfront
// and skip using the IBrushApplicator<TPixel>?.
using (var amount = new Buffer<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options))
{
@ -79,7 +77,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
int offsetY = y - startY;

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

@ -58,7 +58,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, Configuration configuration)
{
Region region = this.Region;
Rectangle rect = region.Bounds;

16
src/ImageSharp/Advanced/IConfigurable.cs

@ -0,0 +1,16 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Encapsulates the properties for configuration
/// </summary>
internal interface IConfigurable
{
/// <summary>
/// Gets the configuration.
/// </summary>
Configuration Configuration { get; }
}
}

11
src/ImageSharp/Advanced/IPixelSource{TPixel}.cs → src/ImageSharp/Advanced/IPixelSource.cs

@ -2,20 +2,23 @@
// 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.Advanced
{
/// <summary>
/// Allows access to the pixels as an area of contiguous memory in the given pixel format.
/// Encapsulates the basic properties and methods required to manipulate images.
/// </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.
/// Gets the pixel buffer.
/// </summary>
Span<TPixel> Span { get; }
Buffer2D<TPixel> PixelBuffer { get; }
}
}
}

85
src/ImageSharp/Advanced/ImageExtensions.cs

@ -2,19 +2,15 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Extension methods over Image{TPixel}
/// </summary>
internal static partial class ImageExtensions
internal static class ImageExtensions
{
/// <summary>
/// Gets the representation of the pixels as an area of contiguous memory in the given pixel format.
@ -22,29 +18,94 @@ 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 a <see cref="Span{TPixal}"/> representing the row 'y' beginning from the the first pixel on that row.
/// Gets the representation of the pixels as an area of contiguous memory at 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 ImageBase<TPixel> source, int row)
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);
=> GetSpan(source, row);
/// <summary>
/// Gets the span.
/// 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>
=> source.Frames.RootFrame.GetPixelSpan();
/// <summary>
/// Gets the representation of the pixels as an area of contiguous memory at 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 Image<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelRowSpan(row);
/// <summary>
/// Gets the configuration for the image.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>Returns the configuration.</returns>
public static Configuration GetConfiguration<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> GetConfiguration((IConfigurable)source);
/// <summary>
/// Gets the span to the backing buffer.
/// </summary>
/// <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)
where TPixel : struct, IPixel<TPixel>
=> source.Span;
=> source.PixelBuffer.Span;
/// <summary>
/// Gets the span to the backing buffer at the given 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 span retuned from Pixel source
/// </returns>
private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>
=> GetSpan(source.PixelBuffer, row);
/// <summary>
/// Gets the span to the backing buffer at the given 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 span retuned from Pixel source
/// </returns>
private static Span<TPixel> GetSpan<TPixel>(Buffer2D<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>
=> source.Span.Slice(row * source.Width, source.Width);
/// <summary>
/// Gets the configuration.
/// </summary>
/// <param name="source">The source image</param>
/// <returns>Returns the bounds of the image</returns>
private static Configuration GetConfiguration(IConfigurable source)
=> source?.Configuration ?? Configuration.Default;
}
}

9
src/ImageSharp/ApplyProcessors.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(operation, nameof(operation));
Guard.NotNull(source, nameof(source));
IInternalImageProcessingContext<TPixel> operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, true);
IInternalImageProcessingContext<TPixel> operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, true);
operation(operationsRunner);
operationsRunner.Apply();
}
@ -41,7 +42,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(operations, nameof(operations));
Guard.NotNull(source, nameof(source));
IInternalImageProcessingContext<TPixel> operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, true);
IInternalImageProcessingContext<TPixel> operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, true);
operationsRunner.ApplyProcessors(operations);
operationsRunner.Apply();
}
@ -59,7 +60,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(operation, nameof(operation));
Guard.NotNull(source, nameof(source));
IInternalImageProcessingContext<TPixel> operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, false);
IInternalImageProcessingContext<TPixel> operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, false);
operation(operationsRunner);
return operationsRunner.Apply();
}
@ -77,7 +78,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(operations, nameof(operations));
Guard.NotNull(source, nameof(source));
IInternalImageProcessingContext<TPixel> operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, false);
IInternalImageProcessingContext<TPixel> operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, false);
operationsRunner.ApplyProcessors(operations);
return operationsRunner.Apply();
}

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/DefaultInternalImageProcessorContext.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
@ -53,8 +54,7 @@ namespace SixLabors.ImageSharp
// This will only work if the first processor applied is the cloning one thus
// realistically for this optermissation to work the resize must the first processor
// applied any only up processors will take the douple data path.
var cloningImageProcessor = processor as ICloningImageProcessor<TPixel>;
if (cloningImageProcessor != null)
if (processor is ICloningImageProcessor<TPixel> cloningImageProcessor)
{
this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle);
return this;

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.

12
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));
@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
WriteHeader(writer, fileHeader);
this.WriteInfo(writer, infoHeader);
this.WriteImage(writer, image);
this.WriteImage(writer, image.Frames.RootFrame);
writer.Flush();
}
@ -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, ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())

3
src/ImageSharp/Formats/Bmp/ImageExtensions.cs

@ -3,6 +3,7 @@
using System;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.PixelFormats;
@ -35,6 +36,6 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsBmp<TPixel>(this Image<TPixel> source, Stream stream, BmpEncoder encoder)
where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.Configuration.FindEncoder(ImageFormats.Bmp));
=> source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Bmp));
}
}

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 = this.image.Frames.RootFrame;
}
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.Frames.RootFrame : 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)
{

2
src/ImageSharp/Formats/Gif/GifEncoder.cs

@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
GifEncoderCore encoder = new GifEncoderCore(this);
var encoder = new GifEncoderCore(this);
encoder.Encode(image, stream);
}
}

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

@ -90,20 +90,20 @@ namespace SixLabors.ImageSharp.Formats.Gif
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
// Ensure that pallete size can be set but has a fallback.
int paletteSize = this.paletteSize;
paletteSize = paletteSize > 0 ? paletteSize.Clamp(1, 256) : 256;
int size = this.paletteSize;
size = size > 0 ? size.Clamp(1, 256) : 256;
// Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(paletteSize);
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(size);
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;
ditheredQuantizer.Dither = !this.hasFrames;
// Quantize the image returning a palette.
QuantizedImage<TPixel> quantized = ditheredQuantizer.Quantize(image, paletteSize);
QuantizedImage<TPixel> quantized = ditheredQuantizer.Quantize(image.Frames.RootFrame, size);
int index = this.GetTransparentIndex(quantized);
@ -114,28 +114,27 @@ 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++)
foreach (ImageFrame<TPixel> frame in image.Frames)
{
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, size);
}
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 +252,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 +320,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 +346,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>

3
src/ImageSharp/Formats/Gif/ImageExtensions.cs

@ -3,6 +3,7 @@
using System;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.PixelFormats;
@ -35,6 +36,6 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsGif<TPixel>(this Image<TPixel> source, Stream stream, GifEncoder encoder)
where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.Configuration.FindEncoder(ImageFormats.Gif));
=> source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Gif));
}
}

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>

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

@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -16,7 +17,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
{
@ -97,7 +98,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="destination">The destination image</param>
public void PostProcess<TPixel>(Image<TPixel> destination)
public void PostProcess<TPixel>(ImageFrame<TPixel> destination)
where TPixel : struct, IPixel<TPixel>
{
this.PixelRowCounter = 0;
@ -118,7 +119,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="destination">The destination image.</param>
public void DoPostProcessorStep<TPixel>(Image<TPixel> destination)
public void DoPostProcessorStep<TPixel>(ImageFrame<TPixel> destination)
where TPixel : struct, IPixel<TPixel>
{
foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors)
@ -136,7 +137,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="destination">The destination image</param>
private void ConvertColorsInto<TPixel>(Image<TPixel> destination)
private void ConvertColorsInto<TPixel>(ImageFrame<TPixel> destination)
where TPixel : struct, IPixel<TPixel>
{
int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep);

2
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs

@ -815,7 +815,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
using (var postProcessor = new JpegImagePostProcessor(this))
{
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
postProcessor.PostProcess(image);
postProcessor.PostProcess(image.Frames.RootFrame);
return image;
}
}

3
src/ImageSharp/Formats/Jpeg/ImageExtensions.cs

@ -3,6 +3,7 @@
using System;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
@ -35,6 +36,6 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsJpeg<TPixel>(this Image<TPixel> source, Stream stream, JpegEncoder encoder)
where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.Configuration.FindEncoder(ImageFormats.Jpeg));
=> source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Jpeg));
}
}

14
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.QuantizeAndInverseAllComponents();
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, metadata);
this.FillPixelData(image);
this.FillPixelData(image.Frames.RootFrame);
this.AssignResolution(image);
return image;
}
@ -326,7 +326,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image</param>
private void FillPixelData<TPixel>(Image<TPixel> image)
private void FillPixelData<TPixel>(ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (this.NumberOfComponents > 4)
@ -856,7 +856,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FillGrayScaleImage<TPixel>(Image<TPixel> image)
private void FillGrayScaleImage<TPixel>(ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
for (int y = 0; y < image.Height; y++)
@ -875,7 +875,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FillYCbCrImage<TPixel>(Image<TPixel> image)
private void FillYCbCrImage<TPixel>(ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
for (int y = 0; y < image.Height; y++)
@ -894,7 +894,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FillYcckImage<TPixel>(Image<TPixel> image)
private void FillYcckImage<TPixel>(ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
for (int y = 0; y < image.Height; y++)
@ -915,7 +915,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FillCmykImage<TPixel>(Image<TPixel> image)
private void FillCmykImage<TPixel>(ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
for (int y = 0; y < image.Height; y++)
@ -941,7 +941,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FillRgbImage<TPixel>(Image<TPixel> image)
private void FillRgbImage<TPixel>(ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
for (int y = 0; y < image.Height; y++)

3
src/ImageSharp/Formats/Png/ImageExtensions.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
@ -34,6 +35,6 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsPng<TPixel>(this Image<TPixel> source, Stream stream, PngEncoder encoder)
where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.Configuration.FindEncoder(ImageFormats.Png));
=> source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Png));
}
}

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>

10
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -236,7 +236,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
deframeStream.AllocateNewBytes(currentChunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
stream.Read(this.crcBuffer, 0, 4);
break;
case PngChunkTypes.Palette:
@ -442,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <param name="image"> The pixel data.</param>
private void ReadScanlines<TPixel>(Stream dataStream, Image<TPixel> image)
private void ReadScanlines<TPixel>(Stream dataStream, ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
@ -461,7 +461,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="image">The image to decode to.</param>
private void DecodePixelData<TPixel>(Stream compressedStream, Image<TPixel> image)
private void DecodePixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
while (this.currentRow < this.header.Height)
@ -519,7 +519,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="image">The current image.</param>
private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, Image<TPixel> image)
private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
while (true)
@ -609,7 +609,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="pixels">The image</param>
private void ProcessDefilteredScanline<TPixel>(byte[] defilteredScanline, Image<TPixel> pixels)
private void ProcessDefilteredScanline<TPixel>(byte[] defilteredScanline, ImageFrame<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);

12
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,12 +233,12 @@ namespace SixLabors.ImageSharp.Formats.Png
// Collect the indexed pixel data
if (this.pngColorType == PngColorType.Palette)
{
this.CollectIndexedBytes(image, stream, header);
this.CollectIndexedBytes<TPixel>(image.Frames.RootFrame, stream, header);
}
this.WritePhysicalChunk(stream, image);
this.WriteGammaChunk(stream);
this.WriteDataChunks(image, stream);
this.WriteDataChunks(image.Frames.RootFrame, stream);
this.WriteEndChunk(stream);
stream.Flush();
}
@ -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)
@ -649,7 +649,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The image.</param>
/// <param name="stream">The stream.</param>
private void WriteDataChunks<TPixel>(Image<TPixel> pixels, Stream stream)
private void WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
this.bytesPerScanline = this.width * this.bytesPerPixel;

61
src/ImageSharp/Helpers/ImageExtensions.cs

@ -0,0 +1,61 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Helpers
{
/// <summary>
/// Extension methods over Image{TPixel}
/// </summary>
public static partial class ImageExtensions
{
/// <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);
/// <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 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);
}
}

2
src/ImageSharp/Image/ICloningImageProcessor.cs → src/ImageSharp/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">

2
src/ImageSharp/Image/IImageProcessor.cs → src/ImageSharp/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">

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; }
}
}

18
src/ImageSharp/Image/IImageFrame.cs

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

84
src/ImageSharp/Image/IImageFrameCollection.cs

@ -0,0 +1,84 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections;
using System.Collections.Generic;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Encapsulates an imaged collection of frames.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
public interface IImageFrameCollection<TPixel> : IEnumerable<ImageFrame<TPixel>>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets the count.
/// </summary>
int Count { get; }
/// <summary>
/// Gets the root frame.
/// </summary>
ImageFrame<TPixel> RootFrame { get; }
/// <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>
ImageFrame<TPixel> this[int index] { get; set; }
/// <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>
int IndexOf(ImageFrame<TPixel> 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>
void Insert(int index, ImageFrame<TPixel> 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>
void RemoveAt(int index);
/// <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>
void Add(ImageFrame<TPixel> 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>
bool Contains(ImageFrame<TPixel> 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>
bool Remove(ImageFrame<TPixel> frame);
}
}

2
src/ImageSharp/Image/Image.LoadPixelData.cs

@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data));
var image = new Image<TPixel>(config, width, height);
SpanHelper.Copy(data, image.GetPixelSpan(), count);
SpanHelper.Copy(data, image.Frames.RootFrame.GetPixelSpan(), count);
return image;
}

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.");
}
}
}
}

91
src/ImageSharp/Image/ImageExtensions.cs

@ -18,26 +18,6 @@ namespace SixLabors.ImageSharp
/// </summary>
public static partial class ImageExtensions
{
/// <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 ImageBase<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> new Rectangle(0, 0, 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 ImageBase<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> new Size(source.Width, source.Height);
#if !NETSTANDARD1_1
/// <summary>
/// Saves the image to the given stream using the currently loaded image format.
@ -52,12 +32,12 @@ namespace SixLabors.ImageSharp
Guard.NotNullOrEmpty(filePath, nameof(filePath));
string ext = Path.GetExtension(filePath).Trim('.');
IImageFormat format = source.Configuration.FindFormatByFileExtension(ext);
IImageFormat format = source.GetConfiguration().FindFormatByFileExtension(ext);
if (format == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:");
foreach (IImageFormat fmt in source.Configuration.ImageFormats)
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{
stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
}
@ -65,13 +45,13 @@ namespace SixLabors.ImageSharp
throw new NotSupportedException(stringBuilder.ToString());
}
IImageEncoder encoder = source.Configuration.FindEncoder(format);
IImageEncoder encoder = source.GetConfiguration().FindEncoder(format);
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.Configuration.ImageEncoders)
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageEncoders)
{
stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
}
@ -94,7 +74,7 @@ namespace SixLabors.ImageSharp
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(encoder, nameof(encoder));
using (Stream fs = source.Configuration.FileSystem.Create(filePath))
using (Stream fs = source.GetConfiguration().FileSystem.Create(filePath))
{
source.Save(fs, encoder);
}
@ -113,14 +93,14 @@ namespace SixLabors.ImageSharp
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(format, nameof(format));
IImageEncoder encoder = source.Configuration.FindEncoder(format);
IImageEncoder encoder = source.GetConfiguration().FindEncoder(format);
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.Configuration.ImageEncoders)
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageEncoders)
{
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
}
@ -131,6 +111,17 @@ 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>
/// <returns>A copy of the pixel data as bytes from this frame.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static byte[] SavePixelData<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.GetPixelSpan().AsBytes().ToArray();
/// <summary>
/// Saves the raw image to the given bytes.
/// </summary>
@ -138,7 +129,18 @@ namespace SixLabors.ImageSharp
/// <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>
internal static void SavePixelData<TPixel>(this Image<TPixel> source, byte[] buffer)
public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, byte[] buffer)
where TPixel : struct, IPixel<TPixel>
=> SavePixelData(source, new Span<byte>(buffer));
/// <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>
private static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, Span<byte> buffer)
where TPixel : struct, IPixel<TPixel>
{
Span<byte> byteBuffer = source.GetPixelSpan().AsBytes();
@ -147,6 +149,39 @@ namespace SixLabors.ImageSharp
byteBuffer.CopyTo(buffer);
}
/// <summary>
/// Saves the raw image to the given bytes.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>A copy of the pixel data from the first frame as bytes.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static byte[] SavePixelData<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData();
/// <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 Image<TPixel> source, byte[] buffer)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData(buffer);
/// <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>
private static void SavePixelData<TPixel>(this Image<TPixel> source, Span<byte> buffer)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData(buffer);
/// <summary>
/// Returns a Base64 encoded string from the given image.
/// </summary>

52
src/ImageSharp/Image/ImageFrame.LoadPixelData.cs

@ -0,0 +1,52 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <content>
/// Adds static methods allowing the creation of new image from raw pixel data.
/// </content>
public static partial class ImageFrame
{
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array in <typeparamref name="TPixel"/> format.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="width">The width of the final image.</param>
/// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static ImageFrame<TPixel> LoadPixelData<TPixel>(Span<byte> data, int width, int height)
where TPixel : struct, IPixel<TPixel>
=> LoadPixelData(data.NonPortableCast<byte, TPixel>(), width, height);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the raw <typeparamref name="TPixel"/> data.
/// </summary>
/// <param name="data">The Span containing the image Pixel data.</param>
/// <param name="width">The width of the final image.</param>
/// <param name="height">The height of the final image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static ImageFrame<TPixel> LoadPixelData<TPixel>(Span<TPixel> data, int width, int height)
where TPixel : struct, IPixel<TPixel>
{
int count = width * height;
Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data));
var image = new ImageFrame<TPixel>(width, height);
SpanHelper.Copy(data, image.GetPixelSpan(), count);
return image;
}
}
}

166
src/ImageSharp/Image/ImageFrameCollection.cs

@ -0,0 +1,166 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections;
using System.Collections.Generic;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Encapsulates an imaged collection of frames.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
internal sealed class ImageFrameCollection<TPixel> : IImageFrameCollection<TPixel>, IDisposable
where TPixel : struct, IPixel<TPixel>
{
private readonly IList<ImageFrame<TPixel>> frames = new List<ImageFrame<TPixel>>();
internal ImageFrameCollection(int width, int height)
{
this.Add(new ImageFrame<TPixel>(width, height));
}
internal ImageFrameCollection(IEnumerable<ImageFrame<TPixel>> frames)
{
Guard.NotNullOrEmpty(frames, nameof(frames));
foreach (ImageFrame<TPixel> f in frames)
{
this.Add(f);
}
}
/// <summary>
/// Gets the count.
/// </summary>
public int Count => this.frames.Count;
/// <summary>
/// Gets the root frame.
/// </summary>
public ImageFrame<TPixel> RootFrame => this.frames.Count > 0 ? this.frames[0] : null;
/// <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 => this.frames[index];
set
{
this.ValidateFrame(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.ValidateFrame(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.Count == 1)
{
throw new InvalidOperationException("Cannot remove last frame.");
}
this.frames.RemoveAt(index);
}
/// <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.ValidateFrame(frame);
this.frames.Add(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.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();
private void ValidateFrame(ImageFrame<TPixel> frame)
{
Guard.NotNull(frame, nameof(frame));
if (this.Count != 0)
{
if (this.RootFrame.Width != frame.Width || this.RootFrame.Height != frame.Height)
{
throw new ArgumentException("Frame must have the same dimensions as the image.", nameof(frame));
}
}
}
/// <inheritdoc/>
public void Dispose()
{
foreach (ImageFrame<TPixel> f in this.frames)
{
f.Dispose();
}
this.frames.Clear();
}
}
}

202
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,174 @@ 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> : IPixelSource<TPixel>, IDisposable
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;
/// <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.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.MetaData = new ImageFrameMetaData();
this.pixelBuffer = new Buffer2D<TPixel>(source.pixelBuffer.Width, source.pixelBuffer.Height);
source.pixelBuffer.Span.CopyTo(this.pixelBuffer.Span);
this.MetaData = source.MetaData.Clone();
}
/// <inheritdoc/>
Buffer2D<TPixel> IPixelSource<TPixel>.PixelBuffer => this.pixelBuffer;
/// <summary>
/// Gets the width.
/// </summary>
public int Width => this.pixelBuffer.Width;
/// <summary>
/// Gets the height.
/// </summary>
public int Height => this.pixelBuffer.Height;
/// <summary>
/// Gets the meta data of the frame.
/// </summary>
public ImageFrameMetaData MetaData { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}"/> class.
/// Gets or sets the pixel at the specified position.
/// </summary>
/// <param name="image">The image to create the frame from.</param>
internal ImageFrame(ImageBase<TPixel> image)
: base(image)
/// <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>
private ImageFrame(ImageFrame<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.CopyProperties(image);
return ref this.pixelBuffer[x, y];
}
/// <summary>
/// Gets the meta data of the frame.
/// Locks the image providing access to the pixels.
/// <remarks>
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
public ImageFrameMetaData MetaData { get; private set; }
/// <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));
// Push my memory into the accessor (which in turn unpins the old buffer ready for the images use)
Buffer2D<TPixel> newPixels = pixelSource.SwapBufferOwnership(this.pixelBuffer);
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));
ComparableExtensions.Swap(ref this.pixelBuffer, ref pixelSource.pixelBuffer);
}
/// <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 +207,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,12 +215,12 @@ namespace SixLabors.ImageSharp
Parallel.For(
0,
target.Height,
this.Configuration.ParallelOptions,
Configuration.Default.ParallelOptions,
y =>
{
for (int x = 0; x < target.Width; x++)
{
TPixel2 color = default(TPixel2);
var color = default(TPixel2);
color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4()));
targetPixels[x, y] = color;
}
@ -131,28 +234,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);
}
}
}

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

@ -3,16 +3,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
@ -20,9 +17,12 @@ 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> : IDisposable, IConfigurable
where TPixel : struct, IPixel<TPixel>
{
private Configuration configuration;
private ImageFrameCollection<TPixel> frames;
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// with the height and the width of the image.
@ -59,41 +59,41 @@ 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 = configuration ?? Configuration.Default;
this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(width, height);
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// by making a copy from another image.
/// Initializes a new instance of the <see cref="Image{TPixel}" /> class
/// with the height and the width of the image.
/// </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="configuration">The configuration providing initialization code which allows extending the library.</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, ImageMetaData metadata, IEnumerable<ImageFrame<TPixel>> frames)
{
foreach (ImageFrame<TPixel> frame in other.Frames)
{
if (frame != null)
{
this.Frames.Add(frame.Clone());
}
}
this.configuration = configuration ?? Configuration.Default;
this.MetaData = metadata ?? new ImageMetaData();
this.CopyProperties(other);
this.frames = new ImageFrameCollection<TPixel>(frames);
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// by making a copy from another image.
/// Gets the pixel 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(ImageBase<TPixel> other)
: base(other)
{
}
Configuration IConfigurable.Configuration => this.configuration;
/// <summary>
/// Gets the width.
/// </summary>
public int Width => this.frames.RootFrame.Width;
/// <summary>
/// Gets the height.
/// </summary>
public int Height => this.frames.RootFrame.Height;
/// <summary>
/// Gets the meta data of the image.
@ -101,10 +101,27 @@ 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 IImageFrameCollection<TPixel> Frames => this.frames;
/// <summary>
/// Gets the root frame.
/// </summary>
/// <value>The list of frame images.</value>
public IList<ImageFrame<TPixel>> Frames { get; } = new List<ImageFrame<TPixel>>();
private IPixelSource<TPixel> PixelSource => this.frames?.RootFrame ?? throw new ObjectDisposedException(nameof(Image<TPixel>));
/// <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]
{
get => this.PixelSource.PixelBuffer[x, y];
set => this.PixelSource.PixelBuffer[x, y] = value;
}
/// <summary>
/// Saves the image to the given stream using the given image encoder.
@ -124,9 +141,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.MetaData.Clone(), frames);
}
/// <inheritdoc/>
@ -143,79 +162,32 @@ 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.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();
this.frames.Dispose();
}
/// <summary>
/// Copies the properties from the other <see cref="IImage"/>.
/// 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 <see cref="IImage"/> to copy the properties from.
/// </param>
private void CopyProperties(IImage other)
/// <param name="pixelSource">The pixel source.</param>
internal void SwapPixelsBuffers(Image<TPixel> pixelSource)
{
this.MetaData = new ImageMetaData(other.MetaData);
Guard.NotNull(pixelSource, nameof(pixelSource));
for (int i = 0; i < this.frames.Count; i++)
{
this.frames[i].SwapPixelsBuffers(pixelSource.frames[i]);
}
}
}
}

54
src/ImageSharp/Image/PixelAccessorExtensions.cs

@ -0,0 +1,54 @@
// 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.Advanced;
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 IPixelSource<TPixel> frame)
where TPixel : struct, IPixel<TPixel>
{
return new PixelAccessor<TPixel>(frame);
}
/// <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="image">The image.</param>
/// <returns>
/// The <see cref="PixelAccessor{TPixel}" />
/// </returns>
internal static PixelAccessor<TPixel> Lock<TPixel>(this Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
return image.Frames.RootFrame.Lock();
}
}
}

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

@ -4,7 +4,7 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using Unsafe = System.Runtime.CompilerServices.Unsafe;
@ -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,13 @@ 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(IPixelSource<TPixel> image)
{
Guard.NotNull(image, nameof(image));
Guard.MustBeGreaterThan(image.Width, 0, "image width");
Guard.MustBeGreaterThan(image.Height, 0, "image height");
Guard.MustBeGreaterThan(image.PixelBuffer.Width, 0, "image width");
Guard.MustBeGreaterThan(image.PixelBuffer.Height, 0, "image height");
this.SetPixelBufferUnsafe(image.Width, image.Height, image.PixelBuffer);
this.ParallelOptions = image.Configuration.ParallelOptions;
this.SetPixelBufferUnsafe(image.PixelBuffer, false);
}
/// <summary>
@ -56,7 +56,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,15 +66,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.ParallelOptions = Configuration.Default.ParallelOptions;
this.SetPixelBufferUnsafe(pixels, ownedBuffer);
}
/// <summary>
@ -100,21 +99,12 @@ namespace SixLabors.ImageSharp
/// </summary>
public int RowStride { get; private set; }
/// <summary>
/// Gets the width of the image.
/// </summary>
/// <inheritdoc />
public int Width { get; private set; }
/// <summary>
/// Gets the height of the image.
/// </summary>
/// <inheritdoc />
public int Height { get; private set; }
/// <summary>
/// Gets the global parallel options for processing tasks in parallel.
/// </summary>
public ParallelOptions ParallelOptions { get; }
/// <inheritdoc />
Span<TPixel> IBuffer2D<TPixel>.Span => this.PixelBuffer;
@ -143,12 +133,10 @@ namespace SixLabors.ImageSharp
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <inheritdoc />
public void Dispose()
{
if (this.isDisposed)
if (this.isDisposed || !this.ownedBuffer)
{
return;
}
@ -236,15 +224,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);
Buffer2D<TPixel> oldPixels = this.PixelBuffer;
this.SetPixelBufferUnsafe(pixels, this.ownedBuffer);
return oldPixels;
}
@ -412,23 +398,18 @@ 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)
/// <param name="ownedBuffer">if set to <c>true</c> then this instance ownes the buffer and thus should dispose of it afterwards.</param>
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;
}

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

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

@ -4,6 +4,7 @@
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Dithering;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -54,13 +55,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, Configuration configuration)
{
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle);
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration);
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
int startY = interest.Y;

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

@ -5,6 +5,7 @@ using System;
using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Dithering;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -62,13 +63,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, Configuration configuration)
{
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle);
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration);
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
int startY = interest.Y;

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

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -27,21 +27,17 @@ 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.");
}
Configuration configuration = source.GetConfiguration();
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];
ImageFrame<TPixel> clonedFrame = clone.Frames[i];
this.BeforeApply(sourceFrame, clonedFrame, sourceRectangle);
this.OnApply(sourceFrame, clonedFrame, sourceRectangle);
this.AfterApply(sourceFrame, clonedFrame, sourceRectangle);
this.BeforeApply(sourceFrame, clonedFrame, sourceRectangle, configuration);
this.OnApply(sourceFrame, clonedFrame, sourceRectangle, configuration);
this.AfterApply(sourceFrame, clonedFrame, sourceRectangle, configuration);
}
this.AfterImageApply(source, clone, sourceRectangle);
@ -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);
}
}
@ -107,33 +99,30 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="destination">The cloned/destination 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 virtual void BeforeApply(ImageBase<TPixel> source, ImageBase<TPixel> destination, Rectangle sourceRectangle)
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected virtual void BeforeApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
{
}
/// <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="destination">The cloned/destination 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, ImageBase<TPixel> destination, Rectangle sourceRectangle);
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected abstract void OnApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration);
/// <summary>
/// This method is called after the process is applied to prepare the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="destination">The cloned/destination 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 virtual void AfterApply(ImageBase<TPixel> source, ImageBase<TPixel> destination, Rectangle sourceRectangle)
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected virtual void AfterApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
{
}

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, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);

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

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

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

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

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

@ -50,9 +50,9 @@ 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, Configuration configuration)
{
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle);
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration);
}
/// <summary>

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, Configuration configuration)
{
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,
configuration.ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);

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

@ -4,6 +4,8 @@
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -39,11 +41,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, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
ParallelOptions parallelOptions = source.Configuration.ParallelOptions;
ParallelOptions parallelOptions = configuration.ParallelOptions;
using (var firstPassPixels = new PixelAccessor<TPixel>(width, height))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
@ -54,7 +56,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, Configuration configuration)
{
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,
configuration.ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);

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

@ -40,17 +40,17 @@ 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, Configuration configuration)
{
new Convolution2DProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle);
new Convolution2DProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration);
}
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
if (this.Grayscale)
{
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle);
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration);
}
}
}

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

@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -61,16 +62,16 @@ 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, Configuration configuration)
{
if (this.Grayscale)
{
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle);
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration);
}
}
/// <inheritdoc />
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
Fast2DArray<float>[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast };
@ -86,9 +87,9 @@ 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);
new ConvolutionProcessor<TPixel>(kernels[0]).Apply(source, sourceRectangle, configuration);
if (kernels.Length == 1)
{
@ -113,9 +114,9 @@ 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);
new ConvolutionProcessor<TPixel>(kernels[i]).Apply(pass, sourceRectangle, configuration);
using (PixelAccessor<TPixel> passPixels = pass.Lock())
using (PixelAccessor<TPixel> targetPixels = source.Lock())
@ -123,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
int offsetY = y - shiftY;

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

@ -33,18 +33,18 @@ 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, Configuration configuration)
{
if (this.Grayscale)
{
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle);
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration);
}
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
new ConvolutionProcessor<TPixel>(this.KernelXY).Apply(source, sourceRectangle);
new ConvolutionProcessor<TPixel>(this.KernelXY).Apply(source, sourceRectangle, configuration);
}
}
}

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

@ -85,9 +85,9 @@ 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, Configuration configuration)
{
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle);
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration);
}
/// <summary>

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

@ -87,9 +87,9 @@ 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, Configuration configuration)
{
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle);
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration);
}
/// <summary>

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, Configuration configuration)
{
// 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, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
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, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
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, Configuration configuration)
{
float brightness = this.Value / 100F;
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
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, Configuration configuration)
{
float contrast = (100F + this.Value) / 100F;
@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
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, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
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, Configuration configuration)
{
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,
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, Configuration configuration)
{
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,
configuration.ParallelOptions,
y =>
{
int offsetY = y - startY;

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

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -20,18 +20,12 @@ namespace SixLabors.ImageSharp.Processing
{
try
{
Configuration config = source.GetConfiguration();
this.BeforeImageApply(source, sourceRectangle);
this.BeforeApply(source, sourceRectangle);
this.OnApply(source, sourceRectangle);
this.AfterApply(source, sourceRectangle);
foreach (ImageFrame<TPixel> sourceFrame in source.Frames)
{
this.BeforeApply(sourceFrame, sourceRectangle);
this.OnApply(sourceFrame, sourceRectangle);
this.AfterApply(sourceFrame, sourceRectangle);
this.Apply(sourceFrame, sourceRectangle, config);
}
this.AfterImageApply(source, sourceRectangle);
@ -53,13 +47,14 @@ 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)
/// <param name="configuration">The configuration.</param>
public void Apply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
try
{
this.BeforeApply(source, sourceRectangle);
this.OnApply(source, sourceRectangle);
this.AfterApply(source, sourceRectangle);
this.BeforeApply(source, sourceRectangle, configuration);
this.OnApply(source, sourceRectangle, configuration);
this.AfterApply(source, sourceRectangle, configuration);
}
#if DEBUG
catch (Exception)
@ -77,9 +72,7 @@ namespace SixLabors.ImageSharp.Processing
/// This method is called before the process is applied to prepare the processor.
/// </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>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
protected virtual void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
}
@ -88,31 +81,28 @@ namespace SixLabors.ImageSharp.Processing
/// This method is called before the process is applied to prepare the processor.
/// </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 virtual void BeforeApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected virtual void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
}
/// <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);
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected abstract void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration);
/// <summary>
/// This method is called after the process is applied to prepare the processor.
/// </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 virtual void AfterApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected virtual void AfterApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
}
@ -120,9 +110,7 @@ namespace SixLabors.ImageSharp.Processing
/// This method is called after the process is applied to prepare the processor.
/// </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>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
protected virtual void AfterImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
}

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

@ -5,6 +5,7 @@ using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -51,7 +52,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, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -92,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
using (var amounts = new Buffer<float>(width))

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

@ -5,6 +5,7 @@ using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -70,7 +71,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, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -113,7 +114,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
using (var amounts = new Buffer<float>(width))

88
src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs

@ -22,55 +22,55 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> sourceBase, Rectangle sourceRectangle)
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
// can only apply to the origional image
var source = sourceBase as Image<TPixel>;
if (source != null)
Orientation orientation = GetExifOrientation(source);
switch (orientation)
{
Orientation orientation = GetExifOrientation(source);
switch (orientation)
{
case Orientation.TopRight:
new FlipProcessor<TPixel>(FlipType.Horizontal).Apply(source, sourceRectangle);
break;
case Orientation.BottomRight:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate180, Expand = false }.Apply(source, sourceRectangle);
break;
case Orientation.BottomLeft:
new FlipProcessor<TPixel>(FlipType.Vertical).Apply(source, sourceRectangle);
break;
case Orientation.LeftTop:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle);
new FlipProcessor<TPixel>(FlipType.Horizontal).Apply(source, sourceRectangle);
break;
case Orientation.RightTop:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle);
break;
case Orientation.RightBottom:
new FlipProcessor<TPixel>(FlipType.Vertical).Apply(source, sourceRectangle);
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle);
break;
case Orientation.LeftBottom:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle);
break;
case Orientation.Unknown:
case Orientation.TopLeft:
default:
break;
}
case Orientation.TopRight:
new FlipProcessor<TPixel>(FlipType.Horizontal).Apply(source, sourceRectangle);
break;
case Orientation.BottomRight:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate180, Expand = false }.Apply(source, sourceRectangle);
break;
case Orientation.BottomLeft:
new FlipProcessor<TPixel>(FlipType.Vertical).Apply(source, sourceRectangle);
break;
case Orientation.LeftTop:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle);
new FlipProcessor<TPixel>(FlipType.Horizontal).Apply(source, sourceRectangle);
break;
case Orientation.RightTop:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle);
break;
case Orientation.RightBottom:
new FlipProcessor<TPixel>(FlipType.Vertical).Apply(source, sourceRectangle);
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle);
break;
case Orientation.LeftBottom:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle);
break;
case Orientation.Unknown:
case Orientation.TopLeft:
default:
break;
}
}
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> sourceBase, Rectangle sourceRectangle, Configuration config)
{
// all processing happens at the image level within BeforeImageApply();
}
/// <summary>
/// Returns the current EXIF orientation
/// </summary>

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, Configuration configuration)
{
if (this.CropRectangle == sourceRectangle)
{
@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(minX);

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

@ -34,15 +34,15 @@ 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, Configuration configuration)
{
using (ImageBase<TPixel> temp = source.Clone())
using (ImageFrame<TPixel> temp = source.Clone())
{
// Detect the edges.
new SobelProcessor<TPixel>().Apply(temp, sourceRectangle);
new SobelProcessor<TPixel>().Apply(temp, sourceRectangle, configuration);
// Apply threshold binarization filter.
new BinaryThresholdProcessor<TPixel>(this.Threshold).Apply(temp, sourceRectangle);
new BinaryThresholdProcessor<TPixel>(this.Threshold).Apply(temp, sourceRectangle, configuration);
// Search for the first white pixels
Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0);
@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
return;
}
new CropProcessor<TPixel>(rectangle).Apply(source, sourceRectangle);
new CropProcessor<TPixel>(rectangle).Apply(source, sourceRectangle, configuration);
}
}
}

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

@ -32,16 +32,16 @@ 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, Configuration configuration)
{
switch (this.FlipType)
{
// No default needed as we have already set the pixels.
case FlipType.Vertical:
this.FlipX(source);
this.FlipX(source, configuration);
break;
case FlipType.Horizontal:
this.FlipY(source);
this.FlipY(source, configuration);
break;
}
}
@ -51,7 +51,8 @@ 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)
/// <param name="configuration">The configuration.</param>
private void FlipX(ImageFrame<TPixel> source, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
@ -62,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
halfHeight,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
int newY = height - y - 1;
@ -84,7 +85,8 @@ 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)
/// <param name="configuration">The configuration.</param>
private void FlipY(ImageFrame<TPixel> source, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
@ -95,7 +97,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
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);

6
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, Configuration configuration)
{
if (!(this.Sampler is NearestNeighborResampler))
{
@ -152,9 +152,9 @@ 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, Configuration configuration)
{
base.AfterApply(source, destination, sourceRectangle);
base.AfterApply(source, destination, sourceRectangle, configuration);
this.HorizontalWeights?.Dispose();
this.HorizontalWeights = null;
this.VerticalWeights?.Dispose();

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

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
@ -51,24 +53,20 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
{
Configuration config = source.GetConfiguration();
// We will always be creating the clone even for mutate because thats the way this base processor works
// ------------
// 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);
}
IEnumerable<ImageFrame<TPixel>> 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>(config, 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, Configuration configuration)
{
// Jump out, we'll deal with that later.
if (source.Width == cloned.Width && source.Height == cloned.Height && sourceRectangle == this.ResizeRectangle)
@ -101,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
// Y coordinates of source points
@ -130,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
sourceRectangle.Bottom,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
// TODO: Without Parallel.For() this buffer object could be reused:
@ -163,7 +161,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
// Ensure offsets are normalised for cropping and padding.

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

@ -5,6 +5,7 @@ using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -34,9 +35,9 @@ 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, Configuration configuration)
{
if (this.OptimizedApply(source))
if (this.OptimizedApply(source, configuration))
{
return;
}
@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
Span<TPixel> targetRow = targetPixels.GetRowSpan(y);
@ -72,7 +73,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, Configuration configuration)
{
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)
{
@ -90,8 +91,11 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees.
/// </summary>
/// <param name="source">The source image.</param>
/// <returns>The <see cref="bool"/></returns>
private bool OptimizedApply(ImageBase<TPixel> source)
/// <param name="configuration">The configuration.</param>
/// <returns>
/// The <see cref="bool" />
/// </returns>
private bool OptimizedApply(ImageFrame<TPixel> source, Configuration configuration)
{
if (MathF.Abs(this.Angle) < Constants.Epsilon)
{
@ -101,19 +105,19 @@ namespace SixLabors.ImageSharp.Processing.Processors
if (MathF.Abs(this.Angle - 90) < Constants.Epsilon)
{
this.Rotate90(source);
this.Rotate90(source, configuration);
return true;
}
if (MathF.Abs(this.Angle - 180) < Constants.Epsilon)
{
this.Rotate180(source);
this.Rotate180(source, configuration);
return true;
}
if (MathF.Abs(this.Angle - 270) < Constants.Epsilon)
{
this.Rotate270(source);
this.Rotate270(source, configuration);
return true;
}
@ -124,7 +128,8 @@ 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)
/// <param name="configuration">The configuration.</param>
private void Rotate270(ImageFrame<TPixel> source, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
@ -136,7 +141,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
for (int x = 0; x < width; x++)
@ -157,7 +162,8 @@ 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)
/// <param name="configuration">The configuration.</param>
private void Rotate180(ImageFrame<TPixel> source, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
@ -167,7 +173,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
@ -187,7 +193,8 @@ 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)
/// <param name="configuration">The configuration.</param>
private void Rotate90(ImageFrame<TPixel> source, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
@ -197,7 +204,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);

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

@ -4,6 +4,8 @@
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -38,7 +40,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, Configuration configuration)
{
int height = this.CanvasRectangle.Height;
int width = this.CanvasRectangle.Width;
@ -50,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Parallel.For(
0,
height,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
Span<TPixel> targetRow = targetPixels.GetRowSpan(y);
@ -71,7 +73,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, Configuration configuration)
{
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.

5
src/ImageSharp/Quantizers/Quantize.cs

@ -3,6 +3,7 @@
using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Quantizers;
@ -65,7 +66,7 @@ namespace SixLabors.ImageSharp
Parallel.For(
0,
pixels.Height,
img.Configuration.ParallelOptions,
img.GetConfiguration().ParallelOptions,
y =>
{
for (int x = 0; x < pixels.Width; x++)
@ -76,7 +77,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/Image/CopyPixels.cs

@ -103,8 +103,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Image
Configuration.Default.ParallelOptions,
y =>
{
Span<Rgba32> sourceRow = source.GetPixelRowSpan(y);
Span<Rgba32> targetRow = target.GetPixelRowSpan(y);
Span<Rgba32> sourceRow = source.Frames.RootFrame.GetPixelRowSpan(y);
Span<Rgba32> targetRow = target.Frames.RootFrame.GetPixelRowSpan(y);
for (int x = 0; x < source.Width; x++)
{

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

@ -17,6 +17,8 @@ namespace SixLabors.ImageSharp.Benchmarks
using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Advanced;
public class Glow : BenchmarkBase
{
@ -35,7 +37,7 @@ namespace SixLabors.ImageSharp.Benchmarks
{
using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
{
bulk.Apply(image, image.Bounds());
this.bulk.Apply(image, image.Bounds());
return new CoreSize(image.Width, image.Height);
}
}
@ -45,7 +47,7 @@ namespace SixLabors.ImageSharp.Benchmarks
{
using (Image<Rgba32> image = new Image<Rgba32>(800, 800))
{
parallel.Apply(image, image.Bounds());
this.parallel.Apply(image, image.Bounds());
return new CoreSize(image.Width, image.Height);
}
}
@ -73,7 +75,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, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
@ -112,7 +114,7 @@ namespace SixLabors.ImageSharp.Benchmarks
Parallel.For(
minY,
maxY,
source.Configuration.ParallelOptions,
configuration.ParallelOptions,
y =>
{
int offsetY = y - startY;

6
tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs

@ -56,9 +56,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
string imageFile = provider.SourceFileOrDescription;
using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile))
using (var pp = new JpegImagePostProcessor(decoder))
using (var image = new Image<Rgba32>(decoder.ImageWidth, decoder.ImageHeight))
using (var imageFrame = new ImageFrame<Rgba32>(decoder.ImageWidth, decoder.ImageHeight))
{
pp.DoPostProcessorStep(image);
pp.DoPostProcessorStep(imageFrame);
JpegComponentPostProcessor[] cp = pp.ComponentProcessors;
@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
using (var pp = new JpegImagePostProcessor(decoder))
using (var image = new Image<Rgba32>(decoder.ImageWidth, decoder.ImageHeight))
{
pp.PostProcess(image);
pp.PostProcess(image.Frames.RootFrame);
image.DebugSave(provider);

219
tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs

@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
public class ImageFramesCollectionTests
{
[Fact]
public void ImageFramesaLwaysHaveOneFrame()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
Assert.Equal(1, collection.Count);
}
[Fact]
public void AddNewFrame_FramesMustHaveSameSize()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
{
collection.Add(new ImageFrame<Rgba32>(1, 1));
});
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void AddNewFrame_FramesNotBeNull()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{
collection.Add(null);
});
Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void InsertNewFrame_FramesMustHaveSameSize()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
{
collection.Insert(1, new ImageFrame<Rgba32>(1, 1));
});
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void InsertNewFrame_FramesNotBeNull()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{
collection.Insert(1, null);
});
Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void SetFrameAtIndex_FramesMustHaveSameSize()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
{
collection[0] = new ImageFrame<Rgba32>(1, 1);
});
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void SetFrameAtIndex_FramesNotBeNull()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{
collection[0] = null;
});
Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void Constructor_FramesMustHaveSameSize()
{
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(1,1),
});
});
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void RemoveAtFrame_ThrowIfRemovingLastFrame()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10)
});
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() =>
{
collection.RemoveAt(0);
});
Assert.Equal("Cannot remove last frame.", ex.Message);
}
[Fact]
public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
collection.RemoveAt(0);
Assert.Equal(1, collection.Count);
}
[Fact]
public void RemoveFrame_ThrowIfRemovingLastFrame()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10)
});
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() =>
{
collection.Remove(collection[0]);
});
Assert.Equal("Cannot remove last frame.", ex.Message);
}
[Fact]
public void RemoveFrame_CanRemoveFrameZeroIfMultipleFramesExist()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
collection.Remove(collection[0]);
Assert.Equal(1, collection.Count);
}
[Fact]
public void RootFrameIsFrameAtIndexZero()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
Assert.Equal(collection.RootFrame, collection[0]);
}
[Fact]
public void ConstructorPopulatesFrames()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
Assert.Equal(2, collection.Count);
}
[Fact]
public void DisposeClearsCollection()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
collection.Dispose();
Assert.Equal(0, collection.Count);
}
[Fact]
public void Dispose_DisposesAllInnerFrames()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
IPixelSource<Rgba32>[] framesSnapShot = collection.OfType<IPixelSource<Rgba32>>().ToArray();
collection.Dispose();
Assert.All(framesSnapShot, f =>
{
// the pixel source of the frame is null after its been disposed.
Assert.Null(f.PixelBuffer);
});
}
}
}

2
tests/ImageSharp.Tests/Image/ImageLoadTests.cs

@ -326,7 +326,7 @@ namespace SixLabors.ImageSharp.Tests
using (Image<Rgba32> img = image1Provider.GetImage())
{
Assert.Equal(166036, img.GetPixelSpan().Length);
Assert.Equal(166036, img.Frames.RootFrame.GetPixelSpan().Length);
}
}

5
tests/ImageSharp.Tests/Image/ImageRotationTests.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Helpers;
using SixLabors.Primitives;
using Xunit;
@ -48,9 +49,9 @@ namespace SixLabors.ImageSharp.Tests
var file = TestFile.Create(TestImages.Bmp.Car);
using (var image = Image.Load<Rgba32>(file.FullPath))
{
Size original = image.Bounds().Size;
Size original = image.Size();
image.Mutate(x => x.Rotate(angle));
return (original, image.Bounds().Size);
return (original, image.Size());
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save