mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 72030d1552aad7da681b03900ffe614bb8fee3d2 Former-commit-id: 13219f66ec6a6f6e3d61eddd8102810ab4b948f7 Former-commit-id: 356cddebc1dd634474e0b5ddc450318d5b21bff3pull/1/head
50 changed files with 159 additions and 3390 deletions
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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); }; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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<T>
|
|||
/// </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<TInner> 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<TInner> 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<TInner> 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 (&) for the given type; this will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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 (>) for the given type; this will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (<) for the given type; this will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (>=) for the given type; this will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (<=) for the given type; this will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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<TInner> 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<T>"/>
|
|||
/// <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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TValue,TResult>"/>
|
|||
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<TInner>) - 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<TInner> 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<TInner> 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<TInner> if TInner does not provide this operator.
|
|||
/// </summary>
|
|||
public static Func<T, T, T> Or => or; |
|||
|
|||
/// <summary>
|
|||
/// Returns a delegate to evaluate bitwise and (&) for the given type; this delegate will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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 (>) for the given type; this delegate will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (<) for the given type; this delegate will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (>=) for the given type; this delegate will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (<=) for the given type; this delegate will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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> |
|||
@ -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(); |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
@ -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 }); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
namespace GenericImage |
|||
{ |
|||
using GenericImage.PackedVectors; |
|||
|
|||
public class ImageRgba32 : ImageBase<Rgba32> |
|||
{ |
|||
/// <inheritdocs/>
|
|||
public override IPixelAccessor Lock() |
|||
{ |
|||
return new PixelAccessorRgba32(this); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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")] |
|||
@ -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; } |
|||
} |
|||
} |
|||
} |
|||
@ -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": { } |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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); }; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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<T>
|
|||
/// </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<TInner> 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<TInner> 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<TInner> 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 (&) for the given type; this will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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 (>) for the given type; this will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (<) for the given type; this will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (>=) for the given type; this will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (<=) for the given type; this will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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<TInner> 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<T>"/>
|
|||
/// <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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TValue,TResult>"/>
|
|||
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<TInner>) - 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<TInner> 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<TInner> 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<TInner> if TInner does not provide this operator.
|
|||
/// </summary>
|
|||
public static Func<T, T, T> Or => or; |
|||
|
|||
/// <summary>
|
|||
/// Returns a delegate to evaluate bitwise and (&) for the given type; this delegate will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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<TInner> 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 (>) for the given type; this delegate will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (<) for the given type; this delegate will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (>=) for the given type; this delegate will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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 (<=) for the given type; this delegate will throw
|
|||
/// an InvalidOperationException if the type T does not provide this operator, or for
|
|||
/// Nullable<TInner> 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)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue