diff --git a/.gitignore b/.gitignore
index 4e9864be0..ae6765a03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -108,6 +108,7 @@ TestResults
*.Cache
ClientBin
stylecop.*
+!stylecop.json
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
diff --git a/src/ImageProcessor/Colors/Bgra.cs b/src/ImageProcessor/Colors/Bgra.cs
deleted file mode 100644
index 052a036e4..000000000
--- a/src/ImageProcessor/Colors/Bgra.cs
+++ /dev/null
@@ -1,397 +0,0 @@
-//
-// Copyright (c) James South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageProcessor
-{
- using System;
- using System.ComponentModel;
- using System.Runtime.InteropServices;
-
- ///
- /// Represents an BGRA (blue, green, red, alpha) color.
- ///
- [StructLayout(LayoutKind.Explicit)]
- public struct Bgra : IEquatable
- {
- ///
- /// Represents a that has B, G, R, and A values set to zero.
- ///
- public static readonly Bgra Empty = default(Bgra);
-
- ///
- /// Represents a transparent that has B, G, R, and A values set to 255, 255, 255, 0.
- ///
- public static readonly Bgra Transparent = new Bgra(255, 255, 255, 0);
-
- ///
- /// Represents a black that has B, G, R, and A values set to 0, 0, 0, 0.
- ///
- public static readonly Bgra Black = new Bgra(0, 0, 0, 255);
-
- ///
- /// Represents a white that has B, G, R, and A values set to 255, 255, 255, 255.
- ///
- public static readonly Bgra White = new Bgra(255, 255, 255, 255);
-
- ///
- /// Holds the blue component of the color
- ///
- [FieldOffset(0)]
- public readonly byte B;
-
- ///
- /// Holds the green component of the color
- ///
- [FieldOffset(1)]
- public readonly byte G;
-
- ///
- /// Holds the red component of the color
- ///
- [FieldOffset(2)]
- public readonly byte R;
-
- ///
- /// Holds the alpha component of the color
- ///
- [FieldOffset(3)]
- public readonly byte A;
-
- ///
- /// Permits the to be treated as a 32 bit integer.
- ///
- [FieldOffset(0)]
- public readonly int BGRA;
-
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.0001f;
-
- ///
- /// Initializes a new instance of the struct.
- ///
- ///
- /// The blue component of this .
- ///
- ///
- /// The green component of this .
- ///
- ///
- /// The red component of this .
- ///
- public Bgra(byte b, byte g, byte r)
- : this()
- {
- this.B = b;
- this.G = g;
- this.R = r;
- this.A = 255;
- }
-
- ///
- /// Initializes a new instance of the struct.
- ///
- ///
- /// The blue component of this .
- ///
- ///
- /// The green component of this .
- ///
- ///
- /// The red component of this .
- ///
- ///
- /// The alpha component of this .
- ///
- public Bgra(byte b, byte g, byte r, byte a)
- : this()
- {
- this.B = b;
- this.G = g;
- this.R = r;
- this.A = a;
- }
-
- ///
- /// Initializes a new instance of the struct.
- ///
- ///
- /// The combined color components.
- ///
- public Bgra(int bgra)
- : this()
- {
- this.BGRA = bgra;
- }
-
- ///
- /// Initializes a new instance of the struct.
- ///
- ///
- /// The hexadecimal representation of the combined color components arranged
- /// in rgb, rrggbb, or aarrggbb format to match web syntax.
- ///
- public Bgra(string hex)
- : this()
- {
- // Hexadecimal representations are layed out AARRGGBB to we need to do some reordering.
- hex = hex.StartsWith("#") ? hex.Substring(1) : hex;
-
- if (hex.Length != 8 && hex.Length != 6 && hex.Length != 3)
- {
- throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex));
- }
-
- if (hex.Length == 8)
- {
- this.B = Convert.ToByte(hex.Substring(6, 2), 16);
- this.G = Convert.ToByte(hex.Substring(4, 2), 16);
- this.R = Convert.ToByte(hex.Substring(2, 2), 16);
- this.A = Convert.ToByte(hex.Substring(0, 2), 16);
- }
- else if (hex.Length == 6)
- {
- this.B = Convert.ToByte(hex.Substring(4, 2), 16);
- this.G = Convert.ToByte(hex.Substring(2, 2), 16);
- this.R = Convert.ToByte(hex.Substring(0, 2), 16);
- this.A = 255;
- }
- else
- {
- string b = char.ToString(hex[2]);
- string g = char.ToString(hex[1]);
- string r = char.ToString(hex[0]);
-
- this.B = Convert.ToByte(b + b, 16);
- this.G = Convert.ToByte(g + g, 16);
- this.R = Convert.ToByte(r + r, 16);
- this.A = 255;
- }
- }
-
- ///
- /// Gets a value indicating whether this is empty.
- ///
- [EditorBrowsable(EditorBrowsableState.Never)]
- public bool IsEmpty => this.B == 0 && this.G == 0 && this.R == 0 && this.A == 0;
-
- ///
- /// Allows the implicit conversion of an instance of to a
- /// .
- ///
- ///
- /// The instance of to convert.
- ///
- ///
- /// An instance of .
- ///
- public static implicit operator Bgra(Hsv color)
- {
- float s = color.S / 100;
- float v = color.V / 100;
-
- if (Math.Abs(s) < Epsilon)
- {
- byte component = (byte)(v * 255);
- return new Bgra(component, component, component, 255);
- }
-
- float h = (Math.Abs(color.H - 360) < Epsilon) ? 0 : color.H / 60;
- int i = (int)Math.Truncate(h);
- float f = h - i;
-
- float p = v * (1.0f - s);
- float q = v * (1.0f - (s * f));
- float t = v * (1.0f - (s * (1.0f - f)));
-
- float r, g, b;
- switch (i)
- {
- case 0:
- r = v;
- g = t;
- b = p;
- break;
-
- case 1:
- r = q;
- g = v;
- b = p;
- break;
-
- case 2:
- r = p;
- g = v;
- b = t;
- break;
-
- case 3:
- r = p;
- g = q;
- b = v;
- break;
-
- case 4:
- r = t;
- g = p;
- b = v;
- break;
-
- default:
- r = v;
- g = p;
- b = q;
- break;
- }
-
- return new Bgra((byte)Math.Round(b * 255), (byte)Math.Round(g * 255), (byte)Math.Round(r * 255));
- }
-
- ///
- /// Allows the implicit conversion of an instance of to a
- /// .
- ///
- ///
- /// The instance of to convert.
- ///
- ///
- /// An instance of .
- ///
- public static implicit operator Bgra(YCbCr color)
- {
- float y = color.Y;
- float cb = color.Cb - 128;
- float cr = color.Cr - 128;
-
- byte b = (y + (1.772 * cb)).ToByte();
- byte g = (y - (0.34414 * cb) - (0.71414 * cr)).ToByte();
- byte r = (y + (1.402 * cr)).ToByte();
-
- return new Bgra(b, g, r, 255);
- }
-
- ///
- /// Allows the implicit conversion of an instance of to a
- /// .
- ///
- ///
- /// The instance of to convert.
- ///
- ///
- /// An instance of .
- ///
- public static implicit operator Bgra(Cmyk cmykColor)
- {
- int red = Convert.ToInt32((1 - (cmykColor.C / 100)) * (1 - (cmykColor.K / 100)) * 255.0);
- int green = Convert.ToInt32((1 - (cmykColor.M / 100)) * (1 - (cmykColor.K / 100)) * 255.0);
- int blue = Convert.ToInt32((1 - (cmykColor.Y / 100)) * (1 - (cmykColor.K / 100)) * 255.0);
- return new Bgra(blue.ToByte(), green.ToByte(), red.ToByte());
- }
-
- ///
- /// Compares two objects. The result specifies whether the values
- /// of the , , , and
- /// properties of the two objects are equal.
- ///
- ///
- /// The on the left side of the operand.
- ///
- ///
- /// The on the right side of the operand.
- ///
- ///
- /// True if the current left is equal to the parameter; otherwise, false.
- ///
- public static bool operator ==(Bgra left, Bgra right)
- {
- return left.Equals(right);
- }
-
- ///
- /// Compares two objects. The result specifies whether the values
- /// of the , , , and
- /// properties of the two objects are unequal.
- ///
- ///
- /// The on the left side of the operand.
- ///
- ///
- /// The on the right side of the operand.
- ///
- ///
- /// True if the current left is unequal to the parameter; otherwise, false.
- ///
- public static bool operator !=(Bgra left, Bgra right)
- {
- return !left.Equals(right);
- }
-
- ///
- /// Indicates whether this instance and a specified object are equal.
- ///
- ///
- /// true if and this instance are the same type and represent the same value; otherwise, false.
- ///
- /// Another object to compare to.
- public override bool Equals(object obj)
- {
- if (obj is Bgra)
- {
- Bgra color = (Bgra)obj;
-
- return this.BGRA == color.BGRA;
- }
-
- return false;
- }
-
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
- public override int GetHashCode()
- {
- unchecked
- {
- int hashCode = this.B.GetHashCode();
- hashCode = (hashCode * 397) ^ this.G.GetHashCode();
- hashCode = (hashCode * 397) ^ this.R.GetHashCode();
- hashCode = (hashCode * 397) ^ this.A.GetHashCode();
- return hashCode;
- }
- }
-
- ///
- /// Returns the fully qualified type name of this instance.
- ///
- ///
- /// A containing a fully qualified type name.
- ///
- public override string ToString()
- {
- if (this.IsEmpty)
- {
- return "Color [ Empty ]";
- }
-
- return $"Color [ B={this.B}, G={this.G}, R={this.R}, A={this.A} ]";
- }
-
- ///
- /// Indicates whether the current object is equal to another object of the same type.
- ///
- ///
- /// True if the current object is equal to the parameter; otherwise, false.
- ///
- /// An object to compare with this object.
- public bool Equals(Bgra other)
- {
- return this.BGRA == other.BGRA;
- }
- }
-}
diff --git a/src/ImageProcessor/Colors/Color.cs b/src/ImageProcessor/Colors/Color.cs
new file mode 100644
index 000000000..d85464471
--- /dev/null
+++ b/src/ImageProcessor/Colors/Color.cs
@@ -0,0 +1,541 @@
+//
+// Copyright (c) James South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessor
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+
+ ///
+ /// Represents a four-component color using red, green, blue, and alpha data.
+ ///
+ ///
+ /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
+ /// as it avoids the need to create new values for modification operations.
+ ///
+ public struct Color : IEquatable
+ {
+ ///
+ /// Represents a that has R, G, B, and A values set to zero.
+ ///
+ public static readonly Color Empty = default(Color);
+
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ private const float Epsilon = 0.0001f;
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private Vector4 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct with the alpha component set to 1.
+ ///
+ /// The red component of this .
+ /// The green component of this .
+ /// The blue component of this .
+ public Color(float r, float g, float b)
+ : this(r, g, b, 1)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The red component of this .
+ /// The green component of this .
+ /// The blue component of this .
+ /// The alpha component of this .
+ public Color(float r, float g, float b, float a)
+ : this()
+ {
+ this.backingVector.X = r;
+ this.backingVector.Y = g;
+ this.backingVector.Z = b;
+ this.backingVector.W = a;
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The hexadecimal representation of the combined color components arranged
+ /// in rgb, rrggbb, or aarrggbb format to match web syntax.
+ ///
+ public Color(string hex)
+ : this()
+ {
+ // Hexadecimal representations are layed out AARRGGBB to we need to do some reordering.
+ hex = hex.StartsWith("#") ? hex.Substring(1) : hex;
+
+ if (hex.Length != 8 && hex.Length != 6 && hex.Length != 3)
+ {
+ throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex));
+ }
+
+ if (hex.Length == 8)
+ {
+ this.R = Convert.ToByte(hex.Substring(2, 2), 16) / 255f;
+ this.G = Convert.ToByte(hex.Substring(4, 2), 16) / 255f;
+ this.B = Convert.ToByte(hex.Substring(6, 2), 16) / 255f;
+ this.A = Convert.ToByte(hex.Substring(0, 2), 16) / 255f;
+ }
+ else if (hex.Length == 6)
+ {
+ this.R = Convert.ToByte(hex.Substring(0, 2), 16) / 255f;
+ this.G = Convert.ToByte(hex.Substring(2, 2), 16) / 255f;
+ this.B = Convert.ToByte(hex.Substring(4, 2), 16) / 255f;
+ this.A = 1;
+ }
+ else
+ {
+ string r = char.ToString(hex[0]);
+ string g = char.ToString(hex[1]);
+ string b = char.ToString(hex[2]);
+
+ this.B = Convert.ToByte(b + b, 16) / 255f;
+ this.G = Convert.ToByte(g + g, 16) / 255f;
+ this.R = Convert.ToByte(r + r, 16) / 255f;
+ this.A = 1;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The vector.
+ ///
+ public Color(Vector4 vector)
+ {
+ this.backingVector = vector;
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The vector representing the red, green, and blue componenets.
+ ///
+ public Color(Vector3 vector)
+ {
+ this.backingVector = new Vector4(vector.X, vector.Y, vector.Z, 1);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The vector representing the red, green, and blue componenets.
+ ///
+ /// The alpha component.
+ public Color(Vector3 vector, float alpha)
+ {
+ this.backingVector = new Vector4(vector.X, vector.Y, vector.Z, alpha);
+ }
+
+ ///
+ /// Gets or sets the red component of the color.
+ ///
+ public float R
+ {
+ get
+ {
+ return this.backingVector.X;
+ }
+
+ set
+ {
+ this.backingVector.X = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the green component of the color.
+ ///
+ public float G
+ {
+ get
+ {
+ return this.backingVector.Y;
+ }
+
+ set
+ {
+ this.backingVector.Y = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the blue component of the color.
+ ///
+ public float B
+ {
+ get
+ {
+ return this.backingVector.Z;
+ }
+
+ set
+ {
+ this.backingVector.Z = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the alpha component of the color.
+ ///
+ public float A
+ {
+ get
+ {
+ return this.backingVector.W;
+ }
+
+ set
+ {
+ this.backingVector.W = value;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.backingVector.Equals(default(Vector4));
+
+ ///
+ /// Gets this color with the component values clamped from 0 to 1.
+ ///
+ public Color Limited
+ {
+ get
+ {
+ float r = this.R.Clamp(0, 1);
+ float g = this.G.Clamp(0, 1);
+ float b = this.B.Clamp(0, 1);
+ float a = this.A.Clamp(0, 1);
+ return new Color(r, g, b, a);
+ }
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(Bgra32 color)
+ {
+ return new Color(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(Cmyk cmykColor)
+ {
+ float r = (1 - cmykColor.C) * (1 - cmykColor.K);
+ float g = (1 - cmykColor.M) * (1 - cmykColor.K);
+ float b = (1 - cmykColor.Y) * (1 - cmykColor.K);
+ return new Color(r, g, b);
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(YCbCr color)
+ {
+ float y = color.Y;
+ float cb = color.Cb - 128;
+ float cr = color.Cr - 128;
+
+ float r = (float)(y + (1.402 * cr)) / 255f;
+ float g = (float)(y - (0.34414 * cb) - (0.71414 * cr)) / 255f;
+ float b = (float)(y + (1.772 * cb)) / 255f;
+
+ return new Color(r, g, b);
+ }
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ /// The instance of to convert.
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Color(Hsv color)
+ {
+ float s = color.S;
+ float v = color.V;
+
+ if (Math.Abs(s) < Epsilon)
+ {
+ return new Color(v, v, v, 1);
+ }
+
+ float h = (Math.Abs(color.H - 360) < Epsilon) ? 0 : color.H / 60;
+ int i = (int)Math.Truncate(h);
+ float f = h - i;
+
+ float p = v * (1.0f - s);
+ float q = v * (1.0f - (s * f));
+ float t = v * (1.0f - (s * (1.0f - f)));
+
+ float r, g, b;
+ switch (i)
+ {
+ case 0:
+ r = v;
+ g = t;
+ b = p;
+ break;
+
+ case 1:
+ r = q;
+ g = v;
+ b = p;
+ break;
+
+ case 2:
+ r = p;
+ g = v;
+ b = t;
+ break;
+
+ case 3:
+ r = p;
+ g = q;
+ b = v;
+ break;
+
+ case 4:
+ r = t;
+ g = p;
+ b = v;
+ break;
+
+ default:
+ r = v;
+ g = p;
+ b = q;
+ break;
+ }
+
+ return new Color(r, g, b);
+ }
+
+ ///
+ /// Computes the product of multiplying a color by a given factor.
+ ///
+ /// The color.
+ /// The multiplication factor.
+ ///
+ /// The
+ ///
+ public static Color operator *(Color color, float factor)
+ {
+ return new Color(color.backingVector * factor);
+ }
+
+ ///
+ /// Computes the product of multiplying a color by a given factor.
+ ///
+ /// The multiplication factor.
+ /// The color.
+ ///
+ /// The
+ ///
+ public static Color operator *(float factor, Color color)
+ {
+ return new Color(color.backingVector * factor);
+ }
+
+ ///
+ /// Computes the product of multiplying two colors.
+ ///
+ /// The color on the left hand of the operand.
+ /// The color on the right hand of the operand.
+ ///
+ /// The
+ ///
+ public static Color operator *(Color left, Color right)
+ {
+ return new Color(left.backingVector * right.backingVector);
+ }
+
+ ///
+ /// Computes the sum of adding two colors.
+ ///
+ /// The color on the left hand of the operand.
+ /// The color on the right hand of the operand.
+ ///
+ /// The
+ ///
+ public static Color operator +(Color left, Color right)
+ {
+ return new Color(left.R + right.R, left.G + right.G, left.B + right.B, left.A + right.A);
+ }
+
+ ///
+ /// Computes the difference left by subtracting one color from another.
+ ///
+ /// The color on the left hand of the operand.
+ /// The color on the right hand of the operand.
+ ///
+ /// The
+ ///
+ public static Color operator -(Color left, Color right)
+ {
+ return new Color(left.R - right.R, left.G - right.G, left.B - right.B, left.A - right.A);
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(Color left, Color right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(Color left, Color right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ /// Returns a new color whose components are the average of the components of first and second.
+ ///
+ /// The first color.
+ /// The second color.
+ ///
+ /// The
+ ///
+ public static Color Average(Color first, Color second)
+ {
+ return new Color((first.backingVector + second.backingVector) * .5f);
+ }
+
+ ///
+ /// Linearly interpolates from one color to another based on the given amount.
+ ///
+ /// The first color value.
+ /// The second color value.
+ ///
+ /// The weight value. At amount = 0, "from" is returned, at amount = 1, "to" is returned.
+ ///
+ ///
+ /// The
+ ///
+ public static Color Lerp(Color from, Color to, float amount)
+ {
+ amount = amount.Clamp(0f, 1f);
+
+ return (from * (1 - amount)) + (to * amount);
+ }
+
+ ///
+ /// Gets a representation for this .
+ ///
+ /// A representation for this object.
+ public Vector4 ToVector4()
+ {
+ return new Vector4(this.R, this.G, this.B, this.A);
+ }
+
+ ///
+ /// Gets a representation for this .
+ ///
+ /// A representation for this object.
+ public Vector3 ToVector3()
+ {
+ return new Vector3(this.R, this.G, this.B);
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is Color)
+ {
+ Color color = (Color)obj;
+
+ return this.backingVector == color.backingVector;
+ }
+
+ return false;
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return GetHashCode(this);
+ }
+
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "Color [ Empty ]";
+ }
+
+ return $"Color [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##}, A={this.A:#0.##} ]";
+ }
+
+ ///
+ public bool Equals(Color other)
+ {
+ return this.backingVector.Equals(other.backingVector);
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// The instance of to return the hash code for.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ private static int GetHashCode(Color color) => color.backingVector.GetHashCode();
+ }
+}
diff --git a/src/ImageProcessor/Colors/Formats/Bgra32.cs b/src/ImageProcessor/Colors/Formats/Bgra32.cs
new file mode 100644
index 000000000..237888e61
--- /dev/null
+++ b/src/ImageProcessor/Colors/Formats/Bgra32.cs
@@ -0,0 +1,204 @@
+//
+// Copyright (c) James South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessor
+{
+ using System;
+ using System.ComponentModel;
+ using System.Numerics;
+
+ ///
+ /// Represents an BGRA (blue, green, red, alpha) color.
+ ///
+ public struct Bgra32 : IEquatable
+ {
+ ///
+ /// Represents a that has B, G, R, and A values set to zero.
+ ///
+ public static readonly Bgra32 Empty = default(Bgra32);
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private Vector4 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The blue component of this .
+ /// The green component of this .
+ /// The red component of this .
+ public Bgra32(byte b, byte g, byte r)
+ : this(b, g, r, 255)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The blue component of this .
+ /// The green component of this .
+ /// The red component of this .
+ /// The alpha component of this .
+ public Bgra32(byte b, byte g, byte r, byte a)
+ : this()
+ {
+ this.backingVector.X = b.Clamp(0, 255);
+ this.backingVector.Y = g.Clamp(0, 255);
+ this.backingVector.Z = r.Clamp(0, 255);
+ this.backingVector.W = a.Clamp(0, 255);
+ }
+
+ ///
+ /// Gets the blue component of the color
+ ///
+ public byte B => (byte)this.backingVector.X;
+
+ ///
+ /// Gets the green component of the color
+ ///
+ public byte G => (byte)this.backingVector.Y;
+
+ ///
+ /// Gets the red component of the color
+ ///
+ public byte R => (byte)this.backingVector.Z;
+
+ ///
+ /// Gets the alpha component of the color
+ ///
+ public byte A => (byte)this.backingVector.W;
+
+ ///
+ /// Gets the integer representation of the color.
+ ///
+ public int Bgra => (this.R << 16) | (this.G << 8) | (this.B << 0) | (this.A << 24);
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool IsEmpty => this.backingVector.Equals(default(Vector4));
+
+ ///
+ /// Allows the implicit conversion of an instance of to a
+ /// .
+ ///
+ ///
+ /// The instance of to convert.
+ ///
+ ///
+ /// An instance of .
+ ///
+ public static implicit operator Bgra32(Color color)
+ {
+ color = color.Limited;
+ return new Bgra32((255f * color.B).ToByte(), (255f * color.G).ToByte(), (255f * color.R).ToByte(), (255f * color.A).ToByte());
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is equal to the parameter; otherwise, false.
+ ///
+ public static bool operator ==(Bgra32 left, Bgra32 right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the current left is unequal to the parameter; otherwise, false.
+ ///
+ public static bool operator !=(Bgra32 left, Bgra32 right)
+ {
+ return !left.Equals(right);
+ }
+
+ ///
+ /// Indicates whether this instance and a specified object are equal.
+ ///
+ ///
+ /// true if and this instance are the same type and represent the same value; otherwise, false.
+ ///
+ /// Another object to compare to.
+ public override bool Equals(object obj)
+ {
+ if (obj is Bgra32)
+ {
+ Bgra32 color = (Bgra32)obj;
+
+ return this.backingVector == color.backingVector;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ public override int GetHashCode()
+ {
+ return GetHashCode(this);
+ }
+
+ ///
+ /// Returns the fully qualified type name of this instance.
+ ///
+ ///
+ /// A containing a fully qualified type name.
+ ///
+ public override string ToString()
+ {
+ if (this.IsEmpty)
+ {
+ return "Color [ Empty ]";
+ }
+
+ return $"Color [ B={this.B}, G={this.G}, R={this.R}, A={this.A} ]";
+ }
+
+ ///
+ /// Indicates whether the current object is equal to another object of the same type.
+ ///
+ ///
+ /// True if the current object is equal to the parameter; otherwise, false.
+ ///
+ /// An object to compare with this object.
+ public bool Equals(Bgra32 other)
+ {
+ return this.backingVector.Equals(other.backingVector);
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// The instance of to return the hash code for.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ private static int GetHashCode(Bgra32 color) => color.backingVector.GetHashCode();
+ }
+}
diff --git a/src/ImageProcessor/Colors/Cmyk.cs b/src/ImageProcessor/Colors/Formats/Cmyk.cs
similarity index 55%
rename from src/ImageProcessor/Colors/Cmyk.cs
rename to src/ImageProcessor/Colors/Formats/Cmyk.cs
index a11fffdd5..e20fe308c 100644
--- a/src/ImageProcessor/Colors/Cmyk.cs
+++ b/src/ImageProcessor/Colors/Formats/Cmyk.cs
@@ -7,6 +7,7 @@ namespace ImageProcessor
{
using System;
using System.ComponentModel;
+ using System.Numerics;
///
/// Represents an CMYK (cyan, magenta, yellow, keyline) color.
@@ -19,92 +20,95 @@ namespace ImageProcessor
public static readonly Cmyk Empty = default(Cmyk);
///
- /// Gets the cyan color component.
+ /// The epsilon for comparing floating point numbers.
///
- /// A value ranging between 0 and 100.
- public readonly float C;
+ private const float Epsilon = 0.0001f;
///
- /// Gets the magenta color component.
+ /// The backing vector for SIMD support.
///
- /// A value ranging between 0 and 100.
- public readonly float M;
+ private Vector4 backingVector;
///
- /// Gets the yellow color component.
+ /// Initializes a new instance of the struct.
///
- /// A value ranging between 0 and 100.
- public readonly float Y;
+ /// The cyan component.
+ /// The magenta component.
+ /// The yellow component.
+ /// The keyline black component.
+ public Cmyk(float cyan, float magenta, float yellow, float keyline)
+ : this()
+ {
+ this.backingVector.X = Clamp(cyan);
+ this.backingVector.Y = Clamp(magenta);
+ this.backingVector.Z = Clamp(yellow);
+ this.backingVector.W = Clamp(keyline);
+ }
///
- /// Gets the keyline black color component.
+ /// Gets the cyan color component.
+ /// A value ranging between 0 and 1.
///
- /// A value ranging between 0 and 100.
- public readonly float K;
+ public float C => this.backingVector.X;
///
- /// The epsilon for comparing floating point numbers.
+ /// Gets the magenta color component.
+ /// A value ranging between 0 and 1.
///
- private const float Epsilon = 0.0001f;
+ public float M => this.backingVector.Y;
///
- /// Initializes a new instance of the struct.
+ /// Gets the yellow color component.
+ /// A value ranging between 0 and 1.
///
- /// The cyan component.
- /// The magenta component.
- /// The yellow component.
- /// The keyline black component.
- public Cmyk(float cyan, float magenta, float yellow, float keyline)
- {
- this.C = Clamp(cyan);
- this.M = Clamp(magenta);
- this.Y = Clamp(yellow);
- this.K = Clamp(keyline);
- }
+ public float Y => this.backingVector.Z;
+
+ ///
+ /// Gets the keyline black color component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float K => this.backingVector.W;
///
/// Gets a value indicating whether this is empty.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public bool IsEmpty => Math.Abs(this.C) < Epsilon
- && Math.Abs(this.M) < Epsilon
- && Math.Abs(this.Y) < Epsilon
- && Math.Abs(this.K) < Epsilon;
+ public bool IsEmpty => this.backingVector.Equals(default(Vector4));
///
- /// Allows the implicit conversion of an instance of to a
+ /// Allows the implicit conversion of an instance of to a
/// .
///
///
- /// The instance of to convert.
+ /// The instance of to convert.
///
///
/// An instance of .
///
- public static implicit operator Cmyk(Bgra color)
+ public static implicit operator Cmyk(Color color)
{
- float c = (255f - color.R) / 255;
- float m = (255f - color.G) / 255;
- float y = (255f - color.B) / 255;
+ color = color.Limited;
+
+ float c = 1f - color.R;
+ float m = 1f - color.G;
+ float y = 1f - color.B;
float k = Math.Min(c, Math.Min(m, y));
- if (Math.Abs(k - 1.0) <= Epsilon)
+ if (Math.Abs(k - 1.0f) <= Epsilon)
{
- return new Cmyk(0, 0, 0, 100);
+ return new Cmyk(0, 0, 0, 1);
}
- c = ((c - k) / (1 - k)) * 100;
- m = ((m - k) / (1 - k)) * 100;
- y = ((y - k) / (1 - k)) * 100;
+ c = (c - k) / (1 - k);
+ m = (m - k) / (1 - k);
+ y = (y - k) / (1 - k);
- return new Cmyk(c, m, y, k * 100);
+ return new Cmyk(c, m, y, k);
}
///
- /// Compares two objects. The result specifies whether the values
- /// of the , , , and
- /// properties of the two objects are equal.
+ /// Compares two objects for equality.
///
///
/// The on the left side of the operand.
@@ -121,9 +125,7 @@ namespace ImageProcessor
}
///
- /// Compares two objects. The result specifies whether the values
- /// of the , , , and
- /// properties of the two objects are unequal.
+ /// Compares two objects for inequality
///
///
/// The on the left side of the operand.
@@ -139,52 +141,26 @@ namespace ImageProcessor
return !left.Equals(right);
}
- ///
- /// Indicates whether this instance and a specified object are equal.
- ///
- ///
- /// true if and this instance are the same type and represent the same value; otherwise, false.
- ///
- /// Another object to compare to.
+ ///
public override bool Equals(object obj)
{
if (obj is Cmyk)
{
Cmyk color = (Cmyk)obj;
- return Math.Abs(this.C - color.C) < Epsilon
- && Math.Abs(this.M - color.M) < Epsilon
- && Math.Abs(this.Y - color.Y) < Epsilon
- && Math.Abs(this.K - color.K) < Epsilon;
+ return this.backingVector == color.backingVector;
}
return false;
}
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
+ ///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = this.C.GetHashCode();
- hashCode = (hashCode * 397) ^ this.M.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Y.GetHashCode();
- hashCode = (hashCode * 397) ^ this.K.GetHashCode();
- return hashCode;
- }
+ return GetHashCode(this);
}
- ///
- /// Returns the fully qualified type name of this instance.
- ///
- ///
- /// A containing a fully qualified type name.
- ///
+ ///
public override string ToString()
{
if (this.IsEmpty)
@@ -195,19 +171,10 @@ namespace ImageProcessor
return $"Cmyk [ C={this.C:#0.##}, M={this.M:#0.##}, Y={this.Y:#0.##}, K={this.K:#0.##}]";
}
- ///
- /// Indicates whether the current object is equal to another object of the same type.
- ///
- ///
- /// True if the current object is equal to the parameter; otherwise, false.
- ///
- /// An object to compare with this object.
+ ///
public bool Equals(Cmyk other)
{
- return Math.Abs(this.C - other.C) < Epsilon
- && Math.Abs(this.M - other.M) < Epsilon
- && Math.Abs(this.Y - other.Y) < Epsilon
- && Math.Abs(this.K - other.Y) < Epsilon;
+ return this.backingVector.Equals(other.backingVector);
}
///
@@ -221,7 +188,18 @@ namespace ImageProcessor
///
private static float Clamp(float value)
{
- return value.Clamp(0, 100);
+ return value.Clamp(0, 1);
}
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ ///
+ /// The instance of to return the hash code for.
+ ///
+ ///
+ /// A 32-bit signed integer that is the hash code for this instance.
+ ///
+ private static int GetHashCode(Cmyk color) => color.backingVector.GetHashCode();
}
}
diff --git a/src/ImageProcessor/Colors/Hsv.cs b/src/ImageProcessor/Colors/Formats/Hsv.cs
similarity index 60%
rename from src/ImageProcessor/Colors/Hsv.cs
rename to src/ImageProcessor/Colors/Formats/Hsv.cs
index 2e184b7e1..a954fcf54 100644
--- a/src/ImageProcessor/Colors/Hsv.cs
+++ b/src/ImageProcessor/Colors/Formats/Hsv.cs
@@ -7,6 +7,7 @@ namespace ImageProcessor
{
using System;
using System.ComponentModel;
+ using System.Numerics;
///
/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
@@ -19,27 +20,14 @@ namespace ImageProcessor
public static readonly Hsv Empty = default(Hsv);
///
- /// Gets the H hue component.
- /// A value ranging between 0 and 360.
- ///
- public readonly float H;
-
- ///
- /// Gets the S saturation component.
- /// A value ranging between 0 and 100.
- ///
- public readonly float S;
-
- ///
- /// Gets the V value (brightness) component.
- /// A value ranging between 0 and 100.
+ /// The epsilon for comparing floating point numbers.
///
- public readonly float V;
+ private const float Epsilon = 0.0001f;
///
- /// The epsilon for comparing floating point numbers.
+ /// The backing vector for SIMD support.
///
- private const float Epsilon = 0.0001f;
+ private Vector3 backingVector;
///
/// Initializes a new instance of the struct.
@@ -49,34 +37,49 @@ namespace ImageProcessor
/// The v value (brightness) component.
public Hsv(float h, float s, float v)
{
- this.H = h.Clamp(0, 360);
- this.S = s.Clamp(0, 100);
- this.V = v.Clamp(0, 100);
+ this.backingVector.X = h.Clamp(0, 360);
+ this.backingVector.Y = s.Clamp(0, 1);
+ this.backingVector.Z = v.Clamp(0, 1);
}
+ ///
+ /// Gets the hue component.
+ /// A value ranging between 0 and 360.
+ ///
+ public float H => this.backingVector.X;
+
+ ///
+ /// Gets the saturation component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float S => this.backingVector.Y;
+
+ ///
+ /// Gets the value (brightness) component.
+ /// A value ranging between 0 and 1.
+ ///
+ public float V => this.backingVector.Z;
+
///
/// Gets a value indicating whether this is empty.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public bool IsEmpty => Math.Abs(this.H) < Epsilon
- && Math.Abs(this.S) < Epsilon
- && Math.Abs(this.V) < Epsilon;
+ public bool IsEmpty => this.backingVector.Equals(default(Vector3));
///
- /// Allows the implicit conversion of an instance of to a
+ /// Allows the implicit conversion of an instance of to a
/// .
///
- ///
- /// The instance of to convert.
- ///
+ /// The instance of to convert.
///
/// An instance of .
///
- public static implicit operator Hsv(Bgra color)
+ public static implicit operator Hsv(Color color)
{
- float r = color.R / 255f;
- float g = color.G / 255f;
- float b = color.B / 255f;
+ color = color.Limited;
+ float r = color.R;
+ float g = color.G;
+ float b = color.B;
float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b));
@@ -87,7 +90,7 @@ namespace ImageProcessor
if (Math.Abs(chroma) < Epsilon)
{
- return new Hsv(0, s * 100, v * 100);
+ return new Hsv(0, s, v);
}
if (Math.Abs(chroma) < Epsilon)
@@ -115,13 +118,11 @@ namespace ImageProcessor
s = chroma / v;
- return new Hsv(h, s * 100, v * 100);
+ return new Hsv(h, s, v);
}
///
- /// Compares two objects. The result specifies whether the values
- /// of the , , and
- /// properties of the two objects are equal.
+ /// Compares two objects for equality.
///
///
/// The on the left side of the operand.
@@ -138,9 +139,7 @@ namespace ImageProcessor
}
///
- /// Compares two objects. The result specifies whether the values
- /// of the , , and
- /// properties of the two objects are unequal.
+ /// Compares two objects for inequality.
///
///
/// The on the left side of the operand.
@@ -156,50 +155,26 @@ namespace ImageProcessor
return !left.Equals(right);
}
- ///
- /// Indicates whether this instance and a specified object are equal.
- ///
- ///
- /// true if and this instance are the same type and represent the same value; otherwise, false.
- ///
- /// Another object to compare to.
+ ///
public override bool Equals(object obj)
{
if (obj is Hsv)
{
Hsv color = (Hsv)obj;
- return Math.Abs(this.H - color.H) < Epsilon
- && Math.Abs(this.S - color.S) < Epsilon
- && Math.Abs(this.V - color.V) < Epsilon;
+ return this.backingVector == color.backingVector;
}
return false;
}
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
+ ///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = this.H.GetHashCode();
- hashCode = (hashCode * 397) ^ this.S.GetHashCode();
- hashCode = (hashCode * 397) ^ this.V.GetHashCode();
- return hashCode;
- }
+ return GetHashCode(this);
}
- ///
- /// Returns the fully qualified type name of this instance.
- ///
- ///
- /// A containing a fully qualified type name.
- ///
+ ///
public override string ToString()
{
if (this.IsEmpty)
@@ -210,18 +185,21 @@ namespace ImageProcessor
return $"Hsv [ H={this.H:#0.##}, S={this.S:#0.##}, V={this.V:#0.##} ]";
}
+ ///
+ public bool Equals(Hsv other)
+ {
+ return this.backingVector.Equals(other.backingVector);
+ }
+
///
- /// Indicates whether the current object is equal to another object of the same type.
+ /// Returns the hash code for this instance.
///
+ ///
+ /// The instance of to return the hash code for.
+ ///
///
- /// True if the current object is equal to the parameter; otherwise, false.
+ /// A 32-bit signed integer that is the hash code for this instance.
///
- /// An object to compare with this object.
- public bool Equals(Hsv other)
- {
- return Math.Abs(this.H - other.H) < Epsilon
- && Math.Abs(this.S - other.S) < Epsilon
- && Math.Abs(this.V - other.V) < Epsilon;
- }
+ private static int GetHashCode(Hsv color) => color.backingVector.GetHashCode();
}
}
diff --git a/src/ImageProcessor/Colors/YCbCr.cs b/src/ImageProcessor/Colors/Formats/YCbCr.cs
similarity index 59%
rename from src/ImageProcessor/Colors/YCbCr.cs
rename to src/ImageProcessor/Colors/Formats/YCbCr.cs
index c99fe55fb..c23d0ae0b 100644
--- a/src/ImageProcessor/Colors/YCbCr.cs
+++ b/src/ImageProcessor/Colors/Formats/YCbCr.cs
@@ -7,10 +7,11 @@ namespace ImageProcessor
{
using System;
using System.ComponentModel;
+ using System.Numerics;
///
/// Represents an YCbCr (luminance, chroma, chroma) color conforming to the
- /// ITU-R BT.601 standard used in digital imaging systems.
+ /// Full range standard used in digital imaging systems.
///
///
public struct YCbCr : IEquatable
@@ -21,64 +22,64 @@ namespace ImageProcessor
public static readonly YCbCr Empty = default(YCbCr);
///
- /// Gets the Y luminance component.
- /// A value ranging between 0 and 255.
+ /// The backing vector for SIMD support.
///
- public readonly float Y;
+ private Vector3 backingVector;
///
- /// Gets the Cb chroma component.
- /// A value ranging between 0 and 255.
+ /// Initializes a new instance of the struct.
///
- public readonly float Cb;
+ /// The y luminance component.
+ /// The cb chroma component.
+ /// The cr chroma component.
+ public YCbCr(float y, float cb, float cr)
+ : this()
+ {
+ this.backingVector.X = y.Clamp(0, 255);
+ this.backingVector.Y = cb.Clamp(0, 255);
+ this.backingVector.Z = cr.Clamp(0, 255);
+ }
///
- /// Gets the Cr chroma component.
+ /// Gets the Y luminance component.
/// A value ranging between 0 and 255.
///
- public readonly float Cr;
+ public float Y => this.backingVector.X;
///
- /// The epsilon for comparing floating point numbers.
+ /// Gets the Cb chroma component.
+ /// A value ranging between 0 and 255.
///
- private const float Epsilon = 0.0001f;
+ public float Cb => this.backingVector.Y;
///
- /// Initializes a new instance of the struct.
+ /// Gets the Cr chroma component.
+ /// A value ranging between 0 and 255.
///
- /// The y luminance component.
- /// The cb chroma component.
- /// The cr chroma component.
- public YCbCr(float y, float cb, float cr)
- {
- this.Y = y.ToByte();
- this.Cb = cb.ToByte();
- this.Cr = cr.ToByte();
- }
+ public float Cr => this.backingVector.Z;
///
/// Gets a value indicating whether this is empty.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public bool IsEmpty => Math.Abs(this.Y) < Epsilon
- && Math.Abs(this.Cb) < Epsilon
- && Math.Abs(this.Cr) < Epsilon;
+ public bool IsEmpty => this.backingVector.Equals(default(Vector3));
///
- /// Allows the implicit conversion of an instance of to a
+ /// Allows the implicit conversion of an instance of to a
/// .
///
///
- /// The instance of to convert.
+ /// The instance of to convert.
///
///
/// An instance of .
///
- public static implicit operator YCbCr(Bgra color)
+ public static implicit operator YCbCr(Color color)
{
- byte b = color.B;
- byte g = color.G;
- byte r = color.R;
+ color = color.Limited;
+ float r = color.R * 255f;
+ float g = color.G * 255f;
+ float b = color.B * 255f;
float y = (float)((0.299 * r) + (0.587 * g) + (0.114 * b));
float cb = 128 + (float)((-0.168736 * r) - (0.331264 * g) + (0.5 * b));
@@ -88,9 +89,7 @@ namespace ImageProcessor
}
///
- /// Compares two objects. The result specifies whether the values
- /// of the , , and
- /// properties of the two objects are equal.
+ /// Compares two objects for equality.
///
///
/// The on the left side of the operand.
@@ -107,9 +106,7 @@ namespace ImageProcessor
}
///
- /// Compares two objects. The result specifies whether the values
- /// of the , , and
- /// properties of the two objects are unequal.
+ /// Compares two objects for inequality.
///
///
/// The on the left side of the operand.
@@ -125,50 +122,26 @@ namespace ImageProcessor
return !left.Equals(right);
}
- ///
- /// Indicates whether this instance and a specified object are equal.
- ///
- ///
- /// true if and this instance are the same type and represent the same value; otherwise, false.
- ///
- /// Another object to compare to.
+ ///
public override bool Equals(object obj)
{
if (obj is YCbCr)
{
YCbCr color = (YCbCr)obj;
- return Math.Abs(this.Y - color.Y) < Epsilon
- && Math.Abs(this.Cb - color.Cb) < Epsilon
- && Math.Abs(this.Cr - color.Cr) < Epsilon;
+ return this.backingVector == color.backingVector;
}
return false;
}
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
+ ///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = this.Y.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Cb.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Cr.GetHashCode();
- return hashCode;
- }
+ return GetHashCode(this);
}
- ///
- /// Returns the fully qualified type name of this instance.
- ///
- ///
- /// A containing a fully qualified type name.
- ///
+ ///
public override string ToString()
{
if (this.IsEmpty)
@@ -179,18 +152,21 @@ namespace ImageProcessor
return $"YCbCr [ Y={this.Y:#0.##}, Cb={this.Cb:#0.##}, Cr={this.Cr:#0.##} ]";
}
+ ///
+ public bool Equals(YCbCr other)
+ {
+ return this.backingVector.Equals(other.backingVector);
+ }
+
///
- /// Indicates whether the current object is equal to another object of the same type.
+ /// Returns the hash code for this instance.
///
+ ///
+ /// The instance of to return the hash code for.
+ ///
///
- /// True if the current object is equal to the parameter; otherwise, false.
+ /// A 32-bit signed integer that is the hash code for this instance.
///
- /// An object to compare with this object.
- public bool Equals(YCbCr other)
- {
- return Math.Abs(this.Y - other.Y) < Epsilon
- && Math.Abs(this.Cb - other.Cb) < Epsilon
- && Math.Abs(this.Cr - other.Cr) < Epsilon;
- }
+ private static int GetHashCode(YCbCr color) => color.backingVector.GetHashCode();
}
}
diff --git a/src/ImageProcessor/Common/Extensions/EnumerableExtensions.cs b/src/ImageProcessor/Common/Extensions/EnumerableExtensions.cs
new file mode 100644
index 000000000..c492bcf8e
--- /dev/null
+++ b/src/ImageProcessor/Common/Extensions/EnumerableExtensions.cs
@@ -0,0 +1,88 @@
+//
+// Copyright (c) James South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessor
+{
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Encapsulates a series of time saving extension methods to the interface.
+ ///
+ public static class EnumerableExtensions
+ {
+ ///
+ /// Generates a sequence of integral numbers within a specified range.
+ ///
+ ///
+ /// The start index, inclusive.
+ ///
+ ///
+ /// The end index, exclusive.
+ ///
+ ///
+ /// The incremental step.
+ ///
+ ///
+ /// The that contains a range of sequential integral numbers.
+ ///
+ public static IEnumerable SteppedRange(int fromInclusive, int toExclusive, int step)
+ {
+ // Borrowed from Enumerable.Range
+ long num = (fromInclusive + toExclusive) - 1L;
+ if ((toExclusive < 0) || (num > 0x7fffffffL))
+ {
+ throw new ArgumentOutOfRangeException(nameof(toExclusive));
+ }
+
+ return RangeIterator(fromInclusive, i => i < toExclusive, step);
+ }
+
+ ///
+ /// Generates a sequence of integral numbers within a specified range.
+ ///
+ ///
+ /// The start index, inclusive.
+ ///
+ ///
+ /// A method that has one parameter and returns a calculating the end index
+ ///
+ ///
+ /// The incremental step.
+ ///
+ ///
+ /// The that contains a range of sequential integral numbers.
+ ///
+ public static IEnumerable SteppedRange(int fromInclusive, Func toDelegate, int step)
+ {
+ return RangeIterator(fromInclusive, toDelegate, step);
+ }
+
+ ///
+ /// Generates a sequence of integral numbers within a specified range.
+ ///
+ ///
+ /// The start index, inclusive.
+ ///
+ ///
+ /// A method that has one parameter and returns a calculating the end index
+ ///
+ ///
+ /// The incremental step.
+ ///
+ ///
+ /// The that contains a range of sequential integral numbers.
+ ///
+ private static IEnumerable RangeIterator(int fromInclusive, Func toDelegate, int step)
+ {
+ int i = fromInclusive;
+ while (toDelegate(i))
+ {
+ yield return i;
+ i += step;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Common/Helpers/ImageMaths.cs b/src/ImageProcessor/Common/Helpers/ImageMaths.cs
index 71f7d9670..b2bf0dede 100644
--- a/src/ImageProcessor/Common/Helpers/ImageMaths.cs
+++ b/src/ImageProcessor/Common/Helpers/ImageMaths.cs
@@ -12,6 +12,12 @@ namespace ImageProcessor
///
internal static class ImageMaths
{
+ ///
+ /// Represents PI, the ratio of a circle's circumference to its diameter.
+ ///
+ // ReSharper disable once InconsistentNaming
+ public const float PI = 3.1415926535897931f;
+
///
/// Returns the result of a B-C filter against the given value.
///
@@ -20,11 +26,11 @@ namespace ImageProcessor
/// The B-Spline curve variable.
/// The Cardinal curve variable.
///
- /// The .
+ /// The .
///
- public static double GetBcValue(double x, double b, double c)
+ public static float GetBcValue(float x, float b, float c)
{
- double temp;
+ float temp;
if (x < 0)
{
@@ -54,19 +60,19 @@ namespace ImageProcessor
/// The value to calculate the result for.
///
///
- /// The .
+ /// The .
///
- public static double SinC(double x)
+ public static float SinC(float x)
{
- const double Epsilon = .0001;
+ const float Epsilon = .00001f;
if (Math.Abs(x) > Epsilon)
{
- x *= Math.PI;
- return Clean(Math.Sin(x) / x);
+ x *= PI;
+ return Clean((float)Math.Sin(x) / x);
}
- return 1.0;
+ return 1.0f;
}
///
@@ -74,15 +80,15 @@ namespace ImageProcessor
///
/// The value to clean.
///
- /// The
+ /// The
/// .
- private static double Clean(double x)
+ private static float Clean(float x)
{
- const double Epsilon = .0001;
+ const float Epsilon = .00001f;
if (Math.Abs(x) < Epsilon)
{
- return 0.0;
+ return 0f;
}
return x;
diff --git a/src/ImageProcessor/Common/Helpers/PixelOperations.cs b/src/ImageProcessor/Common/Helpers/PixelOperations.cs
index a876b45ef..a116c0e35 100644
--- a/src/ImageProcessor/Common/Helpers/PixelOperations.cs
+++ b/src/ImageProcessor/Common/Helpers/PixelOperations.cs
@@ -12,102 +12,42 @@ namespace ImageProcessor
///
public static class PixelOperations
{
- ///
- /// The array of bytes representing each possible value of color component
- /// converted from sRGB to the linear color space.
- ///
- private static readonly Lazy LinearBytes = new Lazy(GetLinearBytes);
-
- ///
- /// The array of bytes representing each possible value of color component
- /// converted from linear to the sRGB color space.
- ///
- private static readonly Lazy SrgbBytes = new Lazy(GetSrgbBytes);
-
- ///
- /// The array of bytes representing each possible value of color component
- /// converted from gamma to the linear color space.
- ///
- private static readonly Lazy LinearGammaBytes = new Lazy(GetLinearGammaBytes);
-
- ///
- /// The array of bytes representing each possible value of color component
- /// converted from linear to the gamma color space.
- ///
- private static readonly Lazy GammaLinearBytes = new Lazy(GetGammaLinearBytes);
-
///
/// Converts an pixel from an sRGB color-space to the equivalent linear color-space.
///
///
- /// The to convert.
+ /// The to convert.
///
///
- /// The .
+ /// The .
///
- public static Bgra ToLinear(Bgra composite)
+ public static Color ToLinear(Color composite)
{
- // Create only once and lazily.
- // byte[] ramp = LinearGammaBytes.Value;
- byte[] ramp = LinearBytes.Value;
+ // TODO: Figure out a way to either cache these values quickly or perform the calcuations together.
+ composite.R = SrgbToLinear(composite.R);
+ composite.G = SrgbToLinear(composite.G);
+ composite.B = SrgbToLinear(composite.B);
- return new Bgra(ramp[composite.B], ramp[composite.G], ramp[composite.R], composite.A);
+ return composite;
}
///
/// Converts a pixel from a linear color-space to the equivalent sRGB color-space.
///
///
- /// The to convert.
+ /// The to convert.
///
///
- /// The .
- ///
- public static Bgra ToSrgb(Bgra linear)
- {
- // Create only once and lazily.
- // byte[] ramp = GammaLinearBytes.Value;
- byte[] ramp = SrgbBytes.Value;
-
- return new Bgra(ramp[linear.B], ramp[linear.G], ramp[linear.R], linear.A);
- }
-
- ///
- /// Gets an array of bytes representing each possible value of color component
- /// converted from sRGB to the linear color space.
- ///
- ///
- /// The .
- ///
- private static byte[] GetLinearBytes()
- {
- byte[] ramp = new byte[256];
- for (int x = 0; x < 256; ++x)
- {
- byte val = (255f * SrgbToLinear(x / 255f)).ToByte();
- ramp[x] = val;
- }
-
- return ramp;
- }
-
- ///
- /// Gets an array of bytes representing each possible value of color component
- /// converted from linear to the sRGB color space.
- ///
- ///
- /// The .
+ /// The .
///
- private static byte[] GetSrgbBytes()
+ public static Color ToSrgb(Color linear)
{
- byte[] ramp = new byte[256];
- for (int x = 0; x < 256; ++x)
- {
- byte val = (255f * LinearToSrgb(x / 255f)).ToByte();
- ramp[x] = val;
- }
+ // TODO: Figure out a way to either cache these values quickly or perform the calcuations together.
+ linear.R = LinearToSrgb(linear.R);
+ linear.G = LinearToSrgb(linear.G);
+ linear.B = LinearToSrgb(linear.B);
- return ramp;
+ return linear;
}
///
@@ -121,14 +61,12 @@ namespace ImageProcessor
///
private static float SrgbToLinear(float signal)
{
- float a = 0.055f;
-
- if (signal <= 0.04045)
+ if (signal <= 0.04045f)
{
return signal / 12.92f;
}
- return (float)Math.Pow((signal + a) / (1 + a), 2.4);
+ return (float)Math.Pow((signal + 0.055f) / 1.055f, 2.4f);
}
///
@@ -142,52 +80,12 @@ namespace ImageProcessor
///
private static float LinearToSrgb(float signal)
{
- float a = 0.055f;
-
- if (signal <= 0.0031308)
+ if (signal <= 0.0031308f)
{
return signal * 12.92f;
}
- return ((float)((1 + a) * Math.Pow(signal, 1 / 2.4f))) - a;
- }
-
- ///
- /// Gets an array of bytes representing each possible value of color component
- /// converted from gamma to the linear color space.
- ///
- ///
- /// The .
- ///
- private static byte[] GetLinearGammaBytes()
- {
- byte[] ramp = new byte[256];
- for (int x = 0; x < 256; ++x)
- {
- byte val = (255f * Math.Pow(x / 255f, 2.2)).ToByte();
- ramp[x] = val;
- }
-
- return ramp;
- }
-
- ///
- /// Gets an array of bytes representing each possible value of color component
- /// converted from linear to the gamma color space.
- ///
- ///
- /// The .
- ///
- private static byte[] GetGammaLinearBytes()
- {
- byte[] ramp = new byte[256];
- for (int x = 0; x < 256; ++x)
- {
- byte val = (255f * Math.Pow(x / 255f, 1 / 2.2)).ToByte();
- ramp[x] = val;
- }
-
- return ramp;
+ return (1.055f * (float)Math.Pow(signal, 0.41666666f)) - 0.055f;
}
}
}
\ No newline at end of file
diff --git a/src/ImageProcessor/Filters/Alpha.cs b/src/ImageProcessor/Filters/Alpha.cs
index 93c335550..4f2e01f70 100644
--- a/src/ImageProcessor/Filters/Alpha.cs
+++ b/src/ImageProcessor/Filters/Alpha.cs
@@ -6,6 +6,7 @@
namespace ImageProcessor.Filters
{
using System;
+ using System.Threading.Tasks;
///
/// An to change the Alpha of an .
@@ -33,24 +34,27 @@ namespace ImageProcessor.Filters
///
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
- double alpha = this.Value / 100.0;
+ float alpha = this.Value / 100f;
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
- for (int y = startY; y < endY; y++)
- {
- if (y >= sourceY && y < sourceBottom)
- {
- for (int x = startX; x < endX; x++)
+ Parallel.For(
+ startY,
+ endY,
+ y =>
{
- Bgra color = source[x, y];
- double a = color.A * alpha;
- target[x, y] = new Bgra(color.B, color.G, color.R, a.ToByte());
- }
- }
- }
+ if (y >= sourceY && y < sourceBottom)
+ {
+ for (int x = startX; x < endX; x++)
+ {
+ Color color = source[x, y];
+ color.A = color.A * alpha;
+ target[x, y] = color;
+ }
+ }
+ });
}
}
}
diff --git a/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs b/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
index 3f7eb7591..17a49b8fb 100644
--- a/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
+++ b/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
@@ -5,6 +5,8 @@
namespace ImageProcessor.Filters
{
+ using System.Threading.Tasks;
+
///
/// The color matrix filter.
///
@@ -34,68 +36,56 @@ namespace ImageProcessor.Filters
///
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
+ bool gamma = this.GammaAdjust;
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
ColorMatrix matrix = this.Value;
- Bgra previousColor = source[0, 0];
- Bgra pixelValue = this.ApplyMatrix(previousColor, matrix);
- for (int y = startY; y < endY; y++)
- {
- if (y >= sourceY && y < sourceBottom)
- {
- for (int x = startX; x < endX; x++)
+ Parallel.For(
+ startY,
+ endY,
+ y =>
{
- Bgra sourceColor = source[x, y];
-
- // Check if this is the same as the last pixel. If so use that value
- // rather than calculating it again. This is an inexpensive optimization.
- if (sourceColor != previousColor)
+ if (y >= sourceY && y < sourceBottom)
{
- // Perform the operation on the pixel.
- pixelValue = this.ApplyMatrix(sourceColor, matrix);
-
- // And setup the previous pointer
- previousColor = sourceColor;
+ for (int x = startX; x < endX; x++)
+ {
+ target[x, y] = ApplyMatrix(source[x, y], matrix, gamma);
+ }
}
-
- target[x, y] = pixelValue;
- }
- }
- }
+ });
}
///
/// Applies the color matrix against the given color.
///
- /// The source color.
+ /// The source color.
/// The matrix.
+ /// Whether to perform gamma adjustments.
///
- /// The .
+ /// The .
///
- private Bgra ApplyMatrix(Bgra sourceColor, ColorMatrix matrix)
+ private static Color ApplyMatrix(Color color, ColorMatrix matrix, bool gamma)
{
- bool gamma = this.GammaAdjust;
-
if (gamma)
{
- sourceColor = PixelOperations.ToLinear(sourceColor);
+ color = PixelOperations.ToLinear(color);
}
- int sr = sourceColor.R;
- int sg = sourceColor.G;
- int sb = sourceColor.B;
- int sa = sourceColor.A;
+ float sr = color.R;
+ float sg = color.G;
+ float sb = color.B;
+ float sa = color.A;
// TODO: Investigate RGBAW
- byte r = ((sr * matrix.Matrix00) + (sg * matrix.Matrix10) + (sb * matrix.Matrix20) + (sa * matrix.Matrix30) + (255f * matrix.Matrix40)).ToByte();
- byte g = ((sr * matrix.Matrix01) + (sg * matrix.Matrix11) + (sb * matrix.Matrix21) + (sa * matrix.Matrix31) + (255f * matrix.Matrix41)).ToByte();
- byte b = ((sr * matrix.Matrix02) + (sg * matrix.Matrix12) + (sb * matrix.Matrix22) + (sa * matrix.Matrix32) + (255f * matrix.Matrix42)).ToByte();
- byte a = ((sr * matrix.Matrix03) + (sg * matrix.Matrix13) + (sb * matrix.Matrix23) + (sa * matrix.Matrix33) + (255f * matrix.Matrix43)).ToByte();
+ color.R = (sr * matrix.Matrix00) + (sg * matrix.Matrix10) + (sb * matrix.Matrix20) + (sa * matrix.Matrix30) + matrix.Matrix40;
+ color.G = (sr * matrix.Matrix01) + (sg * matrix.Matrix11) + (sb * matrix.Matrix21) + (sa * matrix.Matrix31) + matrix.Matrix41;
+ color.B = (sr * matrix.Matrix02) + (sg * matrix.Matrix12) + (sb * matrix.Matrix22) + (sa * matrix.Matrix32) + matrix.Matrix42;
+ color.A = (sr * matrix.Matrix03) + (sg * matrix.Matrix13) + (sb * matrix.Matrix23) + (sa * matrix.Matrix33) + matrix.Matrix43;
- return gamma ? PixelOperations.ToSrgb(new Bgra(b, g, r, a)) : new Bgra(b, g, r, a);
+ return gamma ? PixelOperations.ToSrgb(color) : color;
}
}
}
diff --git a/src/ImageProcessor/Filters/ColorMatrix/Invert.cs b/src/ImageProcessor/Filters/ColorMatrix/Invert.cs
deleted file mode 100644
index 24c8f52a0..000000000
--- a/src/ImageProcessor/Filters/ColorMatrix/Invert.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// Copyright © James South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageProcessor.Filters
-{
- ///
- /// Inverts the colors of the image.
- ///
- public class Invert : ColorMatrixFilter
- {
- ///
- /// The inversion matrix.
- /// TODO: With gamma adjustment enabled this leaves the image too bright.
- ///
- private static readonly ColorMatrix Matrix = new ColorMatrix(
- new[]
- {
- new float[] { -1, 0, 0, 0, 0 },
- new float[] { 0, -1, 0, 0, 0 },
- new float[] { 0, 0, -1, 0, 0 },
- new float[] { 0, 0, 0, 1, 0 },
- new float[] { 1, 1, 1, 0, 1 }
- });
-
- ///
- /// Initializes a new instance of the class.
- ///
- public Invert()
- : base(Matrix, false)
- {
- }
- }
-}
diff --git a/src/ImageProcessor/Filters/Contrast.cs b/src/ImageProcessor/Filters/Contrast.cs
index 556b3828f..0986dc3d0 100644
--- a/src/ImageProcessor/Filters/Contrast.cs
+++ b/src/ImageProcessor/Filters/Contrast.cs
@@ -6,6 +6,7 @@
namespace ImageProcessor.Filters
{
using System;
+ using System.Threading.Tasks;
///
/// An to change the contrast of an .
@@ -33,48 +34,52 @@ namespace ImageProcessor.Filters
///
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
- double contrast = (100.0 + this.Value) / 100.0;
+ float contrast = (100f + this.Value) / 100f;
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
- for (int y = startY; y < endY; y++)
- {
- if (y >= sourceY && y < sourceBottom)
- {
- for (int x = startX; x < endX; x++)
+ Parallel.For(
+ startY,
+ endY,
+ y =>
{
- Bgra sourceColor = source[x, y];
- sourceColor = PixelOperations.ToLinear(sourceColor);
+ if (y >= sourceY && y < sourceBottom)
+ {
+ for (int x = startX; x < endX; x++)
+ {
+ target[x, y] = AdjustContrast(source[x, y], contrast);
+ }
+ }
+ });
+ }
+
+ ///
+ /// Returns a with the contrast adjusted.
+ ///
+ /// The source color.
+ /// The contrast adjustment factor.
+ ///
+ /// The .
+ ///
+ private static Color AdjustContrast(Color color, float contrast)
+ {
+ color = PixelOperations.ToLinear(color);
- double r = sourceColor.R / 255.0;
- r -= 0.5;
- r *= contrast;
- r += 0.5;
- r *= 255;
- r = r.ToByte();
+ color.R -= 0.5f;
+ color.R *= contrast;
+ color.R += 0.5f;
- double g = sourceColor.G / 255.0;
- g -= 0.5;
- g *= contrast;
- g += 0.5;
- g *= 255;
- g = g.ToByte();
+ color.G -= 0.5f;
+ color.G *= contrast;
+ color.G += 0.5f;
- double b = sourceColor.B / 255.0;
- b -= 0.5;
- b *= contrast;
- b += 0.5;
- b *= 255;
- b = b.ToByte();
+ color.B -= 0.5f;
+ color.B *= contrast;
+ color.B += 0.5f;
- Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), sourceColor.A);
- destinationColor = PixelOperations.ToSrgb(destinationColor);
- target[x, y] = destinationColor;
- }
- }
- }
+ return PixelOperations.ToSrgb(color);
}
}
}
diff --git a/src/ImageProcessor/Filters/Invert.cs b/src/ImageProcessor/Filters/Invert.cs
index d24979a2d..d2fa9445a 100644
--- a/src/ImageProcessor/Filters/Invert.cs
+++ b/src/ImageProcessor/Filters/Invert.cs
@@ -5,6 +5,8 @@
namespace ImageProcessor.Filters
{
+ using System.Threading.Tasks;
+
///
/// An to invert the colors of an .
///
@@ -18,19 +20,24 @@ namespace ImageProcessor.Filters
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
- for (int y = startY; y < endY; y++)
- {
- if (y >= sourceY && y < sourceBottom)
- {
- for (int x = startX; x < endX; x++)
+ Parallel.For(
+ startY,
+ endY,
+ y =>
{
- // TODO: This doesn't work for gamma test images.
- Bgra color = source[x, y];
- Bgra targetColor = new Bgra((255 - color.B).ToByte(), (255 - color.G).ToByte(), (255 - color.R).ToByte(), color.A);
- target[x, y] = targetColor;
- }
- }
- }
+ if (y >= sourceY && y < sourceBottom)
+ {
+ for (int x = startX; x < endX; x++)
+ {
+ // TODO: This doesn't work for gamma test images.
+ Color color = source[x, y];
+ color.R = 1 - color.R;
+ color.G = 1 - color.G;
+ color.B = 1 - color.B;
+ target[x, y] = color;
+ }
+ }
+ });
}
}
}
diff --git a/src/ImageProcessor/Formats/Bmp/BmpDecoder.cs b/src/ImageProcessor/Formats/Bmp/BmpDecoder.cs
index cc1b6b464..2e47b607a 100644
--- a/src/ImageProcessor/Formats/Bmp/BmpDecoder.cs
+++ b/src/ImageProcessor/Formats/Bmp/BmpDecoder.cs
@@ -67,9 +67,8 @@ namespace ImageProcessor.Formats
bool isBmp = false;
if (header.Length >= 2)
{
- isBmp =
- header[0] == 0x42 && // B
- header[1] == 0x4D; // M
+ isBmp = header[0] == 0x42 && // B
+ header[1] == 0x4D; // M
}
return isBmp;
diff --git a/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs b/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs
index 1fb911bdd..fa434da84 100644
--- a/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs
@@ -1,17 +1,13 @@
-// --------------------------------------------------------------------------------------------------------------------
-//
-// Copyright (c) James South and contributors.
-// Licensed under the Apache License, Version 2.0.
+//
+// Copyright (c) James South and contributors.
+// Licensed under the Apache License, Version 2.0.
//
-//
-// Performs the bmp decoding operation.
-//
-// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
using System;
using System.IO;
+ using System.Threading.Tasks;
///
/// Performs the bmp decoding operation.
@@ -108,7 +104,7 @@ namespace ImageProcessor.Formats
+ $"bigger then the max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'");
}
- byte[] imageData = new byte[this.infoHeader.Width * this.infoHeader.Height * 4];
+ float[] imageData = new float[this.infoHeader.Width * this.infoHeader.Height * 4];
switch (this.infoHeader.Compression)
{
@@ -179,12 +175,12 @@ namespace ImageProcessor.Formats
///
/// Reads the color palette from the stream.
///
- /// The image data to assign the palette to.
+ /// The image data to assign the palette to.
/// The containing the colors.
/// The width of the bitmap.
/// The height of the bitmap.
/// The number of bits per pixel.
- private void ReadRgbPalette(byte[] imageData, byte[] colors, int width, int height, int bits)
+ private void ReadRgbPalette(float[] imageData, byte[] colors, int width, int height, int bits)
{
// Pixels per byte (bits per pixel)
int ppb = 8 / bits;
@@ -194,7 +190,7 @@ namespace ImageProcessor.Formats
// Bit mask
int mask = 0xFF >> (8 - bits);
- byte[] data = new byte[(arrayWidth * height)];
+ byte[] data = new byte[arrayWidth * height];
this.currentStream.Read(data, 0, data.Length);
@@ -205,134 +201,154 @@ namespace ImageProcessor.Formats
alignment = 4 - alignment;
}
- for (int y = 0; y < height; y++)
- {
- int rowOffset = y * (arrayWidth + alignment);
+ Parallel.For(
+ 0,
+ height,
+ y =>
+ {
+ int rowOffset = y * (arrayWidth + alignment);
- for (int x = 0; x < arrayWidth; x++)
- {
- int offset = rowOffset + x;
+ for (int x = 0; x < arrayWidth; x++)
+ {
+ int offset = rowOffset + x;
- // Revert the y value, because bitmaps are saved from down to top
- int row = Invert(y, height);
+ // Revert the y value, because bitmaps are saved from down to top
+ int row = Invert(y, height);
- int colOffset = x * ppb;
+ int colOffset = x * ppb;
- for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++)
- {
- int colorIndex = (data[offset] >> (8 - bits - (shift * bits))) & mask;
+ for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++)
+ {
+ int colorIndex = ((data[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
+ int arrayOffset = ((row * width) + (colOffset + shift)) * 4;
- int arrayOffset = ((row * width) + (colOffset + shift)) * 4;
- imageData[arrayOffset + 0] = colors[colorIndex * 4];
- imageData[arrayOffset + 1] = colors[(colorIndex * 4) + 1];
- imageData[arrayOffset + 2] = colors[(colorIndex * 4) + 2];
- imageData[arrayOffset + 3] = 255;
- }
- }
- }
+ // We divide by 255 as we will store the colors in our floating point format.
+ // Stored in r-> g-> b-> a order.
+ imageData[arrayOffset] = colors[colorIndex + 2] / 255f; // r
+ imageData[arrayOffset + 1] = colors[colorIndex + 1] / 255f; // g
+ imageData[arrayOffset + 2] = colors[colorIndex] / 255f; // b
+ imageData[arrayOffset + 3] = 1; // a
+ }
+ }
+ });
}
///
/// Reads the 16 bit color palette from the stream
///
- /// The image data to assign the palette to.
+ /// The image data to assign the palette to.
/// The width of the bitmap.
/// The height of the bitmap.
- private void ReadRgb16(byte[] imageData, int width, int height)
+ private void ReadRgb16(float[] imageData, int width, int height)
{
- const int ScaleR = 256 / 32;
- const int ScaleG = 256 / 64;
+ // We divide here as we will store the colors in our floating point format.
+ const int ScaleR = (256 / 32) / 32;
+ const int ScaleG = (256 / 64) / 64;
int alignment;
byte[] data = this.GetImageArray(width, height, 2, out alignment);
- for (int y = 0; y < height; y++)
- {
- int rowOffset = y * ((width * 2) + alignment);
+ Parallel.For(
+ 0,
+ height,
+ y =>
+ {
+ int rowOffset = y * ((width * 2) + alignment);
- // Revert the y value, because bitmaps are saved from down to top
- int row = Invert(y, height);
+ // Revert the y value, because bitmaps are saved from down to top
+ int row = Invert(y, height);
- for (int x = 0; x < width; x++)
- {
- int offset = rowOffset + (x * 2);
+ for (int x = 0; x < width; x++)
+ {
+ int offset = rowOffset + (x * 2);
- short temp = BitConverter.ToInt16(data, offset);
+ short temp = BitConverter.ToInt16(data, offset);
- byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR);
- byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG);
- byte b = (byte)((temp & Rgb16BMask) * ScaleR);
+ float r = ((temp & Rgb16RMask) >> 11) * ScaleR;
+ float g = ((temp & Rgb16GMask) >> 5) * ScaleG;
+ float b = (temp & Rgb16BMask) * ScaleR;
- int arrayOffset = ((row * width) + x) * 4;
+ int arrayOffset = ((row * width) + x) * 4;
- imageData[arrayOffset + 0] = b;
- imageData[arrayOffset + 1] = g;
- imageData[arrayOffset + 2] = r;
- imageData[arrayOffset + 3] = 255;
- }
- }
+ // Stored in r-> g-> b-> a order.
+ imageData[arrayOffset] = r;
+ imageData[arrayOffset + 1] = g;
+ imageData[arrayOffset + 2] = b;
+ imageData[arrayOffset + 3] = 1;
+ }
+ });
}
///
/// Reads the 24 bit color palette from the stream
///
- /// The image data to assign the palette to.
+ /// The image data to assign the palette to.
/// The width of the bitmap.
/// The height of the bitmap.
- private void ReadRgb24(byte[] imageData, int width, int height)
+ private void ReadRgb24(float[] imageData, int width, int height)
{
int alignment;
byte[] data = this.GetImageArray(width, height, 3, out alignment);
- for (int y = 0; y < height; y++)
- {
- int rowOffset = y * ((width * 3) + alignment);
-
- // Revert the y value, because bitmaps are saved from down to top
- int row = Invert(y, height);
+ Parallel.For(
+ 0,
+ height,
+ y =>
+ {
+ int rowOffset = y * ((width * 3) + alignment);
- for (int x = 0; x < width; x++)
- {
- int offset = rowOffset + (x * 3);
- int arrayOffset = ((row * width) + x) * 4;
+ // Revert the y value, because bitmaps are saved from down to top
+ int row = Invert(y, height);
- imageData[arrayOffset + 0] = data[offset + 0];
- imageData[arrayOffset + 1] = data[offset + 1];
- imageData[arrayOffset + 2] = data[offset + 2];
- imageData[arrayOffset + 3] = 255;
- }
- }
+ for (int x = 0; x < width; x++)
+ {
+ int offset = rowOffset + (x * 3);
+ int arrayOffset = ((row * width) + x) * 4;
+
+ // We divide by 255 as we will store the colors in our floating point format.
+ // Stored in r-> g-> b-> a order.
+ imageData[arrayOffset] = data[offset + 2] / 255f;
+ imageData[arrayOffset + 1] = data[offset + 1] / 255f;
+ imageData[arrayOffset + 2] = data[offset] / 255f;
+ imageData[arrayOffset + 3] = 1;
+ }
+ });
}
///
/// Reads the 32 bit color palette from the stream
///
- /// The image data to assign the palette to.
+ /// The image data to assign the palette to.
/// The width of the bitmap.
/// The height of the bitmap.
- private void ReadRgb32(byte[] imageData, int width, int height)
+ private void ReadRgb32(float[] imageData, int width, int height)
{
int alignment;
byte[] data = this.GetImageArray(width, height, 4, out alignment);
- for (int y = 0; y < height; y++)
- {
- int rowOffset = y * ((width * 4) + alignment);
-
- // Revert the y value, because bitmaps are saved from down to top
- int row = Invert(y, height);
+ Parallel.For(
+ 0,
+ height,
+ y =>
+ {
+ int rowOffset = y * ((width * 4) + alignment);
- for (int x = 0; x < width; x++)
- {
- int offset = rowOffset + (x * 4);
+ // Revert the y value, because bitmaps are saved from down to top
+ int row = Invert(y, height);
- var arrayOffset = ((row * width) + x) * 4;
- imageData[arrayOffset + 0] = data[offset + 0];
- imageData[arrayOffset + 1] = data[offset + 1];
- imageData[arrayOffset + 2] = data[offset + 2];
- imageData[arrayOffset + 3] = 255; // Can we get alpha here?
- }
- }
+ for (int x = 0; x < width; x++)
+ {
+ int offset = rowOffset + (x * 4);
+ int arrayOffset = ((row * width) + x) * 4;
+
+ // We divide by 255 as we will store the colors in our floating point format.
+ // Stored in r-> g-> b-> a order.
+ imageData[arrayOffset] = data[offset + 2] / 255f;
+ imageData[arrayOffset + 1] = data[offset + 1] / 255f;
+ imageData[arrayOffset + 2] = data[offset] / 255f;
+ imageData[arrayOffset + 3] = 1; // TODO: Can we use our real alpha here?
+ }
+ });
}
///
diff --git a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
index ed833bf62..c37809635 100644
--- a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
+++ b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
@@ -100,7 +100,7 @@ namespace ImageProcessor.Formats
amount = 4 - amount;
}
- byte[] data = image.Pixels;
+ float[] data = image.Pixels;
for (int y = image.Height - 1; y >= 0; y--)
{
@@ -108,9 +108,11 @@ namespace ImageProcessor.Formats
{
int offset = ((y * image.Width) + x) * 4;
- writer.Write(data[offset + 0]);
- writer.Write(data[offset + 1]);
- writer.Write(data[offset + 2]);
+ // Limit the output range and multiply out from our floating point.
+ // Convert back to b-> g-> r-> a order.
+ writer.Write((byte)(data[offset + 2].Clamp(0, 1) * 255));
+ writer.Write((byte)(data[offset + 1].Clamp(0, 1) * 255));
+ writer.Write((byte)(data[offset].Clamp(0, 1) * 255));
}
for (int i = 0; i < amount; i++)
diff --git a/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs b/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs
index ae91b3d03..8675cbc7a 100644
--- a/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs
@@ -31,7 +31,7 @@ namespace ImageProcessor.Formats
///
/// The current frame.
///
- private byte[] currentFrame;
+ private float[] currentFrame;
///
/// The logical screen descriptor.
@@ -288,15 +288,15 @@ namespace ImageProcessor.Formats
if (this.currentFrame == null)
{
- this.currentFrame = new byte[imageWidth * imageHeight * 4];
+ this.currentFrame = new float[imageWidth * imageHeight * 4];
}
- byte[] lastFrame = null;
+ float[] lastFrame = null;
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{
- lastFrame = new byte[imageWidth * imageHeight * 4];
+ lastFrame = new float[imageWidth * imageHeight * 4];
Array.Copy(this.currentFrame, lastFrame, lastFrame.Length);
}
@@ -352,18 +352,20 @@ namespace ImageProcessor.Formats
this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index)
{
+ // We divide by 255 as we will store the colors in our floating point format.
+ // Stored in r-> g-> b-> a order.
int indexOffset = index * 3;
- this.currentFrame[offset + 0] = colorTable[indexOffset + 2];
- this.currentFrame[offset + 1] = colorTable[indexOffset + 1];
- this.currentFrame[offset + 2] = colorTable[indexOffset + 0];
- this.currentFrame[offset + 3] = 255;
+ this.currentFrame[offset + 0] = colorTable[indexOffset] / 255f; // r
+ this.currentFrame[offset + 1] = colorTable[indexOffset + 1] / 255f; // g
+ this.currentFrame[offset + 2] = colorTable[indexOffset + 2] / 255f; // b
+ this.currentFrame[offset + 3] = 1; // a
}
i++;
}
}
- byte[] pixels = new byte[imageWidth * imageHeight * 4];
+ float[] pixels = new float[imageWidth * imageHeight * 4];
Array.Copy(this.currentFrame, pixels, pixels.Length);
@@ -406,6 +408,7 @@ namespace ImageProcessor.Formats
{
offset = ((y * imageWidth) + x) * 4;
+ // Stored in r-> g-> b-> a order.
this.currentFrame[offset + 0] = 0;
this.currentFrame[offset + 1] = 0;
this.currentFrame[offset + 2] = 0;
diff --git a/src/ImageProcessor/Formats/Gif/GifEncoder.cs b/src/ImageProcessor/Formats/Gif/GifEncoder.cs
index cf8dc73d0..735d26c39 100644
--- a/src/ImageProcessor/Formats/Gif/GifEncoder.cs
+++ b/src/ImageProcessor/Formats/Gif/GifEncoder.cs
@@ -127,7 +127,7 @@ namespace ImageProcessor.Formats
QuantizedImage quantizedImage = quantizer.Quantize(image);
// Grab the pallete and write it to the stream.
- Bgra[] pallete = quantizedImage.Palette;
+ Bgra32[] pallete = quantizedImage.Palette;
int pixelCount = pallete.Length;
// Get max colors for bit depth.
@@ -137,7 +137,7 @@ namespace ImageProcessor.Formats
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 3;
- Bgra color = pallete[i];
+ Bgra32 color = pallete[i];
colorTable[offset + 2] = color.B;
colorTable[offset + 1] = color.G;
colorTable[offset + 0] = color.R;
diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs b/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs
index 7358d015a..6f6f375e9 100644
--- a/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs
+++ b/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs
@@ -70,7 +70,7 @@ namespace ImageProcessor.Formats
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
///
- protected override void InitialQuantizePixel(Bgra pixel)
+ protected override void InitialQuantizePixel(Bgra32 pixel)
{
// Add the color to the Octree
this.octree.AddColor(pixel);
@@ -85,7 +85,7 @@ namespace ImageProcessor.Formats
///
/// The quantized value
///
- protected override byte QuantizePixel(Bgra pixel)
+ protected override byte QuantizePixel(Bgra32 pixel)
{
// The color at [maxColors] is set to transparent
byte paletteIndex = (byte)this.maxColors;
@@ -105,13 +105,13 @@ namespace ImageProcessor.Formats
///
/// The new color palette
///
- protected override List GetPalette()
+ protected override List GetPalette()
{
// First off convert the Octree to maxColors colors
- List palette = this.octree.Palletize(Math.Max(this.maxColors - 1, 1));
+ List palette = this.octree.Palletize(Math.Max(this.maxColors - 1, 1));
// Add empty color for transparency
- palette.Add(Bgra.Empty);
+ palette.Add(Bgra32.Empty);
return palette;
}
@@ -190,18 +190,18 @@ namespace ImageProcessor.Formats
/// Add a given color value to the Octree
///
///
- /// The containing color information to add.
+ /// The containing color information to add.
///
- public void AddColor(Bgra pixel)
+ public void AddColor(Bgra32 pixel)
{
// Check if this request is for the same color as the last
- if (this.previousColor == pixel.BGRA)
+ if (this.previousColor == pixel.Bgra)
{
// If so, check if I have a previous node setup. This will only occur if the first color in the image
// happens to be black, with an alpha component of zero.
- if (null == this.previousNode)
+ if (this.previousNode == null)
{
- this.previousColor = pixel.BGRA;
+ this.previousColor = pixel.Bgra;
this.root.AddColor(pixel, this.maxColorBits, 0, this);
}
else
@@ -212,7 +212,7 @@ namespace ImageProcessor.Formats
}
else
{
- this.previousColor = pixel.BGRA;
+ this.previousColor = pixel.Bgra;
this.root.AddColor(pixel, this.maxColorBits, 0, this);
}
}
@@ -226,7 +226,7 @@ namespace ImageProcessor.Formats
///
/// An with the palletized colors
///
- public List Palletize(int colorCount)
+ public List Palletize(int colorCount)
{
while (this.Leaves > colorCount)
{
@@ -234,7 +234,7 @@ namespace ImageProcessor.Formats
}
// Now palletize the nodes
- List palette = new List(this.Leaves);
+ List palette = new List(this.Leaves);
int paletteIndex = 0;
this.root.ConstructPalette(palette, ref paletteIndex);
@@ -246,12 +246,12 @@ namespace ImageProcessor.Formats
/// Get the palette index for the passed color
///
///
- /// The containing the pixel data.
+ /// The containing the pixel data.
///
///
/// The index of the given structure.
///
- public int GetPaletteIndex(Bgra pixel)
+ public int GetPaletteIndex(Bgra32 pixel)
{
return this.root.GetPaletteIndex(pixel, 0);
}
@@ -274,7 +274,7 @@ namespace ImageProcessor.Formats
{
// Find the deepest level containing at least one reducible node
int index = this.maxColorBits - 1;
- while ((index > 0) && (null == this.reducibleNodes[index]))
+ while ((index > 0) && (this.reducibleNodes[index] == null))
{
index--;
}
@@ -387,7 +387,7 @@ namespace ImageProcessor.Formats
///
/// The tree to which this node belongs
///
- public void AddColor(Bgra pixel, int colorBits, int level, Octree octree)
+ public void AddColor(Bgra32 pixel, int colorBits, int level, Octree octree)
{
// Update the color information if this is a leaf
if (this.leaf)
@@ -407,7 +407,7 @@ namespace ImageProcessor.Formats
OctreeNode child = this.children[index];
- if (null == child)
+ if (child == null)
{
// Create a new child node and store it in the array
child = new OctreeNode(level + 1, colorBits, octree);
@@ -431,7 +431,7 @@ namespace ImageProcessor.Formats
// Loop through all children and add their information to this node
for (int index = 0; index < 8; index++)
{
- if (null != this.children[index])
+ if (this.children[index] != null)
{
this.red += this.children[index].red;
this.green += this.children[index].green;
@@ -458,7 +458,7 @@ namespace ImageProcessor.Formats
///
/// The current palette index
///
- public void ConstructPalette(List palette, ref int index)
+ public void ConstructPalette(List palette, ref int index)
{
if (this.leaf)
{
@@ -470,14 +470,14 @@ namespace ImageProcessor.Formats
byte b = (this.blue / this.pixelCount).ToByte();
// And set the color of the palette entry
- palette.Add(new Bgra(b, g, r));
+ palette.Add(new Bgra32(b, g, r));
}
else
{
// Loop through children looking for leaves
for (int i = 0; i < 8; i++)
{
- if (null != this.children[i])
+ if (this.children[i] != null)
{
this.children[i].ConstructPalette(palette, ref index);
}
@@ -489,7 +489,7 @@ namespace ImageProcessor.Formats
/// Return the palette index for the passed color
///
///
- /// The representing the pixel.
+ /// The representing the pixel.
///
///
/// The level.
@@ -497,7 +497,7 @@ namespace ImageProcessor.Formats
///
/// The representing the index of the pixel in the palette.
///
- public int GetPaletteIndex(Bgra pixel, int level)
+ public int GetPaletteIndex(Bgra32 pixel, int level)
{
int index = this.paletteIndex;
@@ -508,7 +508,7 @@ namespace ImageProcessor.Formats
((pixel.G & Mask[level]) >> (shift - 1)) |
((pixel.B & Mask[level]) >> shift);
- if (null != this.children[pixelIndex])
+ if (this.children[pixelIndex] != null)
{
index = this.children[pixelIndex].GetPaletteIndex(pixel, level + 1);
}
@@ -527,7 +527,7 @@ namespace ImageProcessor.Formats
///
/// The pixel to add.
///
- public void Increment(Bgra pixel)
+ public void Increment(Bgra32 pixel)
{
this.pixelCount++;
this.red += pixel.R;
diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs b/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs
index 76c273fe8..5bb8f9ec6 100644
--- a/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs
+++ b/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs
@@ -19,7 +19,7 @@ namespace ImageProcessor.Formats
/// The image height.
/// The color palette.
/// The quantized pixels.
- public QuantizedImage(int width, int height, Bgra[] palette, byte[] pixels)
+ public QuantizedImage(int width, int height, Bgra32[] palette, byte[] pixels)
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
@@ -51,7 +51,7 @@ namespace ImageProcessor.Formats
///
/// Gets the color palette of this .
///
- public Bgra[] Palette { get; }
+ public Bgra32[] Palette { get; }
///
/// Gets the pixels of this .
@@ -69,12 +69,12 @@ namespace ImageProcessor.Formats
Image image = new Image();
int pixelCount = this.Pixels.Length;
- byte[] bgraPixels = new byte[pixelCount * 4];
+ float[] bgraPixels = new float[pixelCount * 4];
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 4;
- Bgra color = this.Palette[this.Pixels[i]];
+ Bgra32 color = this.Palette[this.Pixels[i]];
bgraPixels[offset + 0] = color.B;
bgraPixels[offset + 1] = color.G;
bgraPixels[offset + 2] = color.R;
diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs b/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs
index dda33d479..c35a9f7f4 100644
--- a/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs
+++ b/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs
@@ -18,7 +18,7 @@ namespace ImageProcessor.Formats
private readonly bool singlePass;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
///
/// If true, the quantization only needs to loop through the source pixels once
@@ -56,7 +56,8 @@ namespace ImageProcessor.Formats
byte[] quantizedPixels = new byte[width * height];
- List palette = this.GetPalette();
+ // Get the pallete
+ List palette = this.GetPalette();
this.SecondPass(imageBase, quantizedPixels, width, height);
@@ -94,8 +95,9 @@ namespace ImageProcessor.Formats
{
int i = 0;
- // Convert the first pixel, so that I have values going into the loop
- Bgra previousPixel = source[0, 0];
+ // Convert the first pixel, so that I have values going into the loop.
+ // Implicit cast here from Color.
+ Bgra32 previousPixel = source[0, 0];
byte pixelValue = this.QuantizePixel(previousPixel);
output[0] = pixelValue;
@@ -104,7 +106,8 @@ namespace ImageProcessor.Formats
{
for (int x = 0; x < width; x++)
{
- Bgra sourcePixel = source[x, y];
+ // Implicit cast here from Color.
+ Bgra32 sourcePixel = source[x, y];
// Check if this is the same as the last pixel. If so use that value
// rather than calculating it again. This is an inexpensive optimization.
@@ -132,7 +135,7 @@ namespace ImageProcessor.Formats
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
///
- protected virtual void InitialQuantizePixel(Bgra pixel)
+ protected virtual void InitialQuantizePixel(Bgra32 pixel)
{
}
@@ -145,7 +148,7 @@ namespace ImageProcessor.Formats
///
/// The quantized value
///
- protected abstract byte QuantizePixel(Bgra pixel);
+ protected abstract byte QuantizePixel(Bgra32 pixel);
///
/// Retrieve the palette for the quantized image
@@ -153,6 +156,6 @@ namespace ImageProcessor.Formats
///
/// The new color palette
///
- protected abstract List GetPalette();
+ protected abstract List GetPalette();
}
}
\ No newline at end of file
diff --git a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
index 766923f7e..541400a4e 100644
--- a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
+++ b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
@@ -7,6 +7,8 @@ namespace ImageProcessor.Formats
{
using System;
using System.IO;
+ using System.Threading.Tasks;
+
using BitMiracle.LibJpeg;
///
@@ -97,50 +99,41 @@ namespace ImageProcessor.Formats
int pixelWidth = jpg.Width;
int pixelHeight = jpg.Height;
- byte[] pixels = new byte[pixelWidth * pixelHeight * 4];
+ float[] pixels = new float[pixelWidth * pixelHeight * 4];
if (!(jpg.Colorspace == Colorspace.RGB && jpg.BitsPerComponent == 8))
{
throw new NotSupportedException("JpegDecoder only support RGB color space.");
}
- for (int y = 0; y < pixelHeight; y++)
- {
- SampleRow row = jpg.GetRow(y);
+ Parallel.For(
+ 0,
+ pixelHeight,
+ y =>
+ {
+ SampleRow row = jpg.GetRow(y);
- for (int x = 0; x < pixelWidth; x++)
- {
- Sample sample = row.GetAt(x);
+ for (int x = 0; x < pixelWidth; x++)
+ {
+ Sample sample = row.GetAt(x);
- int offset = ((y * pixelWidth) + x) * 4;
+ int offset = ((y * pixelWidth) + x) * 4;
- pixels[offset + 0] = (byte)sample[2];
- pixels[offset + 1] = (byte)sample[1];
- pixels[offset + 2] = (byte)sample[0];
- pixels[offset + 3] = 255;
- }
- }
+ pixels[offset + 0] = sample[0] / 255f;
+ pixels[offset + 1] = sample[1] / 255f;
+ pixels[offset + 2] = sample[2] / 255f;
+ pixels[offset + 3] = 1;
+ }
+ });
image.SetPixels(pixelWidth, pixelHeight, pixels);
}
///
- ///
+ /// Returns a value indicating whether the given bytes identify Jpeg data.
///
- ///
- ///
- private bool IsExif(byte[] header)
- {
- bool isExif =
- header[6] == 0x45 && // E
- header[7] == 0x78 && // x
- header[8] == 0x69 && // i
- header[9] == 0x66 && // f
- header[10] == 0x00;
-
- return isExif;
- }
-
+ /// The bytes representing the file header.
+ /// The
private static bool IsJpeg(byte[] header)
{
bool isJpg =
@@ -152,5 +145,22 @@ namespace ImageProcessor.Formats
return isJpg;
}
+
+ ///
+ /// Returns a value indicating whether the given bytes identify EXIF data.
+ ///
+ /// The bytes representing the file header.
+ /// The
+ private bool IsExif(byte[] header)
+ {
+ bool isExif =
+ header[6] == 0x45 && // E
+ header[7] == 0x78 && // x
+ header[8] == 0x69 && // i
+ header[9] == 0x66 && // f
+ header[10] == 0x00;
+
+ return isExif;
+ }
}
}
diff --git a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs
index 35d6e286e..84ce9c423 100644
--- a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs
+++ b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs
@@ -7,6 +7,7 @@ namespace ImageProcessor.Formats
{
using System;
using System.IO;
+ using System.Threading.Tasks;
using BitMiracle.LibJpeg;
@@ -88,26 +89,29 @@ namespace ImageProcessor.Formats
int pixelWidth = image.Width;
int pixelHeight = image.Height;
- byte[] sourcePixels = image.Pixels;
+ float[] sourcePixels = image.Pixels;
SampleRow[] rows = new SampleRow[pixelHeight];
- for (int y = 0; y < pixelHeight; y++)
- {
- byte[] samples = new byte[pixelWidth * 3];
-
- for (int x = 0; x < pixelWidth; x++)
- {
- int start = x * 3;
- int source = ((y * pixelWidth) + x) * 4;
-
- samples[start] = sourcePixels[source + 2];
- samples[start + 1] = sourcePixels[source + 1];
- samples[start + 2] = sourcePixels[source];
- }
-
- rows[y] = new SampleRow(samples, pixelWidth, 8, 3);
- }
+ Parallel.For(
+ 0,
+ pixelHeight,
+ y =>
+ {
+ byte[] samples = new byte[pixelWidth * 3];
+
+ for (int x = 0; x < pixelWidth; x++)
+ {
+ int start = x * 3;
+ int source = ((y * pixelWidth) + x) * 4;
+
+ samples[start] = (byte)(sourcePixels[source].Clamp(0, 1) * 255);
+ samples[start + 1] = (byte)(sourcePixels[source + 1].Clamp(0, 1) * 255);
+ samples[start + 2] = (byte)(sourcePixels[source + 2].Clamp(0, 1) * 255);
+ }
+
+ rows[y] = new SampleRow(samples, pixelWidth, 8, 3);
+ });
JpegImage jpg = new JpegImage(rows, Colorspace.RGB);
jpg.WriteJpeg(stream, new CompressionParameters { Quality = this.Quality });
diff --git a/src/ImageProcessor/Formats/Png/GrayscaleReader.cs b/src/ImageProcessor/Formats/Png/GrayscaleReader.cs
index 552776a67..244c0e8c0 100644
--- a/src/ImageProcessor/Formats/Png/GrayscaleReader.cs
+++ b/src/ImageProcessor/Formats/Png/GrayscaleReader.cs
@@ -1,12 +1,7 @@
-// --------------------------------------------------------------------------------------------------------------------
-//
-// Copyright (c) James South and contributors.
-// Licensed under the Apache License, Version 2.0.
+//
+// Copyright (c) James South and contributors.
+// Licensed under the Apache License, Version 2.0.
//
-//
-// Color reader for reading grayscale colors from a png file.
-//
-// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@@ -37,31 +32,25 @@ namespace ImageProcessor.Formats
this.useAlpha = useAlpha;
}
- ///
- /// Reads the specified scanline.
- ///
- /// The scanline.
- /// The pixels, where the colors should be stored in BGRA format.
- ///
- /// The header, which contains information about the png file, like
- /// the width of the image and the height.
- ///
- public void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header)
+ ///
+ public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header)
{
int offset;
byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth);
+ // We divide by 255 as we will store the colors in our floating point format.
+ // Stored in r-> g-> b-> a order.
if (this.useAlpha)
{
for (int x = 0; x < header.Width / 2; x++)
{
offset = ((this.row * header.Width) + x) * 4;
- pixels[offset + 0] = newScanline[x * 2];
- pixels[offset + 1] = newScanline[x * 2];
- pixels[offset + 2] = newScanline[x * 2];
- pixels[offset + 3] = newScanline[(x * 2) + 1];
+ pixels[offset] = newScanline[x * 2] / 255f;
+ pixels[offset + 1] = newScanline[x * 2] / 255f;
+ pixels[offset + 2] = newScanline[x * 2] / 255f;
+ pixels[offset + 3] = newScanline[(x * 2) + 1] / 255f;
}
}
else
@@ -70,10 +59,10 @@ namespace ImageProcessor.Formats
{
offset = ((this.row * header.Width) + x) * 4;
- pixels[offset + 0] = newScanline[x];
- pixels[offset + 1] = newScanline[x];
- pixels[offset + 2] = newScanline[x];
- pixels[offset + 3] = 255;
+ pixels[offset] = newScanline[x] / 255f;
+ pixels[offset + 1] = newScanline[x] / 255f;
+ pixels[offset + 2] = newScanline[x] / 255f;
+ pixels[offset + 3] = 1;
}
}
diff --git a/src/ImageProcessor/Formats/Png/IColorReader.cs b/src/ImageProcessor/Formats/Png/IColorReader.cs
index 40c14d6ec..0d7ff27e3 100644
--- a/src/ImageProcessor/Formats/Png/IColorReader.cs
+++ b/src/ImageProcessor/Formats/Png/IColorReader.cs
@@ -1,18 +1,12 @@
-// --------------------------------------------------------------------------------------------------------------------
-//
-// Copyright (c) James South and contributors.
-// Licensed under the Apache License, Version 2.0.
+//
+// Copyright (c) James South and contributors.
+// Licensed under the Apache License, Version 2.0.
//
-//
-// Encapsulates methods for color readers, which are responsible for reading
-// different color formats from a png file.
-//
-// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
///
- /// Encapsulates methods for color readers, which are responsible for reading
+ /// Encapsulates methods for color readers, which are responsible for reading
/// different color formats from a png file.
///
public interface IColorReader
@@ -26,6 +20,6 @@ namespace ImageProcessor.Formats
/// The header, which contains information about the png file, like
/// the width of the image and the height.
///
- void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header);
+ void ReadScanline(byte[] scanline, float[] pixels, PngHeader header);
}
}
diff --git a/src/ImageProcessor/Formats/Png/PaletteIndexReader.cs b/src/ImageProcessor/Formats/Png/PaletteIndexReader.cs
index a44d34c29..550ac358b 100644
--- a/src/ImageProcessor/Formats/Png/PaletteIndexReader.cs
+++ b/src/ImageProcessor/Formats/Png/PaletteIndexReader.cs
@@ -1,12 +1,7 @@
-// --------------------------------------------------------------------------------------------------------------------
-//
-// Copyright (c) James South and contributors.
-// Licensed under the Apache License, Version 2.0.
+//
+// Copyright (c) James South and contributors.
+// Licensed under the Apache License, Version 2.0.
//
-//
-// A color reader for reading palette indices from the png file.
-//
-// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@@ -43,14 +38,8 @@ namespace ImageProcessor.Formats
this.paletteAlpha = paletteAlpha;
}
- ///
- /// Reads the specified scanline.
- ///
- /// The scanline.
- /// The pixels, where the colors should be stored in BGRA format.
- /// The header, which contains information about the png file, like
- /// the width of the image and the height.
- public void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header)
+ ///
+ public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header)
{
byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth);
int offset, index;
@@ -65,13 +54,14 @@ namespace ImageProcessor.Formats
index = newScanline[i];
offset = ((this.row * header.Width) + i) * 4;
+ int pixelOffset = index * 3;
- pixels[offset + 0] = this.palette[(index * 3) + 2];
- pixels[offset + 1] = this.palette[(index * 3) + 1];
- pixels[offset + 2] = this.palette[(index * 3) + 0];
+ pixels[offset] = this.palette[pixelOffset] / 255f;
+ pixels[offset + 1] = this.palette[pixelOffset + 1] / 255f;
+ pixels[offset + 2] = this.palette[pixelOffset + 2] / 255f;
pixels[offset + 3] = this.paletteAlpha.Length > index
- ? this.paletteAlpha[index]
- : (byte)255;
+ ? this.paletteAlpha[index] / 255f
+ : 1;
}
}
else
@@ -81,11 +71,12 @@ namespace ImageProcessor.Formats
index = newScanline[i];
offset = ((this.row * header.Width) + i) * 4;
+ int pixelOffset = index * 3;
- pixels[offset + 0] = this.palette[(index * 3) + 2];
- pixels[offset + 1] = this.palette[(index * 3) + 1];
- pixels[offset + 2] = this.palette[(index * 3) + 0];
- pixels[offset + 3] = 255;
+ pixels[offset] = this.palette[pixelOffset] / 255f;
+ pixels[offset + 1] = this.palette[pixelOffset + 1] / 255f;
+ pixels[offset + 2] = this.palette[pixelOffset + 2] / 255f;
+ pixels[offset + 3] = 1;
}
}
diff --git a/src/ImageProcessor/Formats/Png/PngDecoderCore.cs b/src/ImageProcessor/Formats/Png/PngDecoderCore.cs
index 9f837a6bf..1bf8e7740 100644
--- a/src/ImageProcessor/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageProcessor/Formats/Png/PngDecoderCore.cs
@@ -16,9 +16,6 @@ namespace ImageProcessor.Formats
using System.Linq;
using System.Text;
- //using ICSharpCode.SharpZipLib.Checksums;
- //using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
-
///
/// Performs the png decoding operation.
///
@@ -145,7 +142,7 @@ namespace ImageProcessor.Formats
+ $"max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'");
}
- byte[] pixels = new byte[this.header.Width * this.header.Height * 4];
+ float[] pixels = new float[this.header.Width * this.header.Height * 4];
PngColorTypeInformation colorTypeInformation = ColorTypes[this.header.ColorType];
@@ -248,10 +245,10 @@ namespace ImageProcessor.Formats
///
/// The containing data.
///
- /// The containing pixel data.
+ /// The containing pixel data.
/// The color reader.
/// The color type information.
- private void ReadScanlines(MemoryStream dataStream, byte[] pixels, IColorReader colorReader, PngColorTypeInformation colorTypeInformation)
+ private void ReadScanlines(MemoryStream dataStream, float[] pixels, IColorReader colorReader, PngColorTypeInformation colorTypeInformation)
{
dataStream.Position = 0;
diff --git a/src/ImageProcessor/Formats/Png/PngEncoder.cs b/src/ImageProcessor/Formats/Png/PngEncoder.cs
index 14a312220..fd3c531d0 100644
--- a/src/ImageProcessor/Formats/Png/PngEncoder.cs
+++ b/src/ImageProcessor/Formats/Png/PngEncoder.cs
@@ -8,9 +8,6 @@ namespace ImageProcessor.Formats
using System;
using System.IO;
- //using ICSharpCode.SharpZipLib.Checksums;
- //using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
-
///
/// Image encoder for writing image data to a stream in png format.
///
@@ -36,7 +33,7 @@ namespace ImageProcessor.Formats
public int Quality { get; set; }
///
- public string MimeType => "image/jpepngg";
+ public string MimeType => "image/png";
///
public string Extension => "png";
@@ -225,7 +222,7 @@ namespace ImageProcessor.Formats
/// The image base.
private void WriteDataChunksFast(Stream stream, ImageBase imageBase)
{
- byte[] pixels = imageBase.Pixels;
+ float[] pixels = imageBase.Pixels;
// Convert the pixel array to a new array for adding
// the filter byte.
@@ -297,7 +294,7 @@ namespace ImageProcessor.Formats
/// The image base.
private void WriteDataChunks(Stream stream, ImageBase imageBase)
{
- byte[] pixels = imageBase.Pixels;
+ float[] pixels = imageBase.Pixels;
byte[] data = new byte[(imageBase.Width * imageBase.Height * 4) + imageBase.Height];
@@ -321,19 +318,19 @@ namespace ImageProcessor.Formats
// Calculate the offset for the original pixel array.
int pixelOffset = ((y * imageBase.Width) + x) * 4;
- data[dataOffset + 0] = pixels[pixelOffset + 2];
- data[dataOffset + 1] = pixels[pixelOffset + 1];
- data[dataOffset + 2] = pixels[pixelOffset + 0];
- data[dataOffset + 3] = pixels[pixelOffset + 3];
+ data[dataOffset] = (byte)(pixels[pixelOffset].Clamp(0, 1) * 255);
+ data[dataOffset + 1] = (byte)(pixels[pixelOffset + 1].Clamp(0, 1) * 255);
+ data[dataOffset + 2] = (byte)(pixels[pixelOffset + 2].Clamp(0, 1) * 255);
+ data[dataOffset + 3] = (byte)(pixels[pixelOffset + 3].Clamp(0, 1) * 255);
if (y > 0)
{
int lastOffset = (((y - 1) * imageBase.Width) + x) * 4;
- data[dataOffset + 0] -= pixels[lastOffset + 2];
- data[dataOffset + 1] -= pixels[lastOffset + 1];
- data[dataOffset + 2] -= pixels[lastOffset + 0];
- data[dataOffset + 3] -= pixels[lastOffset + 3];
+ data[dataOffset] -= (byte)(pixels[lastOffset].Clamp(0, 1) * 255);
+ data[dataOffset + 1] -= (byte)(pixels[lastOffset + 1].Clamp(0, 1) * 255);
+ data[dataOffset + 2] -= (byte)(pixels[lastOffset + 2].Clamp(0, 1) * 255);
+ data[dataOffset + 3] -= (byte)(pixels[lastOffset + 3].Clamp(0, 1) * 255);
}
}
}
diff --git a/src/ImageProcessor/Formats/Png/TrueColorReader.cs b/src/ImageProcessor/Formats/Png/TrueColorReader.cs
index c60e68306..0ce755a3f 100644
--- a/src/ImageProcessor/Formats/Png/TrueColorReader.cs
+++ b/src/ImageProcessor/Formats/Png/TrueColorReader.cs
@@ -1,13 +1,7 @@
-// --------------------------------------------------------------------------------------------------------------------
-//
-// Copyright (c) James South and contributors.
-// Licensed under the Apache License, Version 2.0.
+//
+// Copyright (c) James South and contributors.
+// Licensed under the Apache License, Version 2.0.
//
-//
-// Color reader for reading true colors from a png file. Only colors
-// with 24 or 32 bit (3 or 4 bytes) per pixel are supported at the moment.
-//
-// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
@@ -37,14 +31,8 @@ namespace ImageProcessor.Formats
this.useAlpha = useAlpha;
}
- ///
- /// Reads the specified scanline.
- ///
- /// The scanline.
- /// The pixels, where the colors should be stored in BGRA format.
- /// The header, which contains information about the png file, like
- /// the width of the image and the height.
- public void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header)
+ ///
+ public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header)
{
int offset;
@@ -56,10 +44,10 @@ namespace ImageProcessor.Formats
{
offset = ((this.row * header.Width) + (x >> 2)) * 4;
- pixels[offset + 0] = newScanline[x + 2];
- pixels[offset + 1] = newScanline[x + 1];
- pixels[offset + 2] = newScanline[x + 0];
- pixels[offset + 3] = newScanline[x + 3];
+ pixels[offset + 0] = newScanline[x] / 255f;
+ pixels[offset + 1] = newScanline[x + 1] / 255f;
+ pixels[offset + 2] = newScanline[x + 2] / 255f;
+ pixels[offset + 3] = newScanline[x + 3] / 255f;
}
}
else
@@ -67,11 +55,12 @@ namespace ImageProcessor.Formats
for (int x = 0; x < newScanline.Length / 3; x++)
{
offset = ((this.row * header.Width) + x) * 4;
+ int pixelOffset = x * 3;
- pixels[offset + 0] = newScanline[(x * 3) + 2];
- pixels[offset + 1] = newScanline[(x * 3) + 1];
- pixels[offset + 2] = newScanline[(x * 3) + 0];
- pixels[offset + 3] = 255;
+ pixels[offset + 0] = newScanline[pixelOffset] / 255f;
+ pixels[offset + 1] = newScanline[pixelOffset + 1] / 255f;
+ pixels[offset + 2] = newScanline[pixelOffset + 2] / 255f;
+ pixels[offset + 3] = 1;
}
}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs
index 42fa1753a..5c70ad41c 100644
--- a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs
+++ b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs
@@ -3,9 +3,6 @@
using System;
using System.IO;
- //using ICSharpCode.SharpZipLib.Zip;
- //using ICSharpCode.SharpZipLib.Zip.Compression;
-
///
/// An input buffer customised for use by
///
@@ -14,7 +11,6 @@
///
public class InflaterInputBuffer
{
- #region Constructors
///
/// Initialise a new instance of with a default buffer size
///
@@ -39,7 +35,6 @@
rawData = new byte[bufferSize];
clearText = rawData;
}
- #endregion
///
/// Get the length of bytes bytes in the
@@ -127,17 +122,7 @@
toRead -= count;
}
-#if !NETCF_1_0 && !NOCRYPTO
- if (cryptoTransform != null)
- {
- clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0);
- }
- else
-#endif
- {
- clearTextLength = rawLength;
- }
-
+ clearTextLength = rawLength;
available = clearTextLength;
}
@@ -178,12 +163,14 @@
return 0;
}
}
+
int toCopy = Math.Min(currentLength, available);
- System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy);
+ Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy);
currentOffset += toCopy;
currentLength -= toCopy;
available -= toCopy;
}
+
return length;
}
@@ -270,57 +257,12 @@
return (uint)ReadLeInt() | ((long)ReadLeInt() << 32);
}
-#if !NETCF_1_0 && !NOCRYPTO
- ///
- /// Get/set the to apply to any data.
- ///
- /// Set this value to null to have no transform applied.
- public ICryptoTransform CryptoTransform
- {
- set
- {
- cryptoTransform = value;
- if (cryptoTransform != null)
- {
- if (rawData == clearText)
- {
- if (internalClearText == null)
- {
- internalClearText = new byte[rawData.Length];
- }
- clearText = internalClearText;
- }
- clearTextLength = rawLength;
- if (available > 0)
- {
- cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available);
- }
- }
- else
- {
- clearText = rawData;
- clearTextLength = rawLength;
- }
- }
- }
-#endif
-
- #region Instance Fields
int rawLength;
byte[] rawData;
int clearTextLength;
byte[] clearText;
-#if !NETCF_1_0 && !NOCRYPTO
- byte[] internalClearText;
-#endif
-
int available;
-
-#if !NETCF_1_0 && !NOCRYPTO
- ICryptoTransform cryptoTransform;
-#endif
Stream inputStream;
- #endregion
}
}
diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs
index 62cba8bb4..b6a35e420 100644
--- a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs
+++ b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs
@@ -149,16 +149,6 @@
}
}
- ///
- /// Clear any cryptographic state.
- ///
- protected void StopDecrypting()
- {
-#if !NETCF_1_0 && !NOCRYPTO
- inputBuffer.CryptoTransform = null;
-#endif
- }
-
///
/// Returns 0 once the end of the stream (EOF) has been reached.
/// Otherwise returns 1.
diff --git a/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs b/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs
index 6dccc6a96..9ba46abe4 100644
--- a/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs
+++ b/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs
@@ -7,7 +7,6 @@
///
public static class ZipConstants
{
- #region Versions
///
/// The version made by field for entries in the central header when created by this library
///
@@ -30,9 +29,7 @@
/// The version required for Zip64 extensions (4.5 or higher)
///
public const int VersionZip64 = 45;
- #endregion
- #region Header Sizes
///
/// Size of local entry header (excluding variable length fields at end)
///
@@ -62,9 +59,7 @@
/// Size of 'classic' cryptographic header stored before any entry data
///
public const int CryptoHeaderSize = 12;
- #endregion
- #region Header Signatures
///
/// Signature for local entry header
@@ -121,52 +116,8 @@
/// End of central directory record signature
///
public const int EndOfCentralDirectorySignature = 'P' | ('K' << 8) | (5 << 16) | (6 << 24);
-
- #endregion
-
-#if NETCF_1_0 || NETCF_2_0
- // This isnt so great but is better than nothing.
- // Trying to work out an appropriate OEM code page would be good.
- // 850 is a good default for english speakers particularly in Europe.
- static int defaultCodePage = CultureInfo.CurrentCulture.TextInfo.ANSICodePage;
-#elif PCL
static Encoding defaultEncoding = Encoding.UTF8;
-#else
- ///
- /// Get OEM codepage from NetFX, which parses the NLP file with culture info table etc etc.
- /// But sometimes it yields the special value of 1 which is nicknamed CodePageNoOEM in sources (might also mean CP_OEMCP, but Encoding puts it so).
- /// This was observed on Ukranian and Hindu systems.
- /// Given this value, throws an .
- /// So replace it with some fallback, e.g. 437 which is the default cpcp in a console in a default Windows installation.
- ///
- static int defaultCodePage =
- // these values cause ArgumentException in subsequent calls to Encoding::GetEncoding()
- ((Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 1) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 2) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 3) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 42))
- ? 437 // The default OEM encoding in a console in a default Windows installation, as a fallback.
- : Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage;
-#endif
-#if !PCL
- ///
- /// Default encoding used for string conversion. 0 gives the default system OEM code page.
- /// Dont use unicode encodings if you want to be Zip compatible!
- /// Using the default code page isnt the full solution neccessarily
- /// there are many variable factors, codepage 850 is often a good choice for
- /// European users, however be careful about compatability.
- ///
- public static int DefaultCodePage {
- get {
- return defaultCodePage;
- }
- set {
- if ((value < 0) || (value > 65535) ||
- (value == 1) || (value == 2) || (value == 3) || (value == 42)) {
- throw new ArgumentOutOfRangeException("value");
- }
-
- defaultCodePage = value;
- }
- }
-#else
+
///
/// PCL don't support CodePage so we used Encoding instead of
///
@@ -176,12 +127,12 @@
{
return defaultEncoding;
}
+
set
{
defaultEncoding = value;
}
}
-#endif
///
/// Convert a portion of a byte array to a string.
@@ -201,11 +152,8 @@
{
return string.Empty;
}
-#if !PCL
- return Encoding.GetEncoding(DefaultCodePage).GetString(data, 0, count);
-#else
+
return DefaultEncoding.GetString(data, 0, count);
-#endif
}
///
@@ -294,11 +242,8 @@
{
return new byte[0];
}
-#if !PCL
- return Encoding.GetEncoding(DefaultCodePage).GetBytes(str);
-#else
+
return DefaultEncoding.GetBytes(str);
-#endif
}
///
@@ -320,10 +265,8 @@
{
return Encoding.UTF8.GetBytes(str);
}
- else
- {
- return ConvertToArray(str);
- }
+
+ return ConvertToArray(str);
}
}
}
diff --git a/src/ImageProcessor/IImageBase.cs b/src/ImageProcessor/IImageBase.cs
index 62fc821b3..cdbe2104a 100644
--- a/src/ImageProcessor/IImageBase.cs
+++ b/src/ImageProcessor/IImageBase.cs
@@ -20,7 +20,7 @@ namespace ImageProcessor
/// and stores the blue, the green, the red and the alpha value for
/// each pixel in this order.
///
- byte[] Pixels { get; }
+ float[] Pixels { get; }
///
/// Gets the width in pixels.
@@ -66,8 +66,8 @@ namespace ImageProcessor
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
///
- /// The at the specified position.
- Bgra this[int x, int y] { get; set; }
+ /// The at the specified position.
+ Color this[int x, int y] { get; set; }
///
/// Sets the pixel array of the image.
@@ -85,6 +85,6 @@ namespace ImageProcessor
///
/// Thrown if the length is not equal to Width * Height * 4.
///
- void SetPixels(int width, int height, byte[] pixels);
+ void SetPixels(int width, int height, float[] pixels);
}
}
diff --git a/src/ImageProcessor/ImageBase.cs b/src/ImageProcessor/ImageBase.cs
index 405cd888f..5e1aaa1a6 100644
--- a/src/ImageProcessor/ImageBase.cs
+++ b/src/ImageProcessor/ImageBase.cs
@@ -40,7 +40,7 @@ namespace ImageProcessor
this.Width = width;
this.Height = height;
- this.Pixels = new byte[width * height * 4];
+ this.Pixels = new float[width * height * 4];
}
///
@@ -56,13 +56,13 @@ namespace ImageProcessor
{
Guard.NotNull(other, nameof(other), "Other image cannot be null.");
- byte[] pixels = other.Pixels;
+ float[] pixels = other.Pixels;
this.Width = other.Width;
this.Height = other.Height;
this.Quality = other.Quality;
this.FrameDelay = other.FrameDelay;
- this.Pixels = new byte[pixels.Length];
+ this.Pixels = new float[pixels.Length];
Array.Copy(pixels, this.Pixels, pixels.Length);
}
@@ -84,7 +84,7 @@ namespace ImageProcessor
/// and stores the blue, the green, the red and the alpha value for
/// each pixel in this order.
///
- public byte[] Pixels { get; private set; }
+ public float[] Pixels { get; private set; }
///
/// Gets the width in pixels.
@@ -106,9 +106,7 @@ namespace ImageProcessor
///
public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height);
- ///
- /// Gets or sets th quality of the image. This affects the output quality of lossy image formats.
- ///
+ ///
public int Quality { get; set; }
///
@@ -119,19 +117,8 @@ namespace ImageProcessor
///
public int FrameDelay { get; set; }
- ///
- /// Gets or sets the color of a pixel at the specified position.
- ///
- ///
- /// The x-coordinate of the pixel. Must be greater
- /// than zero and smaller than the width of the pixel.
- ///
- ///
- /// The y-coordinate of the pixel. Must be greater
- /// than zero and smaller than the width of the pixel.
- ///
- /// The at the specified position.
- public Bgra this[int x, int y]
+ ///
+ public Color this[int x, int y]
{
get
{
@@ -148,7 +135,7 @@ namespace ImageProcessor
#endif
int start = ((y * this.Width) + x) * 4;
- return new Bgra(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]);
+ return new Color(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]);
}
set
@@ -166,31 +153,17 @@ namespace ImageProcessor
#endif
int start = ((y * this.Width) + x) * 4;
- this.Pixels[start + 0] = value.B;
+ this.Pixels[start + 0] = value.R;
this.Pixels[start + 1] = value.G;
- this.Pixels[start + 2] = value.R;
+ this.Pixels[start + 2] = value.B;
this.Pixels[start + 3] = value.A;
}
}
- ///
- /// Sets the pixel array of the image.
- ///
- ///
- /// The new width of the image. Must be greater than zero.
- /// The new height of the image. Must be greater than zero.
- ///
- /// The array with colors. Must be a multiple
- /// of four, width and height.
- ///
- ///
- /// Thrown if either or are less than or equal to 0.
- ///
- ///
- /// Thrown if the length is not equal to Width * Height * 4.
- ///
- public void SetPixels(int width, int height, byte[] pixels)
+ ///
+ public void SetPixels(int width, int height, float[] pixels)
{
+#if DEBUG
if (width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than or equals than zero.");
@@ -205,7 +178,7 @@ namespace ImageProcessor
{
throw new ArgumentException("Pixel array must have the length of Width * Height * 4.");
}
-
+#endif
this.Width = width;
this.Height = height;
this.Pixels = pixels;
diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj
index 77ea223ad..48294d14f 100644
--- a/src/ImageProcessor/ImageProcessor.csproj
+++ b/src/ImageProcessor/ImageProcessor.csproj
@@ -39,9 +39,13 @@
TRACE
prompt
4
+ bin\Release\ImageProcessor.XML
+ true
-
+
+
+
@@ -80,8 +84,8 @@
-
-
+
+
@@ -196,7 +200,7 @@
-
+
@@ -235,13 +239,14 @@
-
-
+
+
+