Browse Source

Merge branch 'af/non-generic-image-baseclass' into af/general-color-type

pull/908/head
Anton Firszov 7 years ago
parent
commit
b7fefaef0a
  1. 16
      README.md
  2. 18
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs
  3. 6
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
  4. 16
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs
  5. 6
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs
  6. 8
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs
  7. 6
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
  8. 21
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
  9. 5
      src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs
  10. 27
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
  11. 68
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs

16
README.md

@ -22,7 +22,7 @@ Designed to democratize image processing, ImageSharp brings you an incredibly po
Compared to `System.Drawing` we have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments.
Built against .NET Standard 1.3 ImageSharp can be used in device, cloud, and embedded/IoT scenarios.
Built against .NET Standard 1.3, ImageSharp can be used in device, cloud, and embedded/IoT scenarios.
### Documentation
For all SixLabors projects, including ImageSharp:
@ -41,15 +41,15 @@ Install stable releases via Nuget; development releases are available via MyGet.
The **ImageSharp** library is made up of multiple packages:
- **SixLabors.ImageSharp**
- Contains the generic `Image<TPixel>` class, PixelFormats, Primitives, Configuration, and other core functionality.
- The `IImageFormat` interface, Jpeg, Png, Bmp, and Gif formats.
- Transform methods like Resize, Crop, Skew, Rotate - Anything that alters the dimensions of the image.
- Non-transform methods like Gaussian Blur, Pixelate, Edge Detection - Anything that maintains the original image dimensions.
- Contains the generic `Image<TPixel>` class, PixelFormats, Primitives, Configuration, and other core functionality
- The `IImageFormat` interface, Jpeg, Png, Bmp, and Gif formats
- Transform methods like Resize, Crop, Skew, Rotate - anything that alters the dimensions of the image
- Non-transform methods like Gaussian Blur, Pixelate, Edge Detection - anything that maintains the original image dimensions
- **SixLabors.ImageSharp.Drawing**
- Brushes and various drawing algorithms, including drawing images.
- Brushes and various drawing algorithms, including drawing images
- Various vector drawing methods for drawing paths, polygons etc.
- Text drawing.
- Text drawing
### Build Status
@ -117,7 +117,7 @@ Alternatively, you can work from command line and/or with a lightweight editor o
- [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp)
- [.NET Core](https://www.microsoft.com/net/core#linuxubuntu)
To clone ImageSharp locally click the "Clone in Windows" button above or run the following git commands.
To clone ImageSharp locally, click the "Clone in Windows" button above or run the following git commands:
```bash
git clone https://github.com/SixLabors/ImageSharp

18
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs → src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs

@ -7,17 +7,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// Applies an adaptive histogram equalization to the image. The image is split up in tiles. For each tile a cumulative distribution function (cdf) is calculated.
/// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated.
/// </summary>
internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor
public class AdaptiveHistogramEqualizationProcessor : HistogramEqualizationProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="AdaptiveHistEqualizationProcessor"/> class.
/// Initializes a new instance of the <see cref="AdaptiveHistogramEqualizationProcessor"/> class.
/// </summary>
/// <param name="luminanceLevels">The number of different luminance levels. Typical values are 256 for 8-bit grayscale images
/// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="numberOfTiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param>
public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int numberOfTiles)
public AdaptiveHistogramEqualizationProcessor(
int luminanceLevels,
bool clipHistogram,
float clipLimitPercentage,
int numberOfTiles)
: base(luminanceLevels, clipHistogram, clipLimitPercentage)
{
this.NumberOfTiles = numberOfTiles;
@ -31,7 +35,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
{
return new AdaptiveHistEqualizationProcessor<TPixel>(this.LuminanceLevels, this.ClipHistogram, this.ClipLimitPercentage, this.NumberOfTiles);
return new AdaptiveHistogramEqualizationProcessor<TPixel>(
this.LuminanceLevels,
this.ClipHistogram,
this.ClipLimitPercentage,
this.NumberOfTiles);
}
}
}
}

6
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs

@ -21,18 +21,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AdaptiveHistEqualizationProcessor<TPixel> : HistogramEqualizationProcessor<TPixel>
internal class AdaptiveHistogramEqualizationProcessor<TPixel> : HistogramEqualizationProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="AdaptiveHistEqualizationProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="AdaptiveHistogramEqualizationProcessor{TPixel}"/> class.
/// </summary>
/// <param name="luminanceLevels">The number of different luminance levels. Typical values are 256 for 8-bit grayscale images
/// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="tiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param>
public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles)
public AdaptiveHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles)
: base(luminanceLevels, clipHistogram, clipLimitPercentage)
{
Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles));

16
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs → src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs

@ -6,17 +6,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary>
/// Applies an adaptive histogram equalization to the image using an sliding window approach.
/// </summary>
internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor
public class AdaptiveHistogramEqualizationSlidingWindowProcessor : HistogramEqualizationProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="AdaptiveHistEqualizationSWProcessor"/> class.
/// Initializes a new instance of the <see cref="AdaptiveHistogramEqualizationSlidingWindowProcessor"/> class.
/// </summary>
/// <param name="luminanceLevels">The number of different luminance levels. Typical values are 256 for 8-bit grayscale images
/// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="numberOfTiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param>
public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int numberOfTiles)
public AdaptiveHistogramEqualizationSlidingWindowProcessor(
int luminanceLevels,
bool clipHistogram,
float clipLimitPercentage,
int numberOfTiles)
: base(luminanceLevels, clipHistogram, clipLimitPercentage)
{
this.NumberOfTiles = numberOfTiles;
@ -30,7 +34,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
{
return new AdaptiveHistEqualizationSWProcessor<TPixel>(this.LuminanceLevels, this.ClipHistogram, this.ClipLimitPercentage, this.NumberOfTiles);
return new AdaptiveHistogramEqualizationSlidingWindowProcessor<TPixel>(
this.LuminanceLevels,
this.ClipHistogram,
this.ClipLimitPercentage,
this.NumberOfTiles);
}
}
}

6
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs

@ -20,18 +20,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// Applies an adaptive histogram equalization to the image using an sliding window approach.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AdaptiveHistEqualizationSWProcessor<TPixel> : HistogramEqualizationProcessor<TPixel>
internal class AdaptiveHistogramEqualizationSlidingWindowProcessor<TPixel> : HistogramEqualizationProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="AdaptiveHistEqualizationSWProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}"/> class.
/// </summary>
/// <param name="luminanceLevels">The number of different luminance levels. Typical values are 256 for 8-bit grayscale images
/// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="tiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param>
public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles)
public AdaptiveHistogramEqualizationSlidingWindowProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles)
: base(luminanceLevels, clipHistogram, clipLimitPercentage)
{
Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles));

8
src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs

@ -6,8 +6,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary>
/// Defines a global histogram equalization applicable to an <see cref="Image"/>.
/// </summary>
internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor
public class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="GlobalHistogramEqualizationProcessor"/> class.
/// </summary>
/// <param name="luminanceLevels">The number of luminance levels.</param>
/// <param name="clipHistogram">A value indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">The histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param>
public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage)
: base(luminanceLevels, clipHistogram, clipLimitPercentage)
{

6
src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary>
/// Defines a processor that normalizes the histogram of an image.
/// </summary>
internal abstract class HistogramEqualizationProcessor : IImageProcessor
public abstract class HistogramEqualizationProcessor : IImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="HistogramEqualizationProcessor"/> class.
@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
break;
case HistogramEqualizationMethod.AdaptiveTileInterpolation:
processor = new AdaptiveHistEqualizationProcessor(
processor = new AdaptiveHistogramEqualizationProcessor(
options.LuminanceLevels,
options.ClipHistogram,
options.ClipLimitPercentage,
@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
break;
case HistogramEqualizationMethod.AdaptiveSlidingWindow:
processor = new AdaptiveHistEqualizationSWProcessor(
processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor(
options.LuminanceLevels,
options.ClipHistogram,
options.ClipLimitPercentage,

21
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs

@ -25,18 +25,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
this.Definition = definition;
}
protected AffineTransformProcessor Definition { get; }
private Size TargetDimensions => this.Definition.TargetDimensions;
private Matrix3x2 TransformMatrix => this.Definition.TransformMatrix;
protected AffineTransformProcessor Definition { get; }
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
{
// We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames =
source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone()));
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(
x => new ImageFrame<TPixel>(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.Metadata.DeepClone(), frames);
@ -111,10 +111,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
var point = Vector2.Transform(new Vector2(x, y), matrix);
kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan);
kernel.Convolve(
point,
x,
ref ySpanRef,
ref xSpanRef,
source.PixelBuffer,
vectorSpan);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(
configuration,
vectorSpan,
targetRowSpan);
}
});
}

5
src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs

@ -61,7 +61,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> sourceBase, Rectangle sourceRectangle, Configuration config)
protected override void OnFrameApply(
ImageFrame<TPixel> sourceBase,
Rectangle sourceRectangle,
Configuration config)
{
// All processing happens at the image level within BeforeImageApply();
}

27
src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs

@ -33,15 +33,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
{
// We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames =
source.Frames.Select(x => new ImageFrame<TPixel>(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.Metadata.DeepClone()));
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(
x => new ImageFrame<TPixel>(
source.GetConfiguration(),
this.TargetDimensions.Width,
this.TargetDimensions.Height,
x.Metadata.DeepClone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.Metadata.DeepClone(), frames);
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Rectangle sourceRectangle,
Configuration configuration)
{
Matrix4x4 transformMatrix = this.definition.TransformMatrix;
@ -110,10 +118,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix);
kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan);
kernel.Convolve(
point,
x,
ref ySpanRef,
ref xSpanRef,
source.PixelBuffer,
vectorSpan);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(
configuration,
vectorSpan,
targetRowSpan);
}
});
}

68
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs

@ -27,18 +27,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private float Degrees { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
{
if (this.OptimizedApply(source, destination, configuration))
{
return;
}
base.OnFrameApply(source, destination, sourceRectangle, configuration);
}
/// <inheritdoc/>
protected override void AfterImageApply(Image<TPixel> source, Image<TPixel> destination, Rectangle sourceRectangle)
protected override void AfterImageApply(
Image<TPixel> source,
Image<TPixel> destination,
Rectangle sourceRectangle)
{
ExifProfile profile = destination.Metadata.ExifProfile;
if (profile is null)
@ -57,6 +49,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
base.AfterImageApply(source, destination, sourceRectangle);
}
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Rectangle sourceRectangle,
Configuration configuration)
{
if (this.OptimizedApply(source, destination, configuration))
{
return;
}
base.OnFrameApply(source, destination, sourceRectangle, configuration);
}
/// <summary>
/// Wraps a given angle in degrees so that it falls withing the 0-360 degree range
/// </summary>
@ -83,7 +90,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <returns>
/// The <see cref="bool" />
/// </returns>
private bool OptimizedApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
private bool OptimizedApply(
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Configuration configuration)
{
// Wrap the degrees to keep within 0-360 so we can apply optimizations when possible.
float degrees = WrapDegrees(this.Degrees);
@ -117,16 +127,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
/// <summary>
/// Rotates the image 270 degrees clockwise at the centre point.
/// Rotates the image 180 degrees clockwise at the centre point.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="destination">The destination image.</param>
/// <param name="configuration">The configuration.</param>
private void Rotate270(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
private void Rotate180(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
Rectangle destinationBounds = destination.Bounds();
ParallelHelper.IterateRows(
source.Bounds(),
@ -136,31 +145,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<TPixel> targetRow = destination.GetPixelRowSpan(height - y - 1);
for (int x = 0; x < width; x++)
{
int newX = height - y - 1;
newX = height - newX - 1;
int newY = width - x - 1;
if (destinationBounds.Contains(newX, newY))
{
destination[newX, newY] = sourceRow[x];
}
targetRow[width - x - 1] = sourceRow[x];
}
}
});
}
/// <summary>
/// Rotates the image 180 degrees clockwise at the centre point.
/// Rotates the image 270 degrees clockwise at the centre point.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="destination">The destination image.</param>
/// <param name="configuration">The configuration.</param>
private void Rotate180(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
private void Rotate270(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
Rectangle destinationBounds = destination.Bounds();
ParallelHelper.IterateRows(
source.Bounds(),
@ -170,11 +175,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<TPixel> targetRow = destination.GetPixelRowSpan(height - y - 1);
for (int x = 0; x < width; x++)
{
targetRow[width - x - 1] = sourceRow[x];
int newX = height - y - 1;
newX = height - newX - 1;
int newY = width - x - 1;
if (destinationBounds.Contains(newX, newY))
{
destination[newX, newY] = sourceRow[x];
}
}
}
});

Loading…
Cancel
Save