Browse Source

Performance improvements + tweaks

Fix issue #321


Former-commit-id: 9e975d942c48badeffa623cedfb5740645e4fd42
Former-commit-id: 30d157147ef9038228b2e283531ab383fdb4813d
Former-commit-id: 7c32b6c8b267e02c6e9f5cde9a2a2da2330e8971
af/merge-core
James Jackson-South 10 years ago
parent
commit
434f0088ad
  1. 9
      README.md
  2. 9
      src/ImageProcessorCore/Colors/Color.cs
  3. 32
      src/ImageProcessorCore/ImageExtensions.cs
  4. 4
      src/ImageProcessorCore/Samplers/Crop.cs
  5. 22
      tests/ImageProcessorCore.Benchmarks/Color/ColorEquality.cs
  6. 23
      tests/ImageProcessorCore.Benchmarks/Colors.cs
  7. 33
      tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs
  8. 44
      tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs
  9. 0
      tests/ImageProcessorCore.Benchmarks/Samplers/Resize.cs

9
README.md

@ -2,7 +2,7 @@
<img src="build/icons/imageprocessor-logo-512.png" width="128" height="128"/>
**ImageProcessorCore** is a new cross-platform 2D graphics API to allow the processing of images without the use of `System.Drawing`. It's still in early stages but progress has been pretty quick.
**ImageProcessorCore** is a new cross-platform 2D graphics API designed to allow the processing of images without the use of `System.Drawing`. It's still in early stages but progress has been pretty quick.
[![Build status](https://ci.appveyor.com/api/projects/status/8ypr7527dnao04yr/branch/Core?svg=true)](https://ci.appveyor.com/project/JamesSouth/imageprocessor/branch/Core)
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/JimBobSquarePants/ImageProcessor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@ -182,8 +182,11 @@ using (Image image = new Image(stream))
new Sobel { Greyscale = true }
};
image.Process(processors.ToArray())
.Save(output);
foreach (IImageProcessor processor in processors){
image.Process(processor)
.Save(output);
}
}
```
Individual processors can be initialised and apply processing against images. This allows nesting which will allow the powerful combination of processing methods:

9
src/ImageProcessorCore/Colors/Color.cs

@ -8,6 +8,7 @@ namespace ImageProcessorCore
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Represents a four-component color using red, green, blue, and alpha data.
@ -316,6 +317,7 @@ namespace ImageProcessorCore
/// <returns>
/// The <see cref="Color"/>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Color Average(Color first, Color second)
{
return new Color((first.backingVector + second.backingVector) * .5f);
@ -361,6 +363,7 @@ namespace ImageProcessorCore
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert.</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Color FromNonPremultiplied(Color color)
{
return new Color(FromNonPremultiplied(color.backingVector, color.A));
@ -383,6 +386,7 @@ namespace ImageProcessorCore
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert.</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Color ToNonPremultiplied(Color color)
{
float a = color.A;
@ -398,6 +402,7 @@ namespace ImageProcessorCore
/// Gets a <see cref="Vector4"/> representation for this <see cref="Color"/>.
/// </summary>
/// <returns>A <see cref="Vector4"/> representation for this object.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
return new Vector4(this.R, this.G, this.B, this.A);
@ -430,6 +435,7 @@ namespace ImageProcessorCore
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Color)
@ -447,6 +453,7 @@ namespace ImageProcessorCore
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Color other, float precision)
{
Vector4 result = Vector4.Abs(this.backingVector - other.backingVector);
@ -466,6 +473,7 @@ namespace ImageProcessorCore
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Compress(float signal)
{
if (signal <= 0.0031308f)
@ -485,6 +493,7 @@ namespace ImageProcessorCore
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Expand(float signal)
{
if (signal <= 0.04045f)

32
src/ImageProcessorCore/ImageExtensions.cs

@ -56,11 +56,11 @@ namespace ImageProcessorCore
/// <remarks>This method does not resize the target image.</remarks>
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="processors">Any processors to apply to the image.</param>
/// <param name="processor">The processor to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Process(this Image source, params IImageProcessor[] processors)
public static Image Process(this Image source, IImageProcessor processor)
{
return Process(source, source.Bounds, processors);
return Process(source, source.Bounds, processor);
}
/// <summary>
@ -71,15 +71,11 @@ namespace ImageProcessorCore
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <param name="processors">Any processors to apply to the image.</param>
/// <param name="processor">The processors to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Process(this Image source, Rectangle sourceRectangle, params IImageProcessor[] processors)
public static Image Process(this Image source, Rectangle sourceRectangle, IImageProcessor processor)
{
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (IImageProcessor filter in processors)
{
source = PerformAction(source, true, (sourceImage, targetImage) => filter.Apply(targetImage, sourceImage, sourceRectangle));
}
source = PerformAction(source, true, (sourceImage, targetImage) => processor.Apply(targetImage, sourceImage, sourceRectangle));
return source;
}
@ -93,11 +89,11 @@ namespace ImageProcessorCore
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="width">The target image width.</param>
/// <param name="height">The target image height.</param>
/// <param name="processors">Any processors to apply to the image.</param>
/// <param name="sampler">The processor to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Process(this Image source, int width, int height, params IImageSampler[] processors)
internal static Image Process(this Image source, int width, int height, IImageSampler sampler)
{
return Process(source, width, height, source.Bounds, default(Rectangle), processors);
return Process(source, width, height, source.Bounds, default(Rectangle), sampler);
}
/// <summary>
@ -117,15 +113,11 @@ namespace ImageProcessorCore
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
/// The image is scaled to fit the rectangle.
/// </param>
/// <param name="processors">Any processors to apply to the image.</param>
/// <param name="sampler">The processor to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Process(this Image source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle, params IImageSampler[] processors)
public static Image Process(this Image source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle, IImageSampler sampler)
{
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (IImageSampler sampler in processors)
{
source = PerformAction(source, false, (sourceImage, targetImage) => sampler.Apply(targetImage, sourceImage, width, height, targetRectangle, sourceRectangle));
}
source = PerformAction(source, false, (sourceImage, targetImage) => sampler.Apply(targetImage, sourceImage, width, height, targetRectangle, sourceRectangle));
return source;
}

4
src/ImageProcessorCore/Samplers/Crop.cs

@ -19,6 +19,8 @@ namespace ImageProcessorCore.Samplers
int targetBottom = targetRectangle.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
int sourceX = sourceRectangle.X;
int sourceY = sourceRectangle.Y;
Parallel.For(
startY,
@ -29,7 +31,7 @@ namespace ImageProcessorCore.Samplers
{
for (int x = startX; x < endX; x++)
{
target[x, y] = source[x + sourceRectangle.X, y + sourceRectangle.Y];
target[x, y] = source[x + sourceX, y + sourceY];
}
this.OnRowProcessed();

22
tests/ImageProcessorCore.Benchmarks/Color/ColorEquality.cs

@ -0,0 +1,22 @@
namespace ImageProcessorCore.Benchmarks
{
using BenchmarkDotNet.Attributes;
using CoreColor = ImageProcessorCore.Color;
using SystemColor = System.Drawing.Color;
public class ColorEquality
{
[Benchmark(Baseline = true, Description = "System.Drawing Color Equals")]
public bool SystemDrawingColorEqual()
{
return SystemColor.FromArgb(128, 128, 128, 128).Equals(SystemColor.FromArgb(128, 128, 128, 128));
}
[Benchmark(Description = "ImageProcessorCore Color Equals")]
public bool ColorEqual()
{
return new CoreColor(.5f, .5f, .5f, .5f).Equals(new CoreColor(.5f, .5f, .5f, .5f));
}
}
}

23
tests/ImageProcessorCore.Benchmarks/Colors.cs

@ -1,23 +0,0 @@
using BenchmarkDotNet.Attributes;
namespace ImageProcessorCore.Benchmarks
{
using System.Drawing;
using CoreColor = ImageProcessorCore.Color;
public class Colors
{
[Benchmark(Baseline = true, Description = "System.Drawing Color")]
public bool SystemDrawingColorEqual()
{
return Color.FromArgb(128, 128, 128, 128).Equals(Color.FromArgb(128, 128, 128, 128));
}
[Benchmark(Description = "ImageProcessorCore Color")]
public bool ColorEqual()
{
return new CoreColor(.5f, .5f, .5f, .5f).Equals(new CoreColor(.5f, .5f, .5f, .5f));
}
}
}

33
tests/ImageProcessorCore.Benchmarks/Image/GetSetPixel.cs

@ -0,0 +1,33 @@
namespace ImageProcessorCore.Benchmarks.Image
{
using System.Drawing;
using BenchmarkDotNet.Attributes;
using CoreColor = ImageProcessorCore.Color;
using CoreImage = ImageProcessorCore.Image;
using SystemColor = System.Drawing.Color;
public class GetSetPixel
{
[Benchmark(Baseline = true, Description = "System.Drawing GetSet Pixel")]
public SystemColor ResizeSystemDrawing()
{
using (Bitmap source = new Bitmap(400, 400))
{
source.SetPixel(200, 200, SystemColor.White);
return source.GetPixel(200, 200);
}
}
[Benchmark(Description = "ImageProcessorCore GetSet Pixel")]
public CoreColor ResizeCore()
{
using (CoreImage image = new CoreImage(400, 400))
{
image[200, 200] = CoreColor.White;
return image[200, 200];
}
}
}
}

44
tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs

@ -0,0 +1,44 @@
namespace ImageProcessorCore.Benchmarks
{
using System.Drawing;
using System.Drawing.Drawing2D;
using BenchmarkDotNet.Attributes;
using ImageProcessorCore.Samplers;
using CoreImage = ImageProcessorCore.Image;
using CoreSize = ImageProcessorCore.Size;
public class Crop
{
[Benchmark(Baseline = true, Description = "System.Drawing Crop")]
public Size CropSystemDrawing()
{
using (Bitmap source = new Bitmap(400, 400))
{
using (Bitmap destination = new Bitmap(100, 100))
{
using (Graphics graphics = Graphics.FromImage(destination))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.DrawImage(source, new Rectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel);
}
return destination.Size;
}
}
}
[Benchmark(Description = "ImageProcessorCore Crop")]
public CoreSize CropResizeCore()
{
using (CoreImage image = new CoreImage(400, 400))
{
image.Crop(100, 100);
return new CoreSize(image.Width, image.Height);
}
}
}
}

0
tests/ImageProcessorCore.Benchmarks/Resize.cs → tests/ImageProcessorCore.Benchmarks/Samplers/Resize.cs

Loading…
Cancel
Save