diff --git a/GenericImage/Common/Helpers/Class.cs b/GenericImage/Common/Helpers/Class.cs new file mode 100644 index 000000000..ad446739a --- /dev/null +++ b/GenericImage/Common/Helpers/Class.cs @@ -0,0 +1,58 @@ +namespace GenericImage.Helpers +{ + interface INullOp + { + bool HasValue(T value); + bool AddIfNotNull(ref T accumulator, T value); + } + sealed class StructNullOp + : INullOp, INullOp + where T : struct + { + public bool HasValue(T value) + { + return true; + } + public bool AddIfNotNull(ref T accumulator, T value) + { + accumulator = Operator.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.Add( + accumulator.GetValueOrDefault(), + value.GetValueOrDefault()) + : value; + return true; + } + return false; + } + } + sealed class ClassNullOp + : INullOp + 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.Add(accumulator, value); + return true; + } + return false; + } + } +} diff --git a/GenericImage/Common/Helpers/ExpressionUtil.cs b/GenericImage/Common/Helpers/ExpressionUtil.cs new file mode 100644 index 000000000..fc66742a0 --- /dev/null +++ b/GenericImage/Common/Helpers/ExpressionUtil.cs @@ -0,0 +1,100 @@ +namespace GenericImage.Helpers +{ + using System; + using System.Linq.Expressions; + + /// + /// General purpose Expression utilities + /// + public static class ExpressionUtil + { + /// + /// Create a function delegate representing a unary operation + /// + /// The parameter type + /// The return type + /// Body factory + /// Compiled function delegate + public static Func CreateExpression( + Func body) + { + ParameterExpression inp = Expression.Parameter(typeof(TArg1), "inp"); + try + { + return Expression.Lambda>(body(inp), inp).Compile(); + } + catch (Exception ex) + { + string msg = ex.Message; // avoid capture of ex itself + return delegate { throw new InvalidOperationException(msg); }; + } + } + + /// + /// Create a function delegate representing a binary operation + /// + /// The first parameter type + /// The second parameter type + /// The return type + /// Body factory + /// Compiled function delegate + public static Func CreateExpression( + Func body) + { + return CreateExpression(body, false); + } + + /// + /// Create a function delegate representing a binary operation + /// + /// + /// 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. + /// + /// The first parameter type + /// The second parameter type + /// The return type + /// Body factory + /// Compiled function delegate + public static Func CreateExpression( + Func body, bool castArgsToResultOnFailure) + { + ParameterExpression lhs = Expression.Parameter(typeof(TArg1), "lhs"); + ParameterExpression rhs = Expression.Parameter(typeof(TArg2), "rhs"); + try + { + try + { + return Expression.Lambda>(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>( + 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); }; + } + } + } +} diff --git a/GenericImage/Common/Helpers/Operator.cs b/GenericImage/Common/Helpers/Operator.cs new file mode 100644 index 000000000..872eb3abe --- /dev/null +++ b/GenericImage/Common/Helpers/Operator.cs @@ -0,0 +1,479 @@ +namespace GenericImage.Helpers +{ + using System; + using System.Linq.Expressions; + using System.Reflection; + + /// + /// The Operator class provides easy access to the standard operators + /// (addition, etc) for generic types, using type inference to simplify + /// usage. + /// + public static class Operator + { + + /// + /// Indicates if the supplied value is non-null, + /// for reference-types or Nullable<T> + /// + /// True for non-null values, else false + public static bool HasValue(T value) + { + + return Operator.NullOp.HasValue(value); + + } + + /// + /// 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. + /// + /// The current total to be incremented (can be null) + /// The value to be tested and added to the accumulator + /// True if the value is non-null, else false - i.e. + /// "has the accumulator been updated?" + public static bool AddIfNotNull(ref T accumulator, T value) + { + return Operator.NullOp.AddIfNotNull(ref accumulator, value); + } + + /// + /// 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. + /// + public static T Negate(T value) + { + return Operator.Negate(value); + } + /// + /// 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. + /// + public static T Not(T value) + { + return Operator.Not(value); + } + /// + /// 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. + /// + public static T Or(T value1, T value2) + { + return Operator.Or(value1, value2); + } + /// + /// 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. + /// + public static T And(T value1, T value2) + { + return Operator.And(value1, value2); + } + /// + /// 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. + /// + public static T Xor(T value1, T value2) + { + return Operator.Xor(value1, value2); + } + /// + /// 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. + /// + public static TTo Convert(TFrom value) + { + return Operator.Convert(value); + } + /// + /// 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. + /// + public static T Add(T value1, T value2) + { + return Operator.Add(value1, value2); + } + /// + /// 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. + /// + public static TArg1 AddAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Add(value1, value2); + } + /// + /// 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. + /// + public static T Subtract(T value1, T value2) + { + return Operator.Subtract(value1, value2); + } + /// + /// 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. + /// + public static TArg1 SubtractAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Subtract(value1, value2); + } + /// + /// 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. + /// + public static T Multiply(T value1, T value2) + { + return Operator.Multiply(value1, value2); + } + /// + /// 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. + /// + public static TArg1 MultiplyAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Multiply(value1, value2); + } + /// + /// 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. + /// + public static T Divide(T value1, T value2) + { + return Operator.Divide(value1, value2); + } + /// + /// 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. + /// + public static TArg1 DivideAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Divide(value1, value2); + } + /// + /// 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. + /// + public static bool Equal(T value1, T value2) + { + return Operator.Equal(value1, value2); + } + /// + /// 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. + /// + public static bool NotEqual(T value1, T value2) + { + return Operator.NotEqual(value1, value2); + } + /// + /// 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. + /// + public static bool GreaterThan(T value1, T value2) + { + return Operator.GreaterThan(value1, value2); + } + /// + /// 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. + /// + public static bool LessThan(T value1, T value2) + { + return Operator.LessThan(value1, value2); + } + /// + /// 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. + /// + public static bool GreaterThanOrEqual(T value1, T value2) + { + return Operator.GreaterThanOrEqual(value1, value2); + } + /// + /// 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. + /// + public static bool LessThanOrEqual(T value1, T value2) + { + return Operator.LessThanOrEqual(value1, value2); + } + /// + /// 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. + /// + /// This operation is particularly useful for computing averages and + /// similar aggregates. + /// + public static T DivideInt32(T value, int divisor) + { + return Operator.Divide(value, divisor); + } + } + /// + /// 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. + /// + /// + /// + public static class Operator + { + private static readonly Func convert; + /// + /// 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. + /// + public static Func Convert => convert; + + static Operator() + { + convert = ExpressionUtil.CreateExpression(body => Expression.Convert(body, typeof(TResult))); + add = ExpressionUtil.CreateExpression(Expression.Add, true); + subtract = ExpressionUtil.CreateExpression(Expression.Subtract, true); + multiply = ExpressionUtil.CreateExpression(Expression.Multiply, true); + divide = ExpressionUtil.CreateExpression(Expression.Divide, true); + } + + private static readonly Func add, subtract, multiply, divide; + + private static readonly Func multiplyF, divideF; + + /// + /// 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. + /// + public static Func Add => add; + + /// + /// 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. + /// + public static Func Subtract => subtract; + + /// + /// 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. + /// + public static Func Multiply => multiply; + + /// + /// 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. + /// + public static Func Divide => divide; + + public static Func MultiplyF => multiplyF; + + public static Func DivideF => divideF; + } + + /// + /// Provides standard operators (such as addition) over a single type + /// + /// + /// + public static class Operator + { + static readonly INullOp nullOp; + internal static INullOp NullOp => nullOp; + + static readonly T zero; + /// + /// Returns the zero value for value-types (even full Nullable<TInner>) - or null for reference types + /// + public static T Zero => zero; + + static readonly Func negate, not; + static readonly Func or, and, xor; + /// + /// 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. + /// + public static Func Negate => negate; + + /// + /// 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. + /// + public static Func Not => not; + + /// + /// 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. + /// + public static Func Or => or; + + /// + /// 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. + /// + public static Func And => and; + + /// + /// 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. + /// + public static Func Xor => xor; + + static readonly Func add, subtract, multiply, divide; + + static readonly Func multiplyF, divideF; + + /// + /// 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. + /// + public static Func Add => add; + + /// + /// 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. + /// + public static Func Subtract => subtract; + + /// + /// 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. + /// + public static Func Multiply => multiply; + + /// + /// 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. + /// + public static Func Divide => divide; + + public static Func MultiplyF => multiplyF; + + public static Func DivideF => divideF; + + static readonly Func equal, notEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual; + + /// + /// 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. + /// + public static Func Equal => equal; + + /// + /// 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. + /// + public static Func NotEqual => notEqual; + + /// + /// 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. + /// + public static Func GreaterThan => greaterThan; + + /// + /// 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. + /// + public static Func LessThan => lessThan; + + /// + /// 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. + /// + public static Func GreaterThanOrEqual => greaterThanOrEqual; + + /// + /// 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. + /// + public static Func LessThanOrEqual => lessThanOrEqual; + + static Operator() + { + add = ExpressionUtil.CreateExpression(Expression.Add); + subtract = ExpressionUtil.CreateExpression(Expression.Subtract); + divide = ExpressionUtil.CreateExpression(Expression.Divide); + multiply = ExpressionUtil.CreateExpression(Expression.Multiply); + multiplyF = ExpressionUtil.CreateExpression(Expression.Multiply); + divideF = ExpressionUtil.CreateExpression(Expression.Multiply); + + greaterThan = ExpressionUtil.CreateExpression(Expression.GreaterThan); + greaterThanOrEqual = ExpressionUtil.CreateExpression(Expression.GreaterThanOrEqual); + lessThan = ExpressionUtil.CreateExpression(Expression.LessThan); + lessThanOrEqual = ExpressionUtil.CreateExpression(Expression.LessThanOrEqual); + equal = ExpressionUtil.CreateExpression(Expression.Equal); + notEqual = ExpressionUtil.CreateExpression(Expression.NotEqual); + + negate = ExpressionUtil.CreateExpression(Expression.Negate); + and = ExpressionUtil.CreateExpression(Expression.And); + or = ExpressionUtil.CreateExpression(Expression.Or); + not = ExpressionUtil.CreateExpression(Expression.Not); + xor = ExpressionUtil.CreateExpression(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, but default(TValue)) + Type nullType = typeT.GetTypeInfo().GenericTypeArguments[0]; + zero = (T)Activator.CreateInstance(nullType); + nullOp = (INullOp)Activator.CreateInstance( + typeof(StructNullOp<>).MakeGenericType(nullType)); + } + else + { + zero = default(T); + if (typeT.GetTypeInfo().IsValueType) + { + nullOp = (INullOp)Activator.CreateInstance( + typeof(StructNullOp<>).MakeGenericType(typeT)); + } + else + { + nullOp = (INullOp)Activator.CreateInstance( + typeof(ClassNullOp<>).MakeGenericType(typeT)); + } + } + } + } +} diff --git a/GenericImage/PackedVectors/Bgra.cs b/GenericImage/PackedVectors/Bgra.cs index 7b82c0683..dadc844c0 100644 --- a/GenericImage/PackedVectors/Bgra.cs +++ b/GenericImage/PackedVectors/Bgra.cs @@ -1,52 +1,85 @@ using System; using System.Numerics; +using GenericImage.Helpers; +using GenericImage.PackedVectors; + namespace GenericImage.PackedVectors { - public struct Bgra : IColor4 + public struct Bgra : IColor where TDepth : struct { - public TDepth X { get; set; } - - public TDepth Y { get; set; } + private static readonly TDepth[] Components = new TDepth[4]; - public TDepth Z { get; set; } - - public TDepth W { get; set; } + public TDepth[] Values => Components; public void Add(TColor value) where TColor : IColor { - throw new NotImplementedException(); + for (int i = 0; i < value.Values.Length; i++) + { + this.Values[i] = Operator.Add(this.Values[i], value.Values[i]); + } } public void Multiply(TColor value) where TColor : IColor { - throw new NotImplementedException(); + for (int i = 0; i < value.Values.Length; i++) + { + this.Values[i] = Operator.Multiply(this.Values[i], value.Values[i]); + } } public void Multiply(float value) where TColor : IColor { - throw new NotImplementedException(); + for (int i = 0; i < this.Values.Length; i++) + { + this.Values[i] = Operator.MultiplyF(this.Values[i], value); + } } public void Divide(TColor value) where TColor : IColor { - throw new NotImplementedException(); + for (int i = 0; i < value.Values.Length; i++) + { + this.Values[i] = Operator.Divide(this.Values[i], value.Values[i]); + } } - public void PackVector(Vector4 vector) + public void Divide(float value) where TColor : IColor { - throw new NotImplementedException(); + for (int i = 0; i < this.Values.Length; i++) + { + this.Values[i] = Operator.DivideF(this.Values[i], value); + } } - public Vector4 ToVector() + public byte[] ToBytes() { - throw new NotImplementedException(); + 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 byte[] ToBytes() + public void FromBytes(byte[] bytes) { - throw new System.NotImplementedException(); + if (bytes.Length != 4) + { + throw new ArgumentOutOfRangeException(nameof(bytes)); + } + + for (int i = 0; i < bytes.Length; i++) + { + this.Values[i] = (TDepth)(object)bytes[i]; + } } } } diff --git a/GenericImage/PackedVectors/IColor.cs b/GenericImage/PackedVectors/IColor.cs index 52825d63a..69924e47f 100644 --- a/GenericImage/PackedVectors/IColor.cs +++ b/GenericImage/PackedVectors/IColor.cs @@ -17,6 +17,8 @@ public interface IColor : IColor where TDepth : struct { + TDepth[] Values { get; } + void Add(TColor value) where TColor : IColor; void Multiply(TColor value) where TColor : IColor; @@ -24,14 +26,17 @@ void Multiply(float value) where TColor : IColor; void Divide(TColor value) where TColor : IColor; + + void Divide(float value) where TColor : IColor; + + void FromBytes(byte[] bytes); + + byte[] ToBytes(); } public interface IColor { - void PackVector(Vector4 vector); - Vector4 ToVector(); - byte[] ToBytes(); } } diff --git a/GenericImage/project.json b/GenericImage/project.json index 617c22489..bdcdc4ff7 100644 --- a/GenericImage/project.json +++ b/GenericImage/project.json @@ -12,6 +12,7 @@ "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", diff --git a/src/ImageProcessorCore/Common/Helpers/Class.cs b/src/ImageProcessorCore/Common/Helpers/Class.cs new file mode 100644 index 000000000..cf60c9f34 --- /dev/null +++ b/src/ImageProcessorCore/Common/Helpers/Class.cs @@ -0,0 +1,58 @@ +namespace ImageProcessorCore.Helpers +{ + interface INullOp + { + bool HasValue(T value); + bool AddIfNotNull(ref T accumulator, T value); + } + sealed class StructNullOp + : INullOp, INullOp + where T : struct + { + public bool HasValue(T value) + { + return true; + } + public bool AddIfNotNull(ref T accumulator, T value) + { + accumulator = Operator.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.Add( + accumulator.GetValueOrDefault(), + value.GetValueOrDefault()) + : value; + return true; + } + return false; + } + } + sealed class ClassNullOp + : INullOp + 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.Add(accumulator, value); + return true; + } + return false; + } + } +} diff --git a/src/ImageProcessorCore/Common/Helpers/ExpressionUtil.cs b/src/ImageProcessorCore/Common/Helpers/ExpressionUtil.cs new file mode 100644 index 000000000..b3c7dbe80 --- /dev/null +++ b/src/ImageProcessorCore/Common/Helpers/ExpressionUtil.cs @@ -0,0 +1,100 @@ +namespace ImageProcessorCore.Helpers +{ + using System; + using System.Linq.Expressions; + + /// + /// General purpose Expression utilities + /// + public static class ExpressionUtil + { + /// + /// Create a function delegate representing a unary operation + /// + /// The parameter type + /// The return type + /// Body factory + /// Compiled function delegate + public static Func CreateExpression( + Func body) + { + ParameterExpression inp = Expression.Parameter(typeof(TArg1), "inp"); + try + { + return Expression.Lambda>(body(inp), inp).Compile(); + } + catch (Exception ex) + { + string msg = ex.Message; // avoid capture of ex itself + return delegate { throw new InvalidOperationException(msg); }; + } + } + + /// + /// Create a function delegate representing a binary operation + /// + /// The first parameter type + /// The second parameter type + /// The return type + /// Body factory + /// Compiled function delegate + public static Func CreateExpression( + Func body) + { + return CreateExpression(body, false); + } + + /// + /// Create a function delegate representing a binary operation + /// + /// + /// 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. + /// + /// The first parameter type + /// The second parameter type + /// The return type + /// Body factory + /// Compiled function delegate + public static Func CreateExpression( + Func body, bool castArgsToResultOnFailure) + { + ParameterExpression lhs = Expression.Parameter(typeof(TArg1), "lhs"); + ParameterExpression rhs = Expression.Parameter(typeof(TArg2), "rhs"); + try + { + try + { + return Expression.Lambda>(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>( + 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); }; + } + } + } +} diff --git a/src/ImageProcessorCore/Common/Helpers/Operator.cs b/src/ImageProcessorCore/Common/Helpers/Operator.cs new file mode 100644 index 000000000..fc09a0c7b --- /dev/null +++ b/src/ImageProcessorCore/Common/Helpers/Operator.cs @@ -0,0 +1,464 @@ +namespace ImageProcessorCore.Helpers +{ + using System; + using System.Linq.Expressions; + using System.Reflection; + + /// + /// The Operator class provides easy access to the standard operators + /// (addition, etc) for generic types, using type inference to simplify + /// usage. + /// + public static class Operator + { + + /// + /// Indicates if the supplied value is non-null, + /// for reference-types or Nullable<T> + /// + /// True for non-null values, else false + public static bool HasValue(T value) + { + + return Operator.NullOp.HasValue(value); + + } + + /// + /// 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. + /// + /// The current total to be incremented (can be null) + /// The value to be tested and added to the accumulator + /// True if the value is non-null, else false - i.e. + /// "has the accumulator been updated?" + public static bool AddIfNotNull(ref T accumulator, T value) + { + return Operator.NullOp.AddIfNotNull(ref accumulator, value); + } + + /// + /// 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. + /// + public static T Negate(T value) + { + return Operator.Negate(value); + } + /// + /// 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. + /// + public static T Not(T value) + { + return Operator.Not(value); + } + /// + /// 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. + /// + public static T Or(T value1, T value2) + { + return Operator.Or(value1, value2); + } + /// + /// 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. + /// + public static T And(T value1, T value2) + { + return Operator.And(value1, value2); + } + /// + /// 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. + /// + public static T Xor(T value1, T value2) + { + return Operator.Xor(value1, value2); + } + /// + /// 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. + /// + public static TTo Convert(TFrom value) + { + return Operator.Convert(value); + } + /// + /// 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. + /// + public static T Add(T value1, T value2) + { + return Operator.Add(value1, value2); + } + /// + /// 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. + /// + public static TArg1 AddAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Add(value1, value2); + } + /// + /// 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. + /// + public static T Subtract(T value1, T value2) + { + return Operator.Subtract(value1, value2); + } + /// + /// 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. + /// + public static TArg1 SubtractAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Subtract(value1, value2); + } + /// + /// 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. + /// + public static T Multiply(T value1, T value2) + { + return Operator.Multiply(value1, value2); + } + /// + /// 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. + /// + public static TArg1 MultiplyAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Multiply(value1, value2); + } + /// + /// 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. + /// + public static T Divide(T value1, T value2) + { + return Operator.Divide(value1, value2); + } + /// + /// 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. + /// + public static TArg1 DivideAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Divide(value1, value2); + } + /// + /// 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. + /// + public static bool Equal(T value1, T value2) + { + return Operator.Equal(value1, value2); + } + /// + /// 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. + /// + public static bool NotEqual(T value1, T value2) + { + return Operator.NotEqual(value1, value2); + } + /// + /// 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. + /// + public static bool GreaterThan(T value1, T value2) + { + return Operator.GreaterThan(value1, value2); + } + /// + /// 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. + /// + public static bool LessThan(T value1, T value2) + { + return Operator.LessThan(value1, value2); + } + /// + /// 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. + /// + public static bool GreaterThanOrEqual(T value1, T value2) + { + return Operator.GreaterThanOrEqual(value1, value2); + } + /// + /// 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. + /// + public static bool LessThanOrEqual(T value1, T value2) + { + return Operator.LessThanOrEqual(value1, value2); + } + /// + /// 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. + /// + /// This operation is particularly useful for computing averages and + /// similar aggregates. + /// + public static T DivideInt32(T value, int divisor) + { + return Operator.Divide(value, divisor); + } + } + /// + /// 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. + /// + /// + /// + public static class Operator + { + private static readonly Func convert; + /// + /// 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. + /// + public static Func Convert => convert; + + static Operator() + { + convert = ExpressionUtil.CreateExpression(body => Expression.Convert(body, typeof(TResult))); + add = ExpressionUtil.CreateExpression(Expression.Add, true); + subtract = ExpressionUtil.CreateExpression(Expression.Subtract, true); + multiply = ExpressionUtil.CreateExpression(Expression.Multiply, true); + divide = ExpressionUtil.CreateExpression(Expression.Divide, true); + } + + private static readonly Func add, subtract, multiply, divide; + /// + /// 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. + /// + public static Func Add => add; + + /// + /// 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. + /// + public static Func Subtract => subtract; + + /// + /// 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. + /// + public static Func Multiply => multiply; + + /// + /// 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. + /// + public static Func Divide => divide; + } + + /// + /// Provides standard operators (such as addition) over a single type + /// + /// + /// + public static class Operator + { + static readonly INullOp nullOp; + internal static INullOp NullOp => nullOp; + + static readonly T zero; + /// + /// Returns the zero value for value-types (even full Nullable<TInner>) - or null for reference types + /// + public static T Zero => zero; + + static readonly Func negate, not; + static readonly Func or, and, xor; + /// + /// 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. + /// + public static Func Negate => negate; + + /// + /// 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. + /// + public static Func Not => not; + + /// + /// 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. + /// + public static Func Or => or; + + /// + /// 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. + /// + public static Func And => and; + + /// + /// 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. + /// + public static Func Xor => xor; + + static readonly Func add, subtract, multiply, divide; + + /// + /// 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. + /// + public static Func Add => add; + + /// + /// 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. + /// + public static Func Subtract => subtract; + + /// + /// 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. + /// + public static Func Multiply => multiply; + + /// + /// 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. + /// + public static Func Divide => divide; + + static readonly Func equal, notEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual; + + /// + /// 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. + /// + public static Func Equal => equal; + + /// + /// 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. + /// + public static Func NotEqual => notEqual; + + /// + /// 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. + /// + public static Func GreaterThan => greaterThan; + + /// + /// 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. + /// + public static Func LessThan => lessThan; + + /// + /// 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. + /// + public static Func GreaterThanOrEqual => greaterThanOrEqual; + + /// + /// 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. + /// + public static Func LessThanOrEqual => lessThanOrEqual; + + static Operator() + { + add = ExpressionUtil.CreateExpression(Expression.Add); + subtract = ExpressionUtil.CreateExpression(Expression.Subtract); + divide = ExpressionUtil.CreateExpression(Expression.Divide); + multiply = ExpressionUtil.CreateExpression(Expression.Multiply); + + greaterThan = ExpressionUtil.CreateExpression(Expression.GreaterThan); + greaterThanOrEqual = ExpressionUtil.CreateExpression(Expression.GreaterThanOrEqual); + lessThan = ExpressionUtil.CreateExpression(Expression.LessThan); + lessThanOrEqual = ExpressionUtil.CreateExpression(Expression.LessThanOrEqual); + equal = ExpressionUtil.CreateExpression(Expression.Equal); + notEqual = ExpressionUtil.CreateExpression(Expression.NotEqual); + + negate = ExpressionUtil.CreateExpression(Expression.Negate); + and = ExpressionUtil.CreateExpression(Expression.And); + or = ExpressionUtil.CreateExpression(Expression.Or); + not = ExpressionUtil.CreateExpression(Expression.Not); + xor = ExpressionUtil.CreateExpression(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, but default(TValue)) + Type nullType = typeT.GetTypeInfo().GenericTypeArguments[0]; + zero = (T)Activator.CreateInstance(nullType); + nullOp = (INullOp)Activator.CreateInstance( + typeof(StructNullOp<>).MakeGenericType(nullType)); + } + else + { + zero = default(T); + if (typeT.GetTypeInfo().IsValueType) + { + nullOp = (INullOp)Activator.CreateInstance( + typeof(StructNullOp<>).MakeGenericType(typeT)); + } + else + { + nullOp = (INullOp)Activator.CreateInstance( + typeof(ClassNullOp<>).MakeGenericType(typeT)); + } + } + } + } +} diff --git a/src/ImageProcessorCore/PackedVector/Bgra32.cs b/src/ImageProcessorCore/PackedVector/Bgra32.cs index c1119d9c4..d1a692e5d 100644 --- a/src/ImageProcessorCore/PackedVector/Bgra32.cs +++ b/src/ImageProcessorCore/PackedVector/Bgra32.cs @@ -43,14 +43,14 @@ namespace ImageProcessorCore // The maths are wrong here I just wanted to test the performance to see if the // issues are caused by the Vector transform or something else. - public void Add(IPackedVector value) + public void Add(IPackedVector value) { - + } public void Subtract(IPackedVector value) { - + } public void Multiply(IPackedVector value) @@ -65,12 +65,12 @@ namespace ImageProcessorCore public void Divide(IPackedVector value) { - + } public void Divide(float value) { - + } /// @@ -132,11 +132,11 @@ namespace ImageProcessorCore { return new[] { - (byte)(this.PackedValue & 0xFF), - (byte)((this.PackedValue >> 8) & 0xFF), - (byte)((this.PackedValue >> 16) & 0xFF), - (byte)((this.PackedValue >> 24) & 0xFF) - }; + (byte)(this.PackedValue & 0xFF), + (byte)((this.PackedValue >> 8) & 0xFF), + (byte)((this.PackedValue >> 16) & 0xFF), + (byte)((this.PackedValue >> 24) & 0xFF) + }; } /// @@ -178,9 +178,9 @@ namespace ImageProcessorCore private static uint Pack(ref Vector4 vector) { return (uint)Math.Round(vector.X) | - ((uint)Math.Round(vector.Y) << 8) | - ((uint)Math.Round(vector.Z) << 16) | - ((uint)Math.Round(vector.W) << 24); + ((uint)Math.Round(vector.Y) << 8) | + ((uint)Math.Round(vector.Z) << 16) | + ((uint)Math.Round(vector.W) << 24); } /// diff --git a/src/ImageProcessorCore/PackedVector/IPackedVector.cs b/src/ImageProcessorCore/PackedVector/IPackedVector.cs index bf4556309..90d52b27f 100644 --- a/src/ImageProcessorCore/PackedVector/IPackedVector.cs +++ b/src/ImageProcessorCore/PackedVector/IPackedVector.cs @@ -31,29 +31,29 @@ namespace ImageProcessorCore /// public interface IPackedVector { - void Add(U value); + //void Add(U value); - void Subtract(U value); + //void Subtract(U value); - void Multiply(U value); + //void Multiply(U value); - void Multiply(float value); + //void Multiply(float value); - void Divide(U value); + //void Divide(U value); - void Divide(float value); + //void Divide(float value); - //void Add(IPackedVector value); + void Add(IPackedVector value); - //void Subtract(IPackedVector value); + void Subtract(IPackedVector value); - //void Multiply(IPackedVector value); + void Multiply(IPackedVector value); - //void Multiply(float value); + void Multiply(float value); - //void Divide(IPackedVector value); + void Divide(IPackedVector value); - //void Divide(float value); + void Divide(float value); /// /// Sets the packed representation from a . diff --git a/src/ImageProcessorCore/project.json b/src/ImageProcessorCore/project.json index e3800d495..9469f297c 100644 --- a/src/ImageProcessorCore/project.json +++ b/src/ImageProcessorCore/project.json @@ -23,6 +23,7 @@ "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",