Browse Source

Faster... Much Faster...

Former-commit-id: 72030d1552aad7da681b03900ffe614bb8fee3d2
Former-commit-id: 13219f66ec6a6f6e3d61eddd8102810ab4b948f7
Former-commit-id: 356cddebc1dd634474e0b5ddc450318d5b21bff3
pull/1/head
James Jackson-South 10 years ago
parent
commit
dc80f36e76
  1. 58
      GenericImage/Common/Helpers/Class.cs
  2. 100
      GenericImage/Common/Helpers/ExpressionUtil.cs
  3. 40
      GenericImage/Common/Helpers/ImageMaths.cs
  4. 479
      GenericImage/Common/Helpers/Operator.cs
  5. 21
      GenericImage/GenericImage.xproj
  6. 17
      GenericImage/IImageBase.cs
  7. 76
      GenericImage/IImageProcessor.cs
  8. 23
      GenericImage/IPixelAccessor.cs
  9. 190
      GenericImage/ImageBase.cs
  10. 165
      GenericImage/ImageProcessor.cs
  11. 13
      GenericImage/ImageRgba32.cs
  12. 25
      GenericImage/ImageRgba64.cs
  13. 85
      GenericImage/PackedVectors/Bgra.cs
  14. 42
      GenericImage/PackedVectors/IColor.cs
  15. 53
      GenericImage/PackedVectors/IPackedVector.cs
  16. 163
      GenericImage/PackedVectors/Rgba32.cs
  17. 163
      GenericImage/PackedVectors/Rgba64.cs
  18. 148
      GenericImage/PixelAccessorRgba32.cs
  19. 148
      GenericImage/PixelAccessorRgba64.cs
  20. 23
      GenericImage/ProgressEventArgs.cs
  21. 19
      GenericImage/Properties/AssemblyInfo.cs
  22. 393
      GenericImage/ResizeProcessor.cs
  23. 28
      GenericImage/project.json
  24. 7
      ImageProcessorCore.sln
  25. 2
      src/ImageProcessorCore/Bootstrapper.cs
  26. 58
      src/ImageProcessorCore/Common/Helpers/Class.cs
  27. 100
      src/ImageProcessorCore/Common/Helpers/ExpressionUtil.cs
  28. 479
      src/ImageProcessorCore/Common/Helpers/Operator.cs
  29. 2
      src/ImageProcessorCore/Formats/Bmp/BmpDecoder.cs
  30. 10
      src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs
  31. 2
      src/ImageProcessorCore/Formats/Bmp/BmpEncoder.cs
  32. 8
      src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs
  33. 2
      src/ImageProcessorCore/Formats/IImageDecoder.cs
  34. 2
      src/ImageProcessorCore/Formats/IImageEncoder.cs
  35. 2
      src/ImageProcessorCore/Image/IImageBase.cs
  36. 2
      src/ImageProcessorCore/Image/IImageFrame.cs
  37. 4
      src/ImageProcessorCore/Image/IImageProcessor.cs
  38. 2
      src/ImageProcessorCore/Image/Image.cs
  39. 2
      src/ImageProcessorCore/Image/ImageBase.cs
  40. 10
      src/ImageProcessorCore/Image/ImageExtensions.cs
  41. 2
      src/ImageProcessorCore/Image/ImageFrame.cs
  42. 10
      src/ImageProcessorCore/ImageProcessor.cs
  43. 285
      src/ImageProcessorCore/PackedVector/Bgra32.cs
  44. 18
      src/ImageProcessorCore/PackedVector/IPackedVector.cs
  45. 2
      src/ImageProcessorCore/PixelAccessor/IPixelAccessor.cs
  46. 12
      src/ImageProcessorCore/Samplers/Options/ResizeHelper.cs
  47. 41
      src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs
  48. 10
      src/ImageProcessorCore/Samplers/Resize.cs
  49. 1
      src/ImageProcessorCore/project.json
  50. 2
      tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

58
GenericImage/Common/Helpers/Class.cs

@ -1,58 +0,0 @@
namespace GenericImage.Helpers
{
interface INullOp<T>
{
bool HasValue(T value);
bool AddIfNotNull(ref T accumulator, T value);
}
sealed class StructNullOp<T>
: INullOp<T>, INullOp<T?>
where T : struct
{
public bool HasValue(T value)
{
return true;
}
public bool AddIfNotNull(ref T accumulator, T value)
{
accumulator = Operator<T>.Add(accumulator, value);
return true;
}
public bool HasValue(T? value)
{
return value.HasValue;
}
public bool AddIfNotNull(ref T? accumulator, T? value)
{
if (value.HasValue)
{
accumulator = accumulator.HasValue ?
Operator<T>.Add(
accumulator.GetValueOrDefault(),
value.GetValueOrDefault())
: value;
return true;
}
return false;
}
}
sealed class ClassNullOp<T>
: INullOp<T>
where T : class
{
public bool HasValue(T value)
{
return value != null;
}
public bool AddIfNotNull(ref T accumulator, T value)
{
if (value != null)
{
accumulator = accumulator == null ?
value : Operator<T>.Add(accumulator, value);
return true;
}
return false;
}
}
}

100
GenericImage/Common/Helpers/ExpressionUtil.cs

@ -1,100 +0,0 @@
namespace GenericImage.Helpers
{
using System;
using System.Linq.Expressions;
/// <summary>
/// General purpose Expression utilities
/// </summary>
public static class ExpressionUtil
{
/// <summary>
/// Create a function delegate representing a unary operation
/// </summary>
/// <typeparam name="TArg1">The parameter type</typeparam>
/// <typeparam name="TResult">The return type</typeparam>
/// <param name="body">Body factory</param>
/// <returns>Compiled function delegate</returns>
public static Func<TArg1, TResult> CreateExpression<TArg1, TResult>(
Func<Expression, UnaryExpression> body)
{
ParameterExpression inp = Expression.Parameter(typeof(TArg1), "inp");
try
{
return Expression.Lambda<Func<TArg1, TResult>>(body(inp), inp).Compile();
}
catch (Exception ex)
{
string msg = ex.Message; // avoid capture of ex itself
return delegate { throw new InvalidOperationException(msg); };
}
}
/// <summary>
/// Create a function delegate representing a binary operation
/// </summary>
/// <typeparam name="TArg1">The first parameter type</typeparam>
/// <typeparam name="TArg2">The second parameter type</typeparam>
/// <typeparam name="TResult">The return type</typeparam>
/// <param name="body">Body factory</param>
/// <returns>Compiled function delegate</returns>
public static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>(
Func<Expression, Expression, BinaryExpression> body)
{
return CreateExpression<TArg1, TArg2, TResult>(body, false);
}
/// <summary>
/// Create a function delegate representing a binary operation
/// </summary>
/// <param name="castArgsToResultOnFailure">
/// If no matching operation is possible, attempt to convert
/// TArg1 and TArg2 to TResult for a match? For example, there is no
/// "decimal operator /(decimal, int)", but by converting TArg2 (int) to
/// TResult (decimal) a match is found.
/// </param>
/// <typeparam name="TArg1">The first parameter type</typeparam>
/// <typeparam name="TArg2">The second parameter type</typeparam>
/// <typeparam name="TResult">The return type</typeparam>
/// <param name="body">Body factory</param>
/// <returns>Compiled function delegate</returns>
public static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>(
Func<Expression, Expression, BinaryExpression> body, bool castArgsToResultOnFailure)
{
ParameterExpression lhs = Expression.Parameter(typeof(TArg1), "lhs");
ParameterExpression rhs = Expression.Parameter(typeof(TArg2), "rhs");
try
{
try
{
return Expression.Lambda<Func<TArg1, TArg2, TResult>>(body(lhs, rhs), lhs, rhs).Compile();
}
catch (InvalidOperationException)
{
// If we show retry and the args aren't already "TValue, TValue, TValue"...
// convert both lhs and rhs to TResult (as appropriate)
if (castArgsToResultOnFailure && !(typeof(TArg1) == typeof(TResult) && typeof(TArg2) == typeof(TResult)))
{
Expression castLhs = typeof(TArg1) == typeof(TResult)
? lhs
: (Expression)Expression.Convert(lhs, typeof(TResult));
Expression castRhs = typeof(TArg2) == typeof(TResult)
? rhs
: (Expression)Expression.Convert(rhs, typeof(TResult));
return Expression.Lambda<Func<TArg1, TArg2, TResult>>(
body(castLhs, castRhs), lhs, rhs).Compile();
}
throw;
}
}
catch (Exception ex)
{
string msg = ex.Message; // avoid capture of ex itself
return delegate { throw new InvalidOperationException(msg); };
}
}
}
}

40
GenericImage/Common/Helpers/ImageMaths.cs

@ -1,40 +0,0 @@
namespace GenericImage
{
public static class ImageMaths
{
/// <summary>
/// Restricts a value to be within a specified range.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">The minimum value. If <c>value</c> is less than <c>min</c>, <c>min</c> will be returned.</param>
/// <param name="max">The maximum value. If <c>value</c> is greater than <c>max</c>, <c>max</c> will be returned.</param>
/// <returns>The clamped value.</returns>
public static float Clamp(float value, float min, float max)
{
// This compare order is very important!!!
// We must follow HLSL behavior in the case user specified min value is bigger than max value.
// First we check to see if we're greater than the max
value = (value > max) ? max : value;
// Then we check to see if we're less than the min.
value = (value < min) ? min : value;
// There's no check to see if min > max.
return value;
}
/// <summary>
/// Restricts a value to be within a specified range.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">The minimum value. If <c>value</c> is less than <c>min</c>, <c>min</c> will be returned.</param>
/// <param name="max">The maximum value. If <c>value</c> is greater than <c>max</c>, <c>max</c> will be returned.</param>
/// <returns>The clamped value.</returns>
public static int Clamp(int value, int min, int max)
{
value = (value > max) ? max : value;
value = (value < min) ? min : value;
return value;
}
}
}

479
GenericImage/Common/Helpers/Operator.cs

@ -1,479 +0,0 @@
namespace GenericImage.Helpers
{
using System;
using System.Linq.Expressions;
using System.Reflection;
/// <summary>
/// The Operator class provides easy access to the standard operators
/// (addition, etc) for generic types, using type inference to simplify
/// usage.
/// </summary>
public static class Operator
{
/// <summary>
/// Indicates if the supplied value is non-null,
/// for reference-types or Nullable&lt;T&gt;
/// </summary>
/// <returns>True for non-null values, else false</returns>
public static bool HasValue<T>(T value)
{
return Operator<T>.NullOp.HasValue(value);
}
/// <summary>
/// Increments the accumulator only
/// if the value is non-null. If the accumulator
/// is null, then the accumulator is given the new
/// value; otherwise the accumulator and value
/// are added.
/// </summary>
/// <param name="accumulator">The current total to be incremented (can be null)</param>
/// <param name="value">The value to be tested and added to the accumulator</param>
/// <returns>True if the value is non-null, else false - i.e.
/// "has the accumulator been updated?"</returns>
public static bool AddIfNotNull<T>(ref T accumulator, T value)
{
return Operator<T>.NullOp.AddIfNotNull(ref accumulator, value);
}
/// <summary>
/// Evaluates unary negation (-) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Negate<T>(T value)
{
return Operator<T>.Negate(value);
}
/// <summary>
/// Evaluates bitwise not (~) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Not<T>(T value)
{
return Operator<T>.Not(value);
}
/// <summary>
/// Evaluates bitwise or (|) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Or<T>(T value1, T value2)
{
return Operator<T>.Or(value1, value2);
}
/// <summary>
/// Evaluates bitwise and (&amp;) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T And<T>(T value1, T value2)
{
return Operator<T>.And(value1, value2);
}
/// <summary>
/// Evaluates bitwise xor (^) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Xor<T>(T value1, T value2)
{
return Operator<T>.Xor(value1, value2);
}
/// <summary>
/// Performs a conversion between the given types; this will throw
/// an InvalidOperationException if the type T does not provide a suitable cast, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this cast.
/// </summary>
public static TTo Convert<TFrom, TTo>(TFrom value)
{
return Operator<TFrom, TTo>.Convert(value);
}
/// <summary>
/// Evaluates binary addition (+) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Add<T>(T value1, T value2)
{
return Operator<T>.Add(value1, value2);
}
/// <summary>
/// Evaluates binary addition (+) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 AddAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Add(value1, value2);
}
/// <summary>
/// Evaluates binary subtraction (-) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Subtract<T>(T value1, T value2)
{
return Operator<T>.Subtract(value1, value2);
}
/// <summary>
/// Evaluates binary subtraction(-) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 SubtractAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Subtract(value1, value2);
}
/// <summary>
/// Evaluates binary multiplication (*) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Multiply<T>(T value1, T value2)
{
return Operator<T>.Multiply(value1, value2);
}
/// <summary>
/// Evaluates binary multiplication (*) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 MultiplyAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Multiply(value1, value2);
}
/// <summary>
/// Evaluates binary division (/) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Divide<T>(T value1, T value2)
{
return Operator<T>.Divide(value1, value2);
}
/// <summary>
/// Evaluates binary division (/) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 DivideAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Divide(value1, value2);
}
/// <summary>
/// Evaluates binary equality (==) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool Equal<T>(T value1, T value2)
{
return Operator<T>.Equal(value1, value2);
}
/// <summary>
/// Evaluates binary inequality (!=) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool NotEqual<T>(T value1, T value2)
{
return Operator<T>.NotEqual(value1, value2);
}
/// <summary>
/// Evaluates binary greater-than (&gt;) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool GreaterThan<T>(T value1, T value2)
{
return Operator<T>.GreaterThan(value1, value2);
}
/// <summary>
/// Evaluates binary less-than (&lt;) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool LessThan<T>(T value1, T value2)
{
return Operator<T>.LessThan(value1, value2);
}
/// <summary>
/// Evaluates binary greater-than-on-eqauls (&gt;=) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool GreaterThanOrEqual<T>(T value1, T value2)
{
return Operator<T>.GreaterThanOrEqual(value1, value2);
}
/// <summary>
/// Evaluates binary less-than-or-equal (&lt;=) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool LessThanOrEqual<T>(T value1, T value2)
{
return Operator<T>.LessThanOrEqual(value1, value2);
}
/// <summary>
/// Evaluates integer division (/) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary><remarks>
/// This operation is particularly useful for computing averages and
/// similar aggregates.
/// </remarks>
public static T DivideInt32<T>(T value, int divisor)
{
return Operator<int, T>.Divide(value, divisor);
}
}
/// <summary>
/// Provides standard operators (such as addition) that operate over operands of
/// different types. For operators, the return type is assumed to match the first
/// operand.
/// </summary>
/// <seealso cref="Operator&lt;T&gt;"/>
/// <seealso cref="Operator"/>
public static class Operator<TValue, TResult>
{
private static readonly Func<TValue, TResult> convert;
/// <summary>
/// Returns a delegate to convert a value between two types; this delegate will throw
/// an InvalidOperationException if the type T does not provide a suitable cast, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this cast.
/// </summary>
public static Func<TValue, TResult> Convert => convert;
static Operator()
{
convert = ExpressionUtil.CreateExpression<TValue, TResult>(body => Expression.Convert(body, typeof(TResult)));
add = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Add, true);
subtract = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Subtract, true);
multiply = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Multiply, true);
divide = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Divide, true);
}
private static readonly Func<TResult, TValue, TResult> add, subtract, multiply, divide;
private static readonly Func<TResult, float, TResult> multiplyF, divideF;
/// <summary>
/// Returns a delegate to evaluate binary addition (+) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Add => add;
/// <summary>
/// Returns a delegate to evaluate binary subtraction (-) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Subtract => subtract;
/// <summary>
/// Returns a delegate to evaluate binary multiplication (*) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Multiply => multiply;
/// <summary>
/// Returns a delegate to evaluate binary division (/) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Divide => divide;
public static Func<TResult, float, TResult> MultiplyF => multiplyF;
public static Func<TResult, float, TResult> DivideF => divideF;
}
/// <summary>
/// Provides standard operators (such as addition) over a single type
/// </summary>
/// <seealso cref="Operator"/>
/// <seealso cref="Operator&lt;TValue,TResult&gt;"/>
public static class Operator<T>
{
static readonly INullOp<T> nullOp;
internal static INullOp<T> NullOp => nullOp;
static readonly T zero;
/// <summary>
/// Returns the zero value for value-types (even full Nullable&lt;TInner&gt;) - or null for reference types
/// </summary>
public static T Zero => zero;
static readonly Func<T, T> negate, not;
static readonly Func<T, T, T> or, and, xor;
/// <summary>
/// Returns a delegate to evaluate unary negation (-) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T> Negate => negate;
/// <summary>
/// Returns a delegate to evaluate bitwise not (~) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T> Not => not;
/// <summary>
/// Returns a delegate to evaluate bitwise or (|) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Or => or;
/// <summary>
/// Returns a delegate to evaluate bitwise and (&amp;) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> And => and;
/// <summary>
/// Returns a delegate to evaluate bitwise xor (^) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Xor => xor;
static readonly Func<T, T, T> add, subtract, multiply, divide;
static readonly Func<T, float, T> multiplyF, divideF;
/// <summary>
/// Returns a delegate to evaluate binary addition (+) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Add => add;
/// <summary>
/// Returns a delegate to evaluate binary subtraction (-) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Subtract => subtract;
/// <summary>
/// Returns a delegate to evaluate binary multiplication (*) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Multiply => multiply;
/// <summary>
/// Returns a delegate to evaluate binary division (/) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Divide => divide;
public static Func<T, float, T> MultiplyF => multiplyF;
public static Func<T, float, T> DivideF => divideF;
static readonly Func<T, T, bool> equal, notEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual;
/// <summary>
/// Returns a delegate to evaluate binary equality (==) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> Equal => equal;
/// <summary>
/// Returns a delegate to evaluate binary inequality (!=) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> NotEqual => notEqual;
/// <summary>
/// Returns a delegate to evaluate binary greater-then (&gt;) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> GreaterThan => greaterThan;
/// <summary>
/// Returns a delegate to evaluate binary less-than (&lt;) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> LessThan => lessThan;
/// <summary>
/// Returns a delegate to evaluate binary greater-than-or-equal (&gt;=) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> GreaterThanOrEqual => greaterThanOrEqual;
/// <summary>
/// Returns a delegate to evaluate binary less-than-or-equal (&lt;=) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> LessThanOrEqual => lessThanOrEqual;
static Operator()
{
add = ExpressionUtil.CreateExpression<T, T, T>(Expression.Add);
subtract = ExpressionUtil.CreateExpression<T, T, T>(Expression.Subtract);
divide = ExpressionUtil.CreateExpression<T, T, T>(Expression.Divide);
multiply = ExpressionUtil.CreateExpression<T, T, T>(Expression.Multiply);
multiplyF = ExpressionUtil.CreateExpression<T, float, T>(Expression.Multiply);
divideF = ExpressionUtil.CreateExpression<T, float, T>(Expression.Multiply);
greaterThan = ExpressionUtil.CreateExpression<T, T, bool>(Expression.GreaterThan);
greaterThanOrEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.GreaterThanOrEqual);
lessThan = ExpressionUtil.CreateExpression<T, T, bool>(Expression.LessThan);
lessThanOrEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.LessThanOrEqual);
equal = ExpressionUtil.CreateExpression<T, T, bool>(Expression.Equal);
notEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.NotEqual);
negate = ExpressionUtil.CreateExpression<T, T>(Expression.Negate);
and = ExpressionUtil.CreateExpression<T, T, T>(Expression.And);
or = ExpressionUtil.CreateExpression<T, T, T>(Expression.Or);
not = ExpressionUtil.CreateExpression<T, T>(Expression.Not);
xor = ExpressionUtil.CreateExpression<T, T, T>(Expression.ExclusiveOr);
Type typeT = typeof(T);
if (typeT.GetTypeInfo().IsValueType && typeT.GetTypeInfo().IsGenericType && (typeT.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
// get the *inner* zero (not a null Nullable<TValue>, but default(TValue))
Type nullType = typeT.GetTypeInfo().GenericTypeArguments[0];
zero = (T)Activator.CreateInstance(nullType);
nullOp = (INullOp<T>)Activator.CreateInstance(
typeof(StructNullOp<>).MakeGenericType(nullType));
}
else
{
zero = default(T);
if (typeT.GetTypeInfo().IsValueType)
{
nullOp = (INullOp<T>)Activator.CreateInstance(
typeof(StructNullOp<>).MakeGenericType(typeT));
}
else
{
nullOp = (INullOp<T>)Activator.CreateInstance(
typeof(ClassNullOp<>).MakeGenericType(typeT));
}
}
}
}
}

21
GenericImage/GenericImage.xproj

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>45d91211-9d4a-4382-a114-8786859e302a</ProjectGuid>
<RootNamespace>GenericImage</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

17
GenericImage/IImageBase.cs

@ -1,17 +0,0 @@
namespace GenericImage
{
using GenericImage.PackedVectors;
public interface IImageBase<TColor, TDepth>
where TColor : IColor<TDepth>
where TDepth : struct
{
TColor[] Pixels { get; }
int Width { get; }
int Height { get; }
IPixelAccessor<TColor> Lock();
}
}

76
GenericImage/IImageProcessor.cs

@ -1,76 +0,0 @@
// <copyright file="IImageProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace GenericImage
{
using PackedVectors;
/// <summary>
/// A delegate which is called as progress is made processing an image.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">An object that contains the event data.</param>
public delegate void ProgressEventHandler(object sender, ProgressEventArgs e);
/// <summary>
/// Encapsulates methods to alter the pixels of an image.
/// </summary>
public interface IImageProcessor
{
/// <summary>
/// Event fires when each row of the source image has been processed.
/// </summary>
/// <remarks>
/// This event may be called from threads other than the client thread, and from multiple threads simultaneously.
/// Individual row notifications may arrived out of order.
/// </remarks>
event ProgressEventHandler OnProgress;
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor,TDepth}"/>.
/// </summary>
/// <typeparam name="T">The type of pixels contained within the image.</typeparam>
/// <param name="target">Target image to apply the process to.</param>
/// <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>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image processing filter as new image.
/// </remarks>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="target"/> is null or <paramref name="source"/> is null.
/// </exception>
/// <exception cref="System.ArgumentException">
/// <paramref name="sourceRectangle"/> doesnt fit the dimension of the image.
/// </exception>
void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle sourceRectangle)
where TColor : IColor<TDepth>, new() where TDepth : struct;
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor, TDepth}"/> at the specified
/// location and with the specified size.
/// </summary>
/// <typeparam name="T">The type of pixels contained within the image.</typeparam>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="width">The target width.</param>
/// <param name="height">The target height.</param>
/// <param name="targetRectangle">
/// 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="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image process as new image.
/// </remarks>
void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, int width, int height, Rectangle targetRectangle, Rectangle sourceRectangle)
where TColor : IColor<TDepth>, new() where TDepth : struct;
}
}

23
GenericImage/IPixelAccessor.cs

@ -1,23 +0,0 @@
namespace GenericImage
{
using System;
public interface IPixelAccessor<TColor> : IDisposable
{
TColor this[int x, int y]
{
get;
set;
}
/// <summary>
/// Gets the width.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height.
/// </summary>
int Height { get; }
}
}

190
GenericImage/ImageBase.cs

@ -1,190 +0,0 @@
namespace GenericImage
{
using System;
using GenericImage.PackedVectors;
/// <summary>
/// Encapsulates the basic properties and methods required to manipulate images
/// in different pixel formats.
/// </summary>
/// <typeparam name="TColor">The packed vector pixels format.</typeparam>
/// <typeparam name="TDepth">The bit depth of the image. byte, float, etc.</typeparam>
public abstract class ImageBase<TColor, TDepth> : IImageBase<TColor, TDepth>
where TColor : IColor<TDepth>
where TDepth : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TColor,TDepth}"/> class.
/// </summary>
protected ImageBase()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TColor,TDepth}"/> class.
/// </summary>
/// <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(int width, int height)
{
// Guard.MustBeGreaterThan(width, 0, nameof(width));
// Guard.MustBeGreaterThan(height, 0, nameof(height));
this.Width = width;
this.Height = height;
this.Pixels = new TColor[width * height];
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TColor,TDepth}"/> class.
/// </summary>
/// <param name="other">
/// The other <see cref="ImageBase{TColor,TDepth}"/> to create this instance from.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown if the given <see cref="ImageBase{TColor,TDepth}"/> is null.
/// </exception>
protected ImageBase(ImageBase<TColor, TDepth> other)
{
// Guard.NotNull(other, nameof(other), "Other image cannot be null.");
this.Width = other.Width;
this.Height = other.Height;
this.Quality = other.Quality;
this.FrameDelay = other.FrameDelay;
// Copy the pixels.
this.Pixels = new TColor[this.Width * this.Height];
Array.Copy(other.Pixels, this.Pixels, other.Pixels.Length);
}
/// <summary>
/// Gets the pixels as an array of the given packed pixel format.
/// </summary>
public TColor[] Pixels { get; private set; }
/// <summary>
/// Gets the width in pixels.
/// </summary>
public int Width { get; private set; }
/// <summary>
/// Gets the height in pixels.
/// </summary>
public int Height { get; private set; }
/// <summary>
/// Gets the pixel ratio made up of the width and height.
/// </summary>
public double PixelRatio => (double)this.Width / this.Height;
/// <summary>
/// Gets the <see cref="Rectangle"/> representing the bounds of the image.
/// </summary>
// public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height);
/// <summary>
/// Gets or sets th quality of the image. This affects the output quality of lossy image formats.
/// </summary>
public int Quality { get; set; }
/// <summary>
/// Gets or sets the frame delay for animated images.
/// If not 0, this field specifies the number of hundredths (1/100) of a second to
/// wait before continuing with the processing of the Data Stream.
/// The clock starts ticking immediately after the graphic is rendered.
/// </summary>
public int FrameDelay { get; set; }
/// <summary>
/// Sets the pixel array of the image to the given value.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">
/// The array with colors. Must be a multiple of the width and height.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height.
/// </exception>
public void SetPixels(int width, int height, TColor[] pixels)
{
if (width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than or equals than zero.");
}
if (height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(height), "Height must be greater than or equal than zero.");
}
if (pixels.Length != width * height)
{
throw new ArgumentException("Pixel array must have the length of Width * Height.");
}
this.Width = width;
this.Height = height;
this.Pixels = pixels;
}
/// <summary>
/// Sets the pixel array of the image to the given value, creating a copy of
/// the original pixels.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">
/// The array with colors. Must be a multiple of four times the width and height.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height.
/// </exception>
public void ClonePixels(int width, int height, TColor[] pixels)
{
if (width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than or equals than zero.");
}
if (height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(height), "Height must be greater than or equal than zero.");
}
if (pixels.Length != width * height)
{
throw new ArgumentException("Pixel array must have the length of Width * Height.");
}
this.Width = width;
this.Height = height;
// Copy the pixels.
this.Pixels = new TColor[pixels.Length];
Array.Copy(pixels, this.Pixels, pixels.Length);
}
/// <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="IPixelAccessor{TColor}"/></returns>
public abstract IPixelAccessor<TColor> Lock();
}
}

165
GenericImage/ImageProcessor.cs

@ -1,165 +0,0 @@
// <copyright file="ImageProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace GenericImage
{
using System;
using System.Threading;
using GenericImage.PackedVectors;
/// <summary>
/// Allows the application of processors to images.
/// </summary>
public abstract class ImageProcessor : IImageProcessor
{
/// <inheritdoc/>
public event ProgressEventHandler OnProgress;
/// <summary>
/// The number of rows processed by a derived class.
/// </summary>
private int numRowsProcessed;
/// <summary>
/// The total number of rows that will be processed by a derived class.
/// </summary>
private int totalRows;
/// <inheritdoc/>
public void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle sourceRectangle)
where TColor : IColor<TDepth>, new() where TDepth : struct
{
try
{
this.OnApply(target, source, target.Bounds, sourceRectangle);
this.numRowsProcessed = 0;
this.totalRows = sourceRectangle.Height;
this.Apply(target, source, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom);
this.AfterApply(target, source, target.Bounds, sourceRectangle);
}
catch (Exception ex)
{
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
}
}
/// <inheritdoc/>
public void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, int width, int height, Rectangle targetRectangle, Rectangle sourceRectangle) where TColor : IColor<TDepth>, new() where TDepth : struct
{
try
{
TColor[] pixels = new TColor[width * height];
target.SetPixels(width, height, pixels);
// Ensure we always have bounds.
if (sourceRectangle == Rectangle.Empty)
{
sourceRectangle = source.Bounds;
}
if (targetRectangle == Rectangle.Empty)
{
targetRectangle = target.Bounds;
}
this.OnApply(target, source, targetRectangle, sourceRectangle);
this.numRowsProcessed = 0;
this.totalRows = targetRectangle.Height;
this.Apply(target, source, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom);
this.AfterApply(target, source, target.Bounds, sourceRectangle);
}
catch (Exception ex)
{
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
}
}
/// <summary>
/// This method is called before the process is applied to prepare the processor.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="targetRectangle">
/// 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="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void OnApply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle)
where TColor : IColor<TDepth>, new() where TDepth : struct
{
}
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{T}"/> at the specified location
/// and with the specified size.
/// </summary>
/// <typeparam name="T">The type of pixels contained within the image.</typeparam>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="targetRectangle">
/// 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="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <param name="startY">The index of the row within the source image to start processing.</param>
/// <param name="endY">The index of the row within the source image to end processing.</param>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image process as new image.
/// </remarks>
protected abstract void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
where TColor : IColor<TDepth>, new() where TDepth : struct;
/// <summary>
/// This method is called after the process is applied to prepare the processor.
/// </summary>
/// <typeparam name="T">The type of pixels contained within the image.</typeparam>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="targetRectangle">
/// 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="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void AfterApply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle)
where TColor : IColor<TDepth>, new() where TDepth : struct
{
}
/// <summary>
/// Must be called by derived classes after processing a single row.
/// </summary>
protected void OnRowProcessed()
{
if (this.OnProgress != null)
{
int currThreadNumRows = Interlocked.Add(ref this.numRowsProcessed, 1);
// Multi-pass filters process multiple times more rows than totalRows, so update totalRows on the fly
if (currThreadNumRows > this.totalRows)
{
this.totalRows = currThreadNumRows;
}
// Report progress. This may be on the client's thread, or on a Task library thread.
this.OnProgress(this, new ProgressEventArgs { RowsProcessed = currThreadNumRows, TotalRows = this.totalRows });
}
}
}
}

13
GenericImage/ImageRgba32.cs

@ -1,13 +0,0 @@
namespace GenericImage
{
using GenericImage.PackedVectors;
public class ImageRgba32 : ImageBase<Rgba32>
{
/// <inheritdocs/>
public override IPixelAccessor Lock()
{
return new PixelAccessorRgba32(this);
}
}
}

25
GenericImage/ImageRgba64.cs

@ -1,25 +0,0 @@
namespace GenericImage
{
using GenericImage.PackedVectors;
public class ImageRgba64 : IImageBase<Rgba64>
{
public ImageRgba64(int width, int height)
{
this.Width = width;
this.Height = height;
this.Pixels = new Rgba64[width * height];
}
public Rgba64[] Pixels { get; }
public int Width { get; }
public int Height { get; }
public IPixelAccessor Lock()
{
return new PixelAccessorRgba64(this);
}
}
}

85
GenericImage/PackedVectors/Bgra.cs

@ -1,85 +0,0 @@
using System;
using System.Numerics;
using GenericImage.Helpers;
using GenericImage.PackedVectors;
namespace GenericImage.PackedVectors
{
public struct Bgra<TDepth> : IColor<TDepth>
where TDepth : struct
{
private static readonly TDepth[] Components = new TDepth[4];
public TDepth[] Values => Components;
public void Add<TColor>(TColor value) where TColor : IColor<TDepth>
{
for (int i = 0; i < value.Values.Length; i++)
{
this.Values[i] = Operator<TDepth>.Add(this.Values[i], value.Values[i]);
}
}
public void Multiply<TColor>(TColor value) where TColor : IColor<TDepth>
{
for (int i = 0; i < value.Values.Length; i++)
{
this.Values[i] = Operator<TDepth>.Multiply(this.Values[i], value.Values[i]);
}
}
public void Multiply<TColor>(float value) where TColor : IColor<TDepth>
{
for (int i = 0; i < this.Values.Length; i++)
{
this.Values[i] = Operator<TDepth>.MultiplyF(this.Values[i], value);
}
}
public void Divide<TColor>(TColor value) where TColor : IColor<TDepth>
{
for (int i = 0; i < value.Values.Length; i++)
{
this.Values[i] = Operator<TDepth>.Divide(this.Values[i], value.Values[i]);
}
}
public void Divide<TColor>(float value) where TColor : IColor<TDepth>
{
for (int i = 0; i < this.Values.Length; i++)
{
this.Values[i] = Operator<TDepth>.DivideF(this.Values[i], value);
}
}
public byte[] ToBytes()
{
if (typeof(TDepth) == typeof(byte))
{
return new[]
{
(byte)(object)this.Values[0],
(byte)(object)this.Values[1],
(byte)(object)this.Values[2],
(byte)(object)this.Values[3]
};
}
return null;
}
public void FromBytes(byte[] bytes)
{
if (bytes.Length != 4)
{
throw new ArgumentOutOfRangeException(nameof(bytes));
}
for (int i = 0; i < bytes.Length; i++)
{
this.Values[i] = (TDepth)(object)bytes[i];
}
}
}
}

42
GenericImage/PackedVectors/IColor.cs

@ -1,42 +0,0 @@
namespace GenericImage.PackedVectors
{
using System.Numerics;
public interface IColor4<T> : IColor<T>
where T : struct
{
T X { get; set; }
T Y { get; set; }
T Z { get; set; }
T W { get; set; }
}
public interface IColor<TDepth> : IColor
where TDepth : struct
{
TDepth[] Values { get; }
void Add<TColor>(TColor value) where TColor : IColor<TDepth>;
void Multiply<TColor>(TColor value) where TColor : IColor<TDepth>;
void Multiply<TColor>(float value) where TColor : IColor<TDepth>;
void Divide<TColor>(TColor value) where TColor : IColor<TDepth>;
void Divide<TColor>(float value) where TColor : IColor<TDepth>;
void FromBytes(byte[] bytes);
byte[] ToBytes();
}
public interface IColor
{
}
}

53
GenericImage/PackedVectors/IPackedVector.cs

@ -1,53 +0,0 @@
namespace GenericImage.PackedVectors
{
using System.Numerics;
/// <summary>
/// An interface that converts packed vector types to and from <see cref="Vector4"/> values,
/// allowing multiple encodings to be manipulated in a generic way.
/// </summary>
/// <typeparam name="TPacked">
/// The type of object representing the packed value.
/// </typeparam>
public interface IPackedVector<TPacked> : IPackedVector
where TPacked : struct
{
/// <summary>
/// Gets or sets the packed representation of the value.
/// </summary>
TPacked PackedValue { get; set; }
}
/// <summary>
/// An interface that converts packed vector types to and from <see cref="Vector4"/> values.
/// </summary>
public interface IPackedVector
{
/// <summary>
/// Sets the packed representation from a <see cref="Vector4"/>.
/// </summary>
/// <param name="vector">The vector to pack.</param>
void PackVector(Vector4 vector);
/// <summary>
/// Sets the packed representation from a <see cref="Vector4"/>.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
void PackBytes(byte x, byte y, byte z, byte w);
/// <summary>
/// Expands the packed representation into a <see cref="Vector4"/>.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToVector4();
/// <summary>
/// Expands the packed representation into a <see cref="T:byte[]"/>.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
byte[] ToBytes();
}
}

163
GenericImage/PackedVectors/Rgba32.cs

@ -1,163 +0,0 @@
namespace GenericImage.PackedVectors
{
using System;
using System.Numerics;
/// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 1.
/// </summary>
public struct Rgba32 : IPackedVector<uint>, IEquatable<Rgba32>
{
/// <summary>
/// Initializes a new instance of the <see cref="Rgba32"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
public Rgba32(float r, float g, float b, float a)
{
Vector4 clamped = Vector4.Clamp(new Vector4(r, g, b, a), Vector4.Zero, Vector4.One) * 255f;
this.PackedValue = Pack(ref clamped);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgba32"/> struct.
/// </summary>
/// <param name="vector">
/// The vector containing the components for the packed vector.
/// </param>
public Rgba32(Vector4 vector)
{
Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255f;
this.PackedValue = Pack(ref clamped);
}
/// <inheritdoc/>
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Rgba32"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba32"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba32"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Rgba32 left, Rgba32 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Rgba32"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Rgba32"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rgba32"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Rgba32 left, Rgba32 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc/>
public void PackVector(Vector4 vector)
{
Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255f;
this.PackedValue = Pack(ref clamped);
}
/// <inheritdoc/>
public void PackBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w);
this.PackedValue = Pack(ref vector);
}
/// <inheritdoc/>
public Vector4 ToVector4()
{
return new Vector4(
(this.PackedValue >> 16) & 0xFF,
(this.PackedValue >> 8) & 0xFF,
this.PackedValue & 0xFF,
(this.PackedValue >> 24) & 0xFF) / 255f;
}
/// <inheritdoc/>
public byte[] ToBytes()
{
return new[]
{
(byte)((this.PackedValue >> 16) & 0xFF),
(byte)((this.PackedValue >> 8) & 0xFF),
(byte)(this.PackedValue & 0xFF),
(byte)((this.PackedValue >> 24) & 0xFF)
};
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return (obj is Rgba32) && this.Equals((Rgba32)obj);
}
/// <inheritdoc/>
public bool Equals(Rgba32 other)
{
return this.PackedValue == other.PackedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
/// <summary>
/// Sets the packed representation from the given component values.
/// </summary>
/// <param name="vector">
/// The vector containing the components for the packed vector.
/// </param>
/// <returns>
/// The <see cref="uint"/>.
/// </returns>
private static uint Pack(ref Vector4 vector)
{
return ((uint)Math.Round(vector.X) << 16) |
((uint)Math.Round(vector.Y) << 8) |
(uint)Math.Round(vector.Z) |
((uint)Math.Round(vector.W) << 24);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="packed">
/// The instance of <see cref="Rgba32"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(Rgba32 packed)
{
return packed.PackedValue.GetHashCode();
}
}
}

163
GenericImage/PackedVectors/Rgba64.cs

@ -1,163 +0,0 @@
namespace GenericImage.PackedVectors
{
using System;
using System.Numerics;
/// <summary>
/// Packed vector type containing four 16-bit unsigned normalized values ranging from 0 to 1.
/// </summary>
public struct Rgba64 : IPackedVector<ulong>, IEquatable<Rgba64>
{
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
public Rgba64(float r, float g, float b, float a)
{
Vector4 clamped = Vector4.Clamp(new Vector4(r, g, b, a), Vector4.Zero, Vector4.One) * 65535f;
this.PackedValue = Pack(ref clamped);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="vector">
/// The vector containing the components for the packed vector.
/// </param>
public Rgba64(Vector4 vector)
{
Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 65535f;
this.PackedValue = Pack(ref clamped);
}
/// <inheritdoc/>
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Rgba64"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba64"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba64"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Rgba64 left, Rgba64 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Rgba64"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Rgba64"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rgba64"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Rgba64 left, Rgba64 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc/>
public void PackVector(Vector4 vector)
{
Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 65535f;
this.PackedValue = Pack(ref clamped);
}
/// <inheritdoc/>
public void PackBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = (new Vector4(x, y, z, w) / 255f) * 65535f;
this.PackedValue = Pack(ref vector);
}
/// <inheritdoc/>
public Vector4 ToVector4()
{
return new Vector4(
(this.PackedValue >> 32) & 0xFFFF,
(this.PackedValue >> 16) & 0xFFFF,
this.PackedValue & 0xFFFF,
(this.PackedValue >> 48) & 0xFFFF) / 65535f;
}
/// <inheritdoc/>
public byte[] ToBytes()
{
return new[]
{
(byte)((this.PackedValue >> 40) & 255),
(byte)((this.PackedValue >> 24) & 255),
(byte)((this.PackedValue >> 8) & 255),
(byte)((this.PackedValue >> 56) & 255)
};
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return (obj is Rgba64) && this.Equals((Rgba64)obj);
}
/// <inheritdoc/>
public bool Equals(Rgba64 other)
{
return this.PackedValue == other.PackedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
}
/// <summary>
/// Sets the packed representation from the given component values.
/// </summary>
/// <param name="vector">
/// The vector containing the components for the packed vector.
/// </param>
/// <returns>
/// The <see cref="ulong"/>.
/// </returns>
private static ulong Pack(ref Vector4 vector)
{
return ((ulong)Math.Round(vector.X) << 32) |
((ulong)Math.Round(vector.Y) << 16) |
(ulong)Math.Round(vector.Z) |
((ulong)Math.Round(vector.W) << 48);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="packed">
/// The instance of <see cref="Rgba64"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(Rgba64 packed)
{
return packed.PackedValue.GetHashCode();
}
}
}

148
GenericImage/PixelAccessorRgba32.cs

@ -1,148 +0,0 @@
namespace GenericImage
{
using System;
using System.Runtime.InteropServices;
using GenericImage.PackedVectors;
/// <summary>
/// Provides per-pixel access to an images pixels.
/// </summary>
public sealed unsafe class PixelAccessorRgba32 : IPixelAccessor
{
/// <summary>
/// The position of the first pixel in the bitmap.
/// </summary>
private Rgba32* pixelsBase;
/// <summary>
/// Provides a way to access the pixels from unmanaged memory.
/// </summary>
private GCHandle pixelsHandle;
/// <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="PixelAccessorRgba32"/> class.
/// </summary>
/// <param name="image">
/// The image to provide pixel access for.
/// </param>
public PixelAccessorRgba32(ImageBase<Rgba32> image)
{
//Guard.NotNull(image, nameof(image));
//Guard.MustBeGreaterThan(image.Width, 0, "image width");
//Guard.MustBeGreaterThan(image.Height, 0, "image height");
this.Width = image.Width;
this.Height = image.Height;
this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned);
this.pixelsBase = (Rgba32*)this.pixelsHandle.AddrOfPinnedObject().ToPointer();
}
/// <summary>
/// Finalizes an instance of the <see cref="PixelAccessorRgba32"/> class.
/// </summary>
~PixelAccessorRgba32()
{
this.Dispose();
}
/// <summary>
/// Gets the width of the image.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the height of the image.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets or sets the color of a pixel at the specified position.
/// </summary>
/// <param name="x">
/// The x-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <param name="y">
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <returns>The <see cref="IPackedVector"/> at the specified position.</returns>
public IPackedVector this[int x, int y]
{
get
{
#if DEBUG
if ((x < 0) || (x >= this.Width))
{
throw new ArgumentOutOfRangeException(nameof(x), "Value cannot be less than zero or greater than the bitmap width.");
}
if ((y < 0) || (y >= this.Height))
{
throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height.");
}
#endif
return *(this.pixelsBase + ((y * this.Width) + x));
}
set
{
#if DEBUG
if ((x < 0) || (x >= this.Width))
{
throw new ArgumentOutOfRangeException(nameof(x), "Value cannot be less than zero or greater than the bitmap width.");
}
if ((y < 0) || (y >= this.Height))
{
throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height.");
}
#endif
*(this.pixelsBase + ((y * this.Width) + x)) = (Rgba32)value;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (this.isDisposed)
{
return;
}
if (this.pixelsHandle.IsAllocated)
{
this.pixelsHandle.Free();
}
this.pixelsBase = null;
// Note disposing is done.
this.isDisposed = 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);
}
}
}

148
GenericImage/PixelAccessorRgba64.cs

@ -1,148 +0,0 @@
namespace GenericImage
{
using System;
using System.Runtime.InteropServices;
using GenericImage.PackedVectors;
/// <summary>
/// Provides per-pixel access to an images pixels.
/// </summary>
public sealed unsafe class PixelAccessorRgba64 : IPixelAccessor
{
/// <summary>
/// The position of the first pixel in the bitmap.
/// </summary>
private Rgba64* pixelsBase;
/// <summary>
/// Provides a way to access the pixels from unmanaged memory.
/// </summary>
private GCHandle pixelsHandle;
/// <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="PixelAccessorRgba64"/> class.
/// </summary>
/// <param name="image">
/// The image to provide pixel access for.
/// </param>
public PixelAccessorRgba64(IImageBase<Rgba64> image)
{
//Guard.NotNull(image, nameof(image));
//Guard.MustBeGreaterThan(image.Width, 0, "image width");
//Guard.MustBeGreaterThan(image.Height, 0, "image height");
this.Width = image.Width;
this.Height = image.Height;
this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned);
this.pixelsBase = (Rgba64*)this.pixelsHandle.AddrOfPinnedObject().ToPointer();
}
/// <summary>
/// Finalizes an instance of the <see cref="PixelAccessorRgba64"/> class.
/// </summary>
~PixelAccessorRgba64()
{
this.Dispose();
}
/// <summary>
/// Gets the width of the image.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the height of the image.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets or sets the color of a pixel at the specified position.
/// </summary>
/// <param name="x">
/// The x-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <param name="y">
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <returns>The <see cref="IPackedVector"/> at the specified position.</returns>
public IPackedVector this[int x, int y]
{
get
{
#if DEBUG
if ((x < 0) || (x >= this.Width))
{
throw new ArgumentOutOfRangeException(nameof(x), "Value cannot be less than zero or greater than the bitmap width.");
}
if ((y < 0) || (y >= this.Height))
{
throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height.");
}
#endif
return *(this.pixelsBase + ((y * this.Width) + x));
}
set
{
#if DEBUG
if ((x < 0) || (x >= this.Width))
{
throw new ArgumentOutOfRangeException(nameof(x), "Value cannot be less than zero or greater than the bitmap width.");
}
if ((y < 0) || (y >= this.Height))
{
throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height.");
}
#endif
*(this.pixelsBase + ((y * this.Width) + x)) = (Rgba64)value;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (this.isDisposed)
{
return;
}
if (this.pixelsHandle.IsAllocated)
{
this.pixelsHandle.Free();
}
this.pixelsBase = null;
// Note disposing is done.
this.isDisposed = 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);
}
}
}

23
GenericImage/ProgressEventArgs.cs

@ -1,23 +0,0 @@
// <copyright file="ProgressEventArgs.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace GenericImage
{
/// <summary>
/// Contains event data related to the progress made processing an image.
/// </summary>
public class ProgressEventArgs : System.EventArgs
{
/// <summary>
/// Gets or sets the number of rows processed.
/// </summary>
public int RowsProcessed { get; set; }
/// <summary>
/// Gets or sets the total number of rows.
/// </summary>
public int TotalRows { get; set; }
}
}

19
GenericImage/Properties/AssemblyInfo.cs

@ -1,19 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GenericImage")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("45d91211-9d4a-4382-a114-8786859e302a")]

393
GenericImage/ResizeProcessor.cs

@ -1,393 +0,0 @@
// <copyright file="ResizeProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
using GenericImage;
/// <summary>
/// Provides methods that allow the resizing of images using various algorithms.
/// </summary>
public class ResizeProcessor : ImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="ResizeProcessor"/> class.
/// </summary>
/// <param name="sampler">
/// The sampler to perform the resize operation.
/// </param>
public ResizeProcessor(IResampler sampler)
{
Guard.NotNull(sampler, nameof(sampler));
this.Sampler = sampler;
}
/// <summary>
/// Gets the sampler to perform the resize operation.
/// </summary>
public IResampler Sampler { get; }
/// <summary>
/// Gets or sets the horizontal weights.
/// </summary>
protected Weights[] HorizontalWeights { get; set; }
/// <summary>
/// Gets or sets the vertical weights.
/// </summary>
protected Weights[] VerticalWeights { get; set; }
/// <inheritdoc/>
protected override void OnApply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
if (!(this.Sampler is NearestNeighborResampler))
{
this.HorizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width);
this.VerticalWeights = this.PrecomputeWeights(targetRectangle.Height, sourceRectangle.Height);
}
}
/// <inheritdoc/>
protected override void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
// Jump out, we'll deal with that later.
if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle)
{
return;
}
int width = target.Width;
int height = target.Height;
int sourceHeight = sourceRectangle.Height;
int targetX = target.Bounds.X;
int targetY = target.Bounds.Y;
int targetRight = target.Bounds.Right;
int targetBottom = target.Bounds.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
bool compand = this.Compand;
if (this.Sampler is NearestNeighborResampler)
{
// Scaling factors
float widthFactor = sourceRectangle.Width / (float)targetRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)targetRectangle.Height;
using (IPixelAccessor<TColor> sourcePixels = source.Lock())
using (IPixelAccessor<TColor> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
y =>
{
if (targetY <= y && y < targetBottom)
{
// Y coordinates of source points
int originY = (int)((y - startY) * heightFactor);
for (int x = startX; x < endX; x++)
{
if (targetX <= x && x < targetRight)
{
// X coordinates of source points
int originX = (int)((x - startX) * widthFactor);
targetPixels[x, y] = sourcePixels[originX, originY];
}
}
this.OnRowProcessed();
}
});
}
// Break out now.
return;
}
// Interpolate the image using the calculated weights.
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
// First process the columns. Since we are not using multiple threads startY and endY
// are the upper and lower bounds of the source rectangle.
Image<TColor, TDepth> firstPass = new Image<TColor, TDepth>(target.Width, source.Height);
using (IPixelAccessor<TColor> sourcePixels = source.Lock())
using (IPixelAccessor<TColor> firstPassPixels = firstPass.Lock())
using (IPixelAccessor<TColor> targetPixels = target.Lock())
{
Parallel.For(
0,
sourceHeight,
y =>
{
for (int x = startX; x < endX; x++)
{
if (x >= 0 && x < width)
{
// Ensure offsets are normalised for cropping and padding.
int offsetX = x - startX;
float sum = this.HorizontalWeights[offsetX].Sum;
Weight[] horizontalValues = this.HorizontalWeights[offsetX].Values;
// Destination color components
//Color destination = new Color();
//for (int i = 0; i < sum; i++)
//{
// Weight xw = horizontalValues[i];
// int originX = xw.Index;
// Color sourceColor = compand
// ? Color.Expand(sourcePixels[originX, y])
// : sourcePixels[originX, y];
// destination += sourceColor * xw.Value;
//}
//if (compand)
//{
// destination = Color.Compress(destination);
//}
//firstPassPixels[x, y] = destination;
TColor sourceColor;
TColor destination = default(TColor);
for (int i = 0; i < sum; i++)
{
Weight xw = horizontalValues[i];
int originX = xw.Index;
sourceColor = sourcePixels[originX, y];
//Color sourceColor = compand
// ? Color.Expand(sourcePixels[originX, y])
// : sourcePixels[originX, y];
//sourceColor.Multiply(xw.Value);
//destination.Add(sourceColor);
//destination += sourceColor * xw.Value;
sourceColor.Multiply<TColor>(xw.Value);
destination.Add(sourceColor);
}
//if (compand)
//{
// destination = Color.Compress(destination);
//}
//T packed = default(T);
//packed.PackVector(destination);
firstPassPixels[x, y] = destination;
}
}
});
// Now process the rows.
Parallel.For(
startY,
endY,
y =>
{
if (y >= 0 && y < height)
{
// Ensure offsets are normalised for cropping and padding.
int offsetY = y - startY;
float sum = this.VerticalWeights[offsetY].Sum;
Weight[] verticalValues = this.VerticalWeights[offsetY].Values;
for (int x = 0; x < width; x++)
{
// Destination color components
TColor sourceColor;
TColor destination = default(TColor);
for (int i = 0; i < sum; i++)
{
Weight yw = verticalValues[i];
int originY = yw.Index;
sourceColor = firstPassPixels[x, originY];
//Color sourceColor = compand
// ? Color.Expand(firstPassPixels[x, originY])
// : firstPassPixels[x, originY];
//Vector4 sourceColor = firstPassPixels[x, originY].ToVector4();
//destination += sourceColor * yw.Value;
sourceColor.Multiply<TColor>(yw.Value);
destination.Add(sourceColor);
}
//if (compand)
//{
// destination = Color.Compress(destination);
//}
//T packed = default(T);
//packed.PackVector(destination);
targetPixels[x, y] = destination;
}
}
this.OnRowProcessed();
});
}
}
/// <inheritdoc/>
protected override void AfterApply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
// Copy the pixels over.
if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle)
{
target.ClonePixels(target.Width, target.Height, source.Pixels);
}
}
/// <summary>
/// Computes the weights to apply at each pixel when resizing.
/// </summary>
/// <param name="destinationSize">The destination section size.</param>
/// <param name="sourceSize">The source section size.</param>
/// <returns>
/// The <see cref="T:Weights[]"/>.
/// </returns>
protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
{
float scale = (float)destinationSize / sourceSize;
IResampler sampler = this.Sampler;
float radius = sampler.Radius;
double left;
double right;
float weight;
int index;
int sum;
Weights[] result = new Weights[destinationSize];
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
if (scale < 1)
{
float width = radius / scale;
float filterScale = 1 / scale;
// Make the weights slices, one source for each column or row.
for (int i = 0; i < destinationSize; i++)
{
float centre = i / scale;
left = Math.Ceiling(centre - width);
right = Math.Floor(centre + width);
result[i] = new Weights
{
Values = new Weight[(int)(right - left + 1)]
};
for (double j = left; j <= right; j++)
{
weight = sampler.GetValue((float)((centre - j) / filterScale)) / filterScale;
if (j < 0)
{
index = (int)-j;
}
else if (j >= sourceSize)
{
index = (int)((sourceSize - j) + sourceSize - 1);
}
else
{
index = (int)j;
}
sum = (int)result[i].Sum++;
result[i].Values[sum] = new Weight(index, weight);
}
}
}
else
{
// Make the weights slices, one source for each column or row.
for (int i = 0; i < destinationSize; i++)
{
float centre = i / scale;
left = Math.Ceiling(centre - radius);
right = Math.Floor(centre + radius);
result[i] = new Weights
{
Values = new Weight[(int)(right - left + 1)]
};
for (double j = left; j <= right; j++)
{
weight = sampler.GetValue((float)(centre - j));
if (j < 0)
{
index = (int)-j;
}
else if (j >= sourceSize)
{
index = (int)((sourceSize - j) + sourceSize - 1);
}
else
{
index = (int)j;
}
sum = (int)result[i].Sum++;
result[i].Values[sum] = new Weight(index, weight);
}
}
}
return result;
}
/// <summary>
/// Represents the weight to be added to a scaled pixel.
/// </summary>
protected struct Weight
{
/// <summary>
/// Initializes a new instance of the <see cref="Weight"/> struct.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="value">The value.</param>
public Weight(int index, float value)
{
this.Index = index;
this.Value = value;
}
/// <summary>
/// Gets the pixel index.
/// </summary>
public int Index { get; }
/// <summary>
/// Gets the result of the interpolation algorithm.
/// </summary>
public float Value { get; }
}
/// <summary>
/// Represents a collection of weights and their sum.
/// </summary>
protected class Weights
{
/// <summary>
/// Gets or sets the values.
/// </summary>
public Weight[] Values { get; set; }
/// <summary>
/// Gets or sets the sum.
/// </summary>
public float Sum { get; set; }
}
}
}

28
GenericImage/project.json

@ -1,28 +0,0 @@
{
"version": "1.0.0-*",
"buildOptions": {
"allowUnsafe": true,
"debugType": "portable"
},
"dependencies": {
"System.Collections": "4.0.11",
"System.Diagnostics.Debug": "4.0.11",
"System.Diagnostics.Tools": "4.0.1",
"System.IO": "4.1.0",
"System.IO.Compression": "4.1.0",
"System.Linq": "4.1.0",
"System.Linq.Expressions": "4.1.0",
"System.Numerics.Vectors": "4.1.1",
"System.Resources.ResourceManager": "4.0.1",
"System.Runtime.Extensions": "4.1.0",
"System.Runtime.InteropServices": "4.1.0",
"System.Text.Encoding.Extensions": "4.0.11",
"System.Threading": "4.0.11",
"System.Threading.Tasks": "4.0.11",
"System.Threading.Tasks.Parallel": "4.0.1"
},
"frameworks": {
"netstandard1.1": { }
}
}

7
ImageProcessorCore.sln

@ -21,8 +21,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ImageProcessorCore.Benchmarks", "tests\ImageProcessorCore.Benchmarks\ImageProcessorCore.Benchmarks.xproj", "{299D8E18-102C-42DE-ADBF-79098EE706A8}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "GenericImage", "GenericImage\GenericImage.xproj", "{45D91211-9D4A-4382-A114-8786859E302A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -41,10 +39,6 @@ Global
{299D8E18-102C-42DE-ADBF-79098EE706A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{299D8E18-102C-42DE-ADBF-79098EE706A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{299D8E18-102C-42DE-ADBF-79098EE706A8}.Release|Any CPU.Build.0 = Release|Any CPU
{45D91211-9D4A-4382-A114-8786859E302A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45D91211-9D4A-4382-A114-8786859E302A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45D91211-9D4A-4382-A114-8786859E302A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45D91211-9D4A-4382-A114-8786859E302A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -53,6 +47,5 @@ Global
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{F836E8E6-B4D9-4208-8346-140C74678B91} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{299D8E18-102C-42DE-ADBF-79098EE706A8} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{45D91211-9D4A-4382-A114-8786859E302A} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
EndGlobalSection
EndGlobal

2
src/ImageProcessorCore/Bootstrapper.cs

@ -74,7 +74,7 @@ namespace ImageProcessorCore
/// <param name="image">The image</param>
/// <returns>The <see cref="IPixelAccessor"/></returns>
public IPixelAccessor<T, TP> GetPixelAccessor<T, TP>(IImageBase image)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
Type packed = typeof(T);

58
src/ImageProcessorCore/Common/Helpers/Class.cs

@ -1,58 +0,0 @@
namespace ImageProcessorCore.Helpers
{
interface INullOp<T>
{
bool HasValue(T value);
bool AddIfNotNull(ref T accumulator, T value);
}
sealed class StructNullOp<T>
: INullOp<T>, INullOp<T?>
where T : struct
{
public bool HasValue(T value)
{
return true;
}
public bool AddIfNotNull(ref T accumulator, T value)
{
accumulator = Operator<T>.Add(accumulator, value);
return true;
}
public bool HasValue(T? value)
{
return value.HasValue;
}
public bool AddIfNotNull(ref T? accumulator, T? value)
{
if (value.HasValue)
{
accumulator = accumulator.HasValue ?
Operator<T>.Add(
accumulator.GetValueOrDefault(),
value.GetValueOrDefault())
: value;
return true;
}
return false;
}
}
sealed class ClassNullOp<T>
: INullOp<T>
where T : class
{
public bool HasValue(T value)
{
return value != null;
}
public bool AddIfNotNull(ref T accumulator, T value)
{
if (value != null)
{
accumulator = accumulator == null ?
value : Operator<T>.Add(accumulator, value);
return true;
}
return false;
}
}
}

100
src/ImageProcessorCore/Common/Helpers/ExpressionUtil.cs

@ -1,100 +0,0 @@
namespace ImageProcessorCore.Helpers
{
using System;
using System.Linq.Expressions;
/// <summary>
/// General purpose Expression utilities
/// </summary>
public static class ExpressionUtil
{
/// <summary>
/// Create a function delegate representing a unary operation
/// </summary>
/// <typeparam name="TArg1">The parameter type</typeparam>
/// <typeparam name="TResult">The return type</typeparam>
/// <param name="body">Body factory</param>
/// <returns>Compiled function delegate</returns>
public static Func<TArg1, TResult> CreateExpression<TArg1, TResult>(
Func<Expression, UnaryExpression> body)
{
ParameterExpression inp = Expression.Parameter(typeof(TArg1), "inp");
try
{
return Expression.Lambda<Func<TArg1, TResult>>(body(inp), inp).Compile();
}
catch (Exception ex)
{
string msg = ex.Message; // avoid capture of ex itself
return delegate { throw new InvalidOperationException(msg); };
}
}
/// <summary>
/// Create a function delegate representing a binary operation
/// </summary>
/// <typeparam name="TArg1">The first parameter type</typeparam>
/// <typeparam name="TArg2">The second parameter type</typeparam>
/// <typeparam name="TResult">The return type</typeparam>
/// <param name="body">Body factory</param>
/// <returns>Compiled function delegate</returns>
public static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>(
Func<Expression, Expression, BinaryExpression> body)
{
return CreateExpression<TArg1, TArg2, TResult>(body, false);
}
/// <summary>
/// Create a function delegate representing a binary operation
/// </summary>
/// <param name="castArgsToResultOnFailure">
/// If no matching operation is possible, attempt to convert
/// TArg1 and TArg2 to TResult for a match? For example, there is no
/// "decimal operator /(decimal, int)", but by converting TArg2 (int) to
/// TResult (decimal) a match is found.
/// </param>
/// <typeparam name="TArg1">The first parameter type</typeparam>
/// <typeparam name="TArg2">The second parameter type</typeparam>
/// <typeparam name="TResult">The return type</typeparam>
/// <param name="body">Body factory</param>
/// <returns>Compiled function delegate</returns>
public static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>(
Func<Expression, Expression, BinaryExpression> body, bool castArgsToResultOnFailure)
{
ParameterExpression lhs = Expression.Parameter(typeof(TArg1), "lhs");
ParameterExpression rhs = Expression.Parameter(typeof(TArg2), "rhs");
try
{
try
{
return Expression.Lambda<Func<TArg1, TArg2, TResult>>(body(lhs, rhs), lhs, rhs).Compile();
}
catch (InvalidOperationException)
{
// If we show retry and the args aren't already "TValue, TValue, TValue"...
// convert both lhs and rhs to TResult (as appropriate)
if (castArgsToResultOnFailure && !(typeof(TArg1) == typeof(TResult) && typeof(TArg2) == typeof(TResult)))
{
Expression castLhs = typeof(TArg1) == typeof(TResult)
? lhs
: (Expression)Expression.Convert(lhs, typeof(TResult));
Expression castRhs = typeof(TArg2) == typeof(TResult)
? rhs
: (Expression)Expression.Convert(rhs, typeof(TResult));
return Expression.Lambda<Func<TArg1, TArg2, TResult>>(
body(castLhs, castRhs), lhs, rhs).Compile();
}
throw;
}
}
catch (Exception ex)
{
string msg = ex.Message; // avoid capture of ex itself
return delegate { throw new InvalidOperationException(msg); };
}
}
}
}

479
src/ImageProcessorCore/Common/Helpers/Operator.cs

@ -1,479 +0,0 @@
namespace ImageProcessorCore.Helpers
{
using System;
using System.Linq.Expressions;
using System.Reflection;
/// <summary>
/// The Operator class provides easy access to the standard operators
/// (addition, etc) for generic types, using type inference to simplify
/// usage.
/// </summary>
public static class Operator
{
/// <summary>
/// Indicates if the supplied value is non-null,
/// for reference-types or Nullable&lt;T&gt;
/// </summary>
/// <returns>True for non-null values, else false</returns>
public static bool HasValue<T>(T value)
{
return Operator<T>.NullOp.HasValue(value);
}
/// <summary>
/// Increments the accumulator only
/// if the value is non-null. If the accumulator
/// is null, then the accumulator is given the new
/// value; otherwise the accumulator and value
/// are added.
/// </summary>
/// <param name="accumulator">The current total to be incremented (can be null)</param>
/// <param name="value">The value to be tested and added to the accumulator</param>
/// <returns>True if the value is non-null, else false - i.e.
/// "has the accumulator been updated?"</returns>
public static bool AddIfNotNull<T>(ref T accumulator, T value)
{
return Operator<T>.NullOp.AddIfNotNull(ref accumulator, value);
}
/// <summary>
/// Evaluates unary negation (-) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Negate<T>(T value)
{
return Operator<T>.Negate(value);
}
/// <summary>
/// Evaluates bitwise not (~) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Not<T>(T value)
{
return Operator<T>.Not(value);
}
/// <summary>
/// Evaluates bitwise or (|) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Or<T>(T value1, T value2)
{
return Operator<T>.Or(value1, value2);
}
/// <summary>
/// Evaluates bitwise and (&amp;) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T And<T>(T value1, T value2)
{
return Operator<T>.And(value1, value2);
}
/// <summary>
/// Evaluates bitwise xor (^) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Xor<T>(T value1, T value2)
{
return Operator<T>.Xor(value1, value2);
}
/// <summary>
/// Performs a conversion between the given types; this will throw
/// an InvalidOperationException if the type T does not provide a suitable cast, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this cast.
/// </summary>
public static TTo Convert<TFrom, TTo>(TFrom value)
{
return Operator<TFrom, TTo>.Convert(value);
}
/// <summary>
/// Evaluates binary addition (+) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Add<T>(T value1, T value2)
{
return Operator<T>.Add(value1, value2);
}
/// <summary>
/// Evaluates binary addition (+) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 AddAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Add(value1, value2);
}
/// <summary>
/// Evaluates binary subtraction (-) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Subtract<T>(T value1, T value2)
{
return Operator<T>.Subtract(value1, value2);
}
/// <summary>
/// Evaluates binary subtraction(-) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 SubtractAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Subtract(value1, value2);
}
/// <summary>
/// Evaluates binary multiplication (*) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Multiply<T>(T value1, T value2)
{
return Operator<T>.Multiply(value1, value2);
}
/// <summary>
/// Evaluates binary multiplication (*) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 MultiplyAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Multiply(value1, value2);
}
/// <summary>
/// Evaluates binary division (/) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Divide<T>(T value1, T value2)
{
return Operator<T>.Divide(value1, value2);
}
/// <summary>
/// Evaluates binary division (/) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 DivideAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Divide(value1, value2);
}
/// <summary>
/// Evaluates binary equality (==) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool Equal<T>(T value1, T value2)
{
return Operator<T>.Equal(value1, value2);
}
/// <summary>
/// Evaluates binary inequality (!=) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool NotEqual<T>(T value1, T value2)
{
return Operator<T>.NotEqual(value1, value2);
}
/// <summary>
/// Evaluates binary greater-than (&gt;) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool GreaterThan<T>(T value1, T value2)
{
return Operator<T>.GreaterThan(value1, value2);
}
/// <summary>
/// Evaluates binary less-than (&lt;) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool LessThan<T>(T value1, T value2)
{
return Operator<T>.LessThan(value1, value2);
}
/// <summary>
/// Evaluates binary greater-than-on-eqauls (&gt;=) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool GreaterThanOrEqual<T>(T value1, T value2)
{
return Operator<T>.GreaterThanOrEqual(value1, value2);
}
/// <summary>
/// Evaluates binary less-than-or-equal (&lt;=) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool LessThanOrEqual<T>(T value1, T value2)
{
return Operator<T>.LessThanOrEqual(value1, value2);
}
/// <summary>
/// Evaluates integer division (/) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary><remarks>
/// This operation is particularly useful for computing averages and
/// similar aggregates.
/// </remarks>
public static T DivideInt32<T>(T value, int divisor)
{
return Operator<int, T>.Divide(value, divisor);
}
}
/// <summary>
/// Provides standard operators (such as addition) that operate over operands of
/// different types. For operators, the return type is assumed to match the first
/// operand.
/// </summary>
/// <seealso cref="Operator&lt;T&gt;"/>
/// <seealso cref="Operator"/>
public static class Operator<TValue, TResult>
{
private static readonly Func<TValue, TResult> convert;
/// <summary>
/// Returns a delegate to convert a value between two types; this delegate will throw
/// an InvalidOperationException if the type T does not provide a suitable cast, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this cast.
/// </summary>
public static Func<TValue, TResult> Convert => convert;
static Operator()
{
convert = ExpressionUtil.CreateExpression<TValue, TResult>(body => Expression.Convert(body, typeof(TResult)));
add = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Add, true);
subtract = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Subtract, true);
multiply = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Multiply, true);
divide = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Divide, true);
}
private static readonly Func<TResult, TValue, TResult> add, subtract, multiply, divide;
private static readonly Func<TResult, float, TResult> multiplyF, divideF;
/// <summary>
/// Returns a delegate to evaluate binary addition (+) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Add => add;
/// <summary>
/// Returns a delegate to evaluate binary subtraction (-) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Subtract => subtract;
/// <summary>
/// Returns a delegate to evaluate binary multiplication (*) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Multiply => multiply;
/// <summary>
/// Returns a delegate to evaluate binary division (/) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Divide => divide;
public static Func<TResult, float, TResult> MultiplyF => multiplyF;
public static Func<TResult, float, TResult> DivideF => divideF;
}
/// <summary>
/// Provides standard operators (such as addition) over a single type
/// </summary>
/// <seealso cref="Operator"/>
/// <seealso cref="Operator&lt;TValue,TResult&gt;"/>
public static class Operator<T>
{
static readonly INullOp<T> nullOp;
internal static INullOp<T> NullOp => nullOp;
static readonly T zero;
/// <summary>
/// Returns the zero value for value-types (even full Nullable&lt;TInner&gt;) - or null for reference types
/// </summary>
public static T Zero => zero;
static readonly Func<T, T> negate, not;
static readonly Func<T, T, T> or, and, xor;
/// <summary>
/// Returns a delegate to evaluate unary negation (-) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T> Negate => negate;
/// <summary>
/// Returns a delegate to evaluate bitwise not (~) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T> Not => not;
/// <summary>
/// Returns a delegate to evaluate bitwise or (|) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Or => or;
/// <summary>
/// Returns a delegate to evaluate bitwise and (&amp;) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> And => and;
/// <summary>
/// Returns a delegate to evaluate bitwise xor (^) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Xor => xor;
static readonly Func<T, T, T> add, subtract, multiply, divide;
static readonly Func<T, float, T> multiplyF, divideF;
/// <summary>
/// Returns a delegate to evaluate binary addition (+) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Add => add;
/// <summary>
/// Returns a delegate to evaluate binary subtraction (-) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Subtract => subtract;
/// <summary>
/// Returns a delegate to evaluate binary multiplication (*) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Multiply => multiply;
/// <summary>
/// Returns a delegate to evaluate binary division (/) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Divide => divide;
public static Func<T, float, T> MultiplyF => multiplyF;
public static Func<T, float, T> DivideF => divideF;
static readonly Func<T, T, bool> equal, notEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual;
/// <summary>
/// Returns a delegate to evaluate binary equality (==) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> Equal => equal;
/// <summary>
/// Returns a delegate to evaluate binary inequality (!=) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> NotEqual => notEqual;
/// <summary>
/// Returns a delegate to evaluate binary greater-then (&gt;) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> GreaterThan => greaterThan;
/// <summary>
/// Returns a delegate to evaluate binary less-than (&lt;) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> LessThan => lessThan;
/// <summary>
/// Returns a delegate to evaluate binary greater-than-or-equal (&gt;=) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> GreaterThanOrEqual => greaterThanOrEqual;
/// <summary>
/// Returns a delegate to evaluate binary less-than-or-equal (&lt;=) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> LessThanOrEqual => lessThanOrEqual;
static Operator()
{
add = ExpressionUtil.CreateExpression<T, T, T>(Expression.Add);
subtract = ExpressionUtil.CreateExpression<T, T, T>(Expression.Subtract);
divide = ExpressionUtil.CreateExpression<T, T, T>(Expression.Divide);
multiply = ExpressionUtil.CreateExpression<T, T, T>(Expression.Multiply);
multiplyF = ExpressionUtil.CreateExpression<T, float, T>(Expression.Multiply);
divideF = ExpressionUtil.CreateExpression<T, float, T>(Expression.Multiply);
greaterThan = ExpressionUtil.CreateExpression<T, T, bool>(Expression.GreaterThan);
greaterThanOrEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.GreaterThanOrEqual);
lessThan = ExpressionUtil.CreateExpression<T, T, bool>(Expression.LessThan);
lessThanOrEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.LessThanOrEqual);
equal = ExpressionUtil.CreateExpression<T, T, bool>(Expression.Equal);
notEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.NotEqual);
negate = ExpressionUtil.CreateExpression<T, T>(Expression.Negate);
and = ExpressionUtil.CreateExpression<T, T, T>(Expression.And);
or = ExpressionUtil.CreateExpression<T, T, T>(Expression.Or);
not = ExpressionUtil.CreateExpression<T, T>(Expression.Not);
xor = ExpressionUtil.CreateExpression<T, T, T>(Expression.ExclusiveOr);
Type typeT = typeof(T);
if (typeT.GetTypeInfo().IsValueType && typeT.GetTypeInfo().IsGenericType && (typeT.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
// get the *inner* zero (not a null Nullable<TValue>, but default(TValue))
Type nullType = typeT.GetTypeInfo().GenericTypeArguments[0];
zero = (T)Activator.CreateInstance(nullType);
nullOp = (INullOp<T>)Activator.CreateInstance(
typeof(StructNullOp<>).MakeGenericType(nullType));
}
else
{
zero = default(T);
if (typeT.GetTypeInfo().IsValueType)
{
nullOp = (INullOp<T>)Activator.CreateInstance(
typeof(StructNullOp<>).MakeGenericType(typeT));
}
else
{
nullOp = (INullOp<T>)Activator.CreateInstance(
typeof(ClassNullOp<>).MakeGenericType(typeT));
}
}
}
}
}

2
src/ImageProcessorCore/Formats/Bmp/BmpDecoder.cs

@ -75,7 +75,7 @@ namespace ImageProcessorCore.Formats
/// <param name="image">The <see cref="ImageBase{T}"/> to decode to.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public void Decode<T,TP>(Image<T,TP> image, Stream stream)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
new BmpDecoderCore().Decode(image, stream);

10
src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs

@ -60,7 +60,7 @@ namespace ImageProcessorCore.Formats
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
public void Decode<T, TP>(Image<T, TP> image, Stream stream)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
this.currentStream = stream;
@ -195,7 +195,7 @@ namespace ImageProcessorCore.Formats
/// <param name="bits">The number of bits per pixel.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgbPalette<T, TP>(T[] imageData, byte[] colors, int width, int height, int bits, bool inverted)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
// Pixels per byte (bits per pixel)
@ -256,7 +256,7 @@ namespace ImageProcessorCore.Formats
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb16<T, TP>(T[] imageData, int width, int height, bool inverted)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
// We divide here as we will store the colors in our floating point format.
@ -305,7 +305,7 @@ namespace ImageProcessorCore.Formats
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb24<T, TP>(T[] imageData, int width, int height, bool inverted)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
int alignment;
@ -344,7 +344,7 @@ namespace ImageProcessorCore.Formats
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb32<T, TP>(T[] imageData, int width, int height, bool inverted)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
int alignment;

2
src/ImageProcessorCore/Formats/Bmp/BmpEncoder.cs

@ -44,7 +44,7 @@ namespace ImageProcessorCore.Formats
/// <inheritdoc/>
public void Encode<T,TP>(ImageBase<T,TP> image, Stream stream)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
BmpEncoderCore encoder = new BmpEncoderCore();

8
src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs

@ -29,7 +29,7 @@ namespace ImageProcessorCore.Formats
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="bitsPerPixel">The <see cref="BmpBitsPerPixel"/></param>
public void Encode<T,TP>(ImageBase<T,TP> image, Stream stream, BmpBitsPerPixel bitsPerPixel)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
Guard.NotNull(image, nameof(image));
@ -129,7 +129,7 @@ namespace ImageProcessorCore.Formats
/// The <see cref="ImageBase{T}"/> containing pixel data.
/// </param>
private void WriteImage<T,TP>(EndianBinaryWriter writer, ImageBase<T,TP> image)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
// TODO: Add more compression formats.
@ -162,7 +162,7 @@ namespace ImageProcessorCore.Formats
/// <param name="pixels">The <see cref="IPixelAccessor"/> containing pixel data.</param>
/// <param name="amount">The amount to pad each row by.</param>
private void Write32bit<T,TP>(EndianBinaryWriter writer, IPixelAccessor<T,TP> pixels, int amount)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
for (int y = pixels.Height - 1; y >= 0; y--)
@ -189,7 +189,7 @@ namespace ImageProcessorCore.Formats
/// <param name="pixels">The <see cref="IPixelAccessor"/> containing pixel data.</param>
/// <param name="amount">The amount to pad each row by.</param>
private void Write24bit<T,TP>(EndianBinaryWriter writer, IPixelAccessor<T,TP> pixels, int amount)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
for (int y = pixels.Height - 1; y >= 0; y--)

2
src/ImageProcessorCore/Formats/IImageDecoder.cs

@ -45,7 +45,7 @@ namespace ImageProcessorCore.Formats
/// <param name="image">The <see cref="ImageBase{T}"/> to decode to.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
void Decode<T, TP>(Image<T, TP> image, Stream stream)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct;
}
}

2
src/ImageProcessorCore/Formats/IImageEncoder.cs

@ -49,7 +49,7 @@ namespace ImageProcessorCore.Formats
/// <param name="image">The <see cref="ImageBase{T}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
void Encode<T, TP>(ImageBase<T, TP> image, Stream stream)
where T : IPackedVector<TP>,
where T : IPackedVector<T, TP>,
new() where TP : struct;
}
}

2
src/ImageProcessorCore/Image/IImageBase.cs

@ -3,7 +3,7 @@
namespace ImageProcessorCore
{
public interface IImageBase<T, TP> : IImageBase
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
T[] Pixels { get; }

2
src/ImageProcessorCore/Image/IImageFrame.cs

@ -1,7 +1,7 @@
namespace ImageProcessorCore
{
public interface IImageFrame<T,TP> : IImageBase<T,TP>
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
}

4
src/ImageProcessorCore/Image/IImageProcessor.cs

@ -46,7 +46,7 @@ namespace ImageProcessorCore.Processors
/// <paramref name="sourceRectangle"/> doesnt fit the dimension of the image.
/// </exception>
void Apply<T, TP>(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle sourceRectangle)
where T : IPackedVector<TP>,
where T : IPackedVector<T, TP>,
new() where TP : struct;
/// <summary>
@ -70,7 +70,7 @@ namespace ImageProcessorCore.Processors
/// the result of image process as new image.
/// </remarks>
void Apply<T, TP>(ImageBase<T, TP> target, ImageBase<T, TP> source, int width, int height, Rectangle targetRectangle, Rectangle sourceRectangle)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct;
}
}

2
src/ImageProcessorCore/Image/Image.cs

@ -21,7 +21,7 @@ namespace ImageProcessorCore
/// The packed vector containing pixel information.
/// </typeparam>
public class Image<T, TP> : ImageBase<T, TP>
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
/// <summary>

2
src/ImageProcessorCore/Image/ImageBase.cs

@ -15,7 +15,7 @@ namespace ImageProcessorCore
/// The packed vector pixels format.
/// </typeparam>
public abstract class ImageBase<T, TP> : IImageBase<T, TP>
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
/// <summary>

10
src/ImageProcessorCore/Image/ImageExtensions.cs

@ -62,7 +62,7 @@ namespace ImageProcessorCore
/// <param name="processor">The processor to apply to the image.</param>
/// <returns>The <see cref="Image{T}"/>.</returns>
public static Image<T, TP> Process<T, TP>(this Image<T, TP> source, IImageProcessor processor)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
return Process(source, source.Bounds, processor);
@ -80,7 +80,7 @@ namespace ImageProcessorCore
/// <param name="processor">The processors to apply to the image.</param>
/// <returns>The <see cref="Image{T}"/>.</returns>
public static Image<T, TP> Process<T, TP>(this Image<T, TP> source, Rectangle sourceRectangle, IImageProcessor processor)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
return PerformAction(source, true, (sourceImage, targetImage) => processor.Apply(targetImage, sourceImage, sourceRectangle));
@ -99,7 +99,7 @@ namespace ImageProcessorCore
/// <param name="sampler">The processor to apply to the image.</param>
/// <returns>The <see cref="Image{T}"/>.</returns>
public static Image<T, TP> Process<T, TP>(this Image<T, TP> source, int width, int height, IImageSampler sampler)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
return Process(source, width, height, source.Bounds, default(Rectangle), sampler);
@ -125,7 +125,7 @@ namespace ImageProcessorCore
/// <param name="sampler">The processor to apply to the image.</param>
/// <returns>The <see cref="Image{T}"/>.</returns>
public static Image<T, TP> Process<T, TP>(this Image<T, TP> source, int width, int height, Rectangle sourceRectangle, Rectangle targetRectangle, IImageSampler sampler)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
return PerformAction(source, false, (sourceImage, targetImage) => sampler.Apply(targetImage, sourceImage, width, height, targetRectangle, sourceRectangle));
@ -140,7 +140,7 @@ namespace ImageProcessorCore
/// <param name="action">The <see cref="Action"/> to perform against the image.</param>
/// <returns>The <see cref="Image{T}"/>.</returns>
private static Image<T, TP> PerformAction<T, TP>(Image<T, TP> source, bool clone, Action<ImageBase<T, TP>, ImageBase<T, TP>> action)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
Image<T, TP> transformedImage = clone

2
src/ImageProcessorCore/Image/ImageFrame.cs

@ -12,7 +12,7 @@ namespace ImageProcessorCore
/// The packed vector containing pixel information.
/// </typeparam>
public class ImageFrame<T, TP> : ImageBase<T, TP>
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
/// <summary>

10
src/ImageProcessorCore/ImageProcessor.cs

@ -28,7 +28,7 @@ namespace ImageProcessorCore.Processors
/// <inheritdoc/>
public void Apply<T, TP>(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle sourceRectangle)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
try
@ -51,7 +51,7 @@ namespace ImageProcessorCore.Processors
/// <inheritdoc/>
public void Apply<T, TP>(ImageBase<T, TP> target, ImageBase<T, TP> source, int width, int height, Rectangle targetRectangle = default(Rectangle), Rectangle sourceRectangle = default(Rectangle))
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
try
@ -98,7 +98,7 @@ namespace ImageProcessorCore.Processors
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void OnApply<T, TP>(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
}
@ -124,7 +124,7 @@ namespace ImageProcessorCore.Processors
/// the result of image process as new image.
/// </remarks>
protected abstract void Apply<T, TP>(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct;
/// <summary>
@ -141,7 +141,7 @@ namespace ImageProcessorCore.Processors
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void AfterApply<T, TP>(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
}

285
src/ImageProcessorCore/PackedVector/Bgra32.cs

@ -2,6 +2,7 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
{
using System;
@ -10,17 +11,11 @@ namespace ImageProcessorCore
/// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 1.
/// </summary>
public unsafe struct Bgra32 : IPackedVector<uint>, IEquatable<Bgra32>
public struct Bgra32 : IPackedVector<Bgra32, uint>, IEquatable<Bgra32>
{
const uint B_MASK = 0x000000FF;
const uint G_MASK = 0x0000FF00;
const uint R_MASK = 0x00FF0000;
const uint A_MASK = 0xFF000000;
const int B_SHIFT = 0;
const int G_SHIFT = 8;
const int R_SHIFT = 16;
const int A_SHIFT = 24;
/// <summary>
/// The packed value.
/// </summary>
private uint packedValue;
/// <summary>
@ -31,11 +26,29 @@ namespace ImageProcessorCore
/// <param name="r">The red component.</param>
/// <param name="a">The alpha component.</param>
public Bgra32(float b, float g, float r, float a)
: this()
{
Vector4 clamped = Vector4.Clamp(new Vector4(b, g, r, a), Vector4.Zero, Vector4.One) * 255f;
this.packedValue = Pack(ref clamped);
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
/// </summary>
/// <param name="b">The blue component.</param>
/// <param name="g">The green component.</param>
/// <param name="r">The red component.</param>
/// <param name="a">The alpha component.</param>
public Bgra32(byte b, byte g, byte r, byte a)
: this()
{
this.B = b;
this.G = g;
this.R = r;
this.A = a;
this.packedValue = this.Pack();
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
/// </summary>
@ -43,194 +56,31 @@ namespace ImageProcessorCore
/// The vector containing the components for the packed vector.
/// </param>
public Bgra32(Vector4 vector)
: this()
{
Vector4 clamped = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * 255f;
this.packedValue = Pack(ref clamped);
}
public byte B
{
get
{
return (byte)(this.packedValue & B_MASK);
}
set
{
this.packedValue = (this.packedValue & ~B_MASK) | value;
}
}
public byte G
{
get
{
return (byte)((this.packedValue & G_MASK) >> G_SHIFT);
}
set
{
this.packedValue = (this.packedValue & ~G_MASK) | (((uint)value) << G_SHIFT);
}
}
public byte R
{
get
{
return (byte)((this.packedValue & R_MASK) >> R_SHIFT);
}
set
{
this.packedValue = (this.packedValue & ~R_MASK) | (((uint)value) << R_SHIFT);
}
}
public byte A
{
get
{
return (byte)((this.packedValue & A_MASK) >> A_SHIFT);
}
set
{
this.packedValue = (this.packedValue & ~A_MASK) | (((uint)value) << A_SHIFT);
}
}
/// <inheritdoc/>
public uint PackedValue()
{
return this.packedValue;
}
public void Add<TP>(TP value) where TP : IPackedVector<uint>
{
// this.PackVector(this.ToVector4() + value.ToVector4());
}
public void Subtract<TP>(TP value) where TP : IPackedVector<uint>
{
// this.PackVector(this.ToVector4() - value.ToVector4());
}
public void Multiply<TP>(TP value) where TP : IPackedVector<uint>
{
// this.PackVector(this.ToVector4() * value.ToVector4());
}
public void Multiply<TP>(float value) where TP : IPackedVector<uint>
{
this.B = (byte)(this.B * value);
this.G = (byte)(this.G * value);
this.R = (byte)(this.R * value);
this.A = (byte)(this.A * value);
}
public void Divide<TP>(TP value) where TP : IPackedVector<uint>
{
// this.PackVector(this.ToVector4() / value.ToVector4());
}
public void Divide<TP>(float value) where TP : IPackedVector<uint>
{
this.B = (byte)(this.B / value);
this.G = (byte)(this.G / value);
this.R = (byte)(this.R / value);
this.A = (byte)(this.A / value);
}
/// <summary>
/// Computes the product of multiplying a Bgra32 by a given factor.
/// Gets or sets the blue component.
/// </summary>
/// <param name="value">The Bgra32.</param>
/// <param name="factor">The multiplication factor.</param>
/// <returns>
/// The <see cref="Bgra32"/>
/// </returns>
public static Bgra32 operator *(Bgra32 value, float factor)
{
byte b = (byte)(value.B * factor);
byte g = (byte)(value.G * factor);
byte r = (byte)(value.R * factor);
byte a = (byte)(value.A * factor);
return new Bgra32(b, g, r, a);
}
public byte B { get; set; }
/// <summary>
/// Computes the product of multiplying a Bgra32 by a given factor.
/// Gets or sets the green component.
/// </summary>
/// <param name="factor">The multiplication factor.</param>
/// <param name="value">The Bgra32.</param>
/// <returns>
/// The <see cref="Bgra32"/>
/// </returns>
public static Bgra32 operator *(float factor, Bgra32 value)
{
byte b = (byte)(value.B * factor);
byte g = (byte)(value.G * factor);
byte r = (byte)(value.R * factor);
byte a = (byte)(value.A * factor);
return new Bgra32(b, g, r, a);
}
/// <summary>
/// Computes the product of multiplying two Bgra32s.
/// </summary>
/// <param name="left">The Bgra32 on the left hand of the operand.</param>
/// <param name="right">The Bgra32 on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Bgra32"/>
/// </returns>
public static Bgra32 operator *(Bgra32 left, Bgra32 right)
{
byte b = (byte)(left.B * right.B);
byte g = (byte)(left.G * right.G);
byte r = (byte)(left.R * right.R);
byte a = (byte)(left.A * right.A);
return new Bgra32(b, g, r, a);
}
public byte G { get; set; }
/// <summary>
/// Computes the sum of adding two Bgra32s.
/// Gets or sets the red component.
/// </summary>
/// <param name="left">The Bgra32 on the left hand of the operand.</param>
/// <param name="right">The Bgra32 on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Bgra32"/>
/// </returns>
public static Bgra32 operator +(Bgra32 left, Bgra32 right)
{
byte b = (byte)(left.B + right.B);
byte g = (byte)(left.G + right.G);
byte r = (byte)(left.R + right.R);
byte a = (byte)(left.A + right.A);
return new Bgra32(b, g, r, a);
}
public byte R { get; set; }
/// <summary>
/// Computes the difference left by subtracting one Bgra32 from another.
/// Gets or sets the alpha component.
/// </summary>
/// <param name="left">The Bgra32 on the left hand of the operand.</param>
/// <param name="right">The Bgra32 on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Bgra32"/>
/// </returns>
public static Bgra32 operator -(Bgra32 left, Bgra32 right)
{
byte b = (byte)(left.B - right.B);
byte g = (byte)(left.G - right.G);
byte r = (byte)(left.R - right.R);
byte a = (byte)(left.A - right.A);
return new Bgra32(b, g, r, a);
}
public byte A { get; set; }
/// <summary>
/// Compares two <see cref="Bgra32"/> objects for equality.
@ -262,6 +112,61 @@ namespace ImageProcessorCore
return left.packedValue != right.packedValue;
}
/// <inheritdoc/>
public uint PackedValue()
{
this.packedValue = this.Pack();
return this.packedValue;
}
public void Add(Bgra32 value)
{
this.B = (byte)(this.B + value.B);
this.G = (byte)(this.G + value.G);
this.R = (byte)(this.R + value.R);
this.A = (byte)(this.A + value.A);
}
public void Subtract(Bgra32 value)
{
this.B = (byte)(this.B - value.B);
this.G = (byte)(this.G - value.G);
this.R = (byte)(this.R - value.R);
this.A = (byte)(this.A - value.A);
}
public void Multiply(Bgra32 value)
{
this.B = (byte)(this.B * value.B);
this.G = (byte)(this.G * value.G);
this.R = (byte)(this.R * value.R);
this.A = (byte)(this.A * value.A);
}
public void Multiply(float value)
{
this.B = (byte)(this.B * value);
this.G = (byte)(this.G * value);
this.R = (byte)(this.R * value);
this.A = (byte)(this.A * value);
}
public void Divide(Bgra32 value)
{
this.B = (byte)(this.B / value.B);
this.G = (byte)(this.G / value.G);
this.R = (byte)(this.R / value.R);
this.A = (byte)(this.A / value.A);
}
public void Divide(float value)
{
this.B = (byte)(this.B / value);
this.G = (byte)(this.G / value);
this.R = (byte)(this.R / value);
this.A = (byte)(this.A / value);
}
/// <inheritdoc/>
public void PackVector(Vector4 vector)
{
@ -272,7 +177,11 @@ namespace ImageProcessorCore
/// <inheritdoc/>
public void PackBytes(byte x, byte y, byte z, byte w)
{
this.packedValue = Pack(ref x, ref y, ref z, ref w);
this.B = x;
this.G = y;
this.R = z;
this.A = w;
this.packedValue = this.Pack();
}
/// <inheritdoc/>
@ -341,9 +250,15 @@ namespace ImageProcessorCore
((uint)Math.Round(vector.W) << 24);
}
private static uint Pack(ref byte x, ref byte y, ref byte z, ref byte w)
/// <summary>
/// Sets the packed representation from the given component values.
/// </summary>
/// <returns>
/// The <see cref="uint"/>.
/// </returns>
private uint Pack()
{
return x | ((uint)y << 8) | ((uint)z << 16) | ((uint)w << 24);
return this.B | ((uint)this.G << 8) | ((uint)this.R << 16) | ((uint)this.A << 24);
}
/// <summary>

18
src/ImageProcessorCore/PackedVector/IPackedVector.cs

@ -14,26 +14,26 @@ namespace ImageProcessorCore
/// <typeparam name="T">
/// The type of object representing the packed value.
/// </typeparam>
public interface IPackedVector<T> : IPackedVector
where T : struct
public interface IPackedVector<T, TP> : IPackedVector
where TP : struct
{
/// <summary>
/// Gets the packed representation of the value.
/// Typically packed in least to greatest significance order.
/// </summary>
T PackedValue();
TP PackedValue();
void Add<TP>(TP value) where TP : IPackedVector<T>;
void Add(T value);
void Subtract<TP>(TP value) where TP : IPackedVector<T>;
void Subtract(T value);
void Multiply<TP>(TP value) where TP : IPackedVector<T>;
void Multiply(T value);
void Multiply<TP>(float value) where TP : IPackedVector<T>;
void Multiply(float value);
void Divide<TP>(TP value) where TP : IPackedVector<T>;
void Divide(T value);
void Divide<TP>(float value) where TP : IPackedVector<T>;
void Divide(float value);
}
/// <summary>

2
src/ImageProcessorCore/PixelAccessor/IPixelAccessor.cs

@ -11,7 +11,7 @@ namespace ImageProcessorCore
/// Encapsulates properties to provides per-pixel access to an images pixels.
/// </summary>
public interface IPixelAccessor<T, TP> : IPixelAccessor
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
/// <summary>

12
src/ImageProcessorCore/Samplers/Options/ResizeHelper.cs

@ -24,7 +24,7 @@ namespace ImageProcessorCore
/// The <see cref="Rectangle"/>.
/// </returns>
public static Rectangle CalculateTargetLocationAndBounds<T, TP>(ImageBase<T, TP> source, ResizeOptions options)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
switch (options.Mode)
@ -56,7 +56,7 @@ namespace ImageProcessorCore
/// The <see cref="Rectangle"/>.
/// </returns>
private static Rectangle CalculateCropRectangle<T, TP>(ImageBase<T, TP> source, ResizeOptions options)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
int width = options.Size.Width;
@ -176,7 +176,7 @@ namespace ImageProcessorCore
/// The <see cref="Rectangle"/>.
/// </returns>
private static Rectangle CalculatePadRectangle<T, TP>(ImageBase<T, TP> source, ResizeOptions options)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
int width = options.Size.Width;
@ -258,7 +258,7 @@ namespace ImageProcessorCore
/// The <see cref="Rectangle"/>.
/// </returns>
private static Rectangle CalculateBoxPadRectangle<T, TP>(ImageBase<T, TP> source, ResizeOptions options)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
int width = options.Size.Width;
@ -346,7 +346,7 @@ namespace ImageProcessorCore
/// The <see cref="Rectangle"/>.
/// </returns>
private static Rectangle CalculateMaxRectangle<T, TP>(ImageBase<T, TP> source, ResizeOptions options)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
int width = options.Size.Width;
@ -388,7 +388,7 @@ namespace ImageProcessorCore
/// The <see cref="Rectangle"/>.
/// </returns>
private static Rectangle CalculateMinRectangle<T, TP>(ImageBase<T, TP> source, ResizeOptions options)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
int width = options.Size.Width;

41
src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs

@ -9,8 +9,6 @@ namespace ImageProcessorCore.Processors
using System.Numerics;
using System.Threading.Tasks;
using ImageProcessorCore.Helpers;
/// <summary>
/// Provides methods that allow the resizing of images using various algorithms.
/// </summary>
@ -136,25 +134,6 @@ namespace ImageProcessorCore.Processors
Weight[] horizontalValues = this.HorizontalWeights[offsetX].Values;
// Destination color components
//Color destination = new Color();
//for (int i = 0; i < sum; i++)
//{
// Weight xw = horizontalValues[i];
// int originX = xw.Index;
// Color sourceColor = compand
// ? Color.Expand(sourcePixels[originX, y])
// : sourcePixels[originX, y];
// destination += sourceColor * xw.Value;
//}
//if (compand)
//{
// destination = Color.Compress(destination);
//}
//firstPassPixels[x, y] = destination;
T destination = default(T);
for (int i = 0; i < sum; i++)
@ -162,23 +141,20 @@ namespace ImageProcessorCore.Processors
Weight xw = horizontalValues[i];
int originX = xw.Index;
T sourceColor = sourcePixels[originX, y];
//Color sourceColor = compand
// ? Color.Expand(sourcePixels[originX, y])
// : sourcePixels[originX, y];
//sourceColor.Multiply(xw.Value);
//destination.Add(sourceColor);
//destination += sourceColor * xw.Value;
//sourceColor.Multiply<T>(xw.Value);
destination.Add(Operator<T>.Add(destination, Operator<T>.MultiplyF(sourceColor, xw.Value)));
sourceColor.Multiply(xw.Value);
destination.Add(sourceColor);
}
//if (compand)
//{
// destination = Color.Compress(destination);
//}
//T packed = default(T);
//packed.PackVector(destination);
firstPassPixels[x, y] = destination;
}
@ -208,16 +184,14 @@ namespace ImageProcessorCore.Processors
Weight yw = verticalValues[i];
int originY = yw.Index;
T sourceColor = firstPassPixels[x, originY];
//Color sourceColor = compand
// ? Color.Expand(firstPassPixels[x, originY])
// : firstPassPixels[x, originY];
//Vector4 sourceColor = firstPassPixels[x, originY].ToVector4();
//destination += sourceColor * yw.Value;
//sourceColor.Multiply<T>(yw.Value);
//destination.Add(sourceColor);
destination.Add(Operator<T>.Add(destination, Operator<T>.MultiplyF(sourceColor, yw.Value)));
sourceColor.Multiply(yw.Value);
destination.Add(sourceColor);
}
//if (compand)
@ -225,9 +199,6 @@ namespace ImageProcessorCore.Processors
// destination = Color.Compress(destination);
//}
//T packed = default(T);
//packed.PackVector(destination);
targetPixels[x, y] = destination;
}
}

10
src/ImageProcessorCore/Samplers/Resize.cs

@ -22,7 +22,7 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image{T}"/></returns>
/// <remarks>Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image</remarks>
public static Image<T,TP> Resize<T,TP>(this Image<T,TP> source, ResizeOptions options, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
// Ensure size is populated across both dimensions.
@ -52,7 +52,7 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image{T}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static Image<T,TP> Resize<T,TP>(this Image<T,TP> source, int width, int height, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
return Resize(source, width, height, new BicubicResampler(), false, progressHandler);
@ -70,7 +70,7 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image{T}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static Image<T,TP> Resize<T,TP>(this Image<T,TP> source, int width, int height, bool compand, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
return Resize(source, width, height, new BicubicResampler(), compand, progressHandler);
@ -89,7 +89,7 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image{T}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static Image<T,TP> Resize<T,TP>(this Image<T,TP> source, int width, int height, IResampler sampler, bool compand, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
return Resize(source, width, height, sampler, source.Bounds, new Rectangle(0, 0, width, height), compand, progressHandler);
@ -115,7 +115,7 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image{T}"/></returns>
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static Image<T,TP> Resize<T,TP>(this Image<T,TP> source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand = false, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>, new()
where T : IPackedVector<T, TP>, new()
where TP : struct
{
if (width == 0 && height > 0)

1
src/ImageProcessorCore/project.json

@ -23,7 +23,6 @@
"System.IO": "4.1.0",
"System.IO.Compression": "4.1.0",
"System.Linq": "4.1.0",
"System.Linq.Expressions": "4.1.0",
"System.Numerics.Vectors": "4.1.1",
"System.Resources.ResourceManager": "4.0.1",
"System.Runtime.Extensions": "4.1.0",

2
tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

@ -114,7 +114,7 @@ namespace ImageProcessorCore.Tests
{
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
Image<Bgra32> image = new Image<Bgra32>(stream);
Image image = new Image(stream);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
image.Resize(image.Width / 2, image.Height / 2, sampler, false, this.ProgressUpdate)

Loading…
Cancel
Save