diff --git a/src/ImageProcessor/Colors/Color.cs b/src/ImageProcessor/Colors/Color.cs index abd3e8620..44ed036e3 100644 --- a/src/ImageProcessor/Colors/Color.cs +++ b/src/ImageProcessor/Colors/Color.cs @@ -325,30 +325,6 @@ namespace ImageProcessor 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); - - if (Math.Abs(from.A - 1) < Epsilon && Math.Abs(to.A - 1) < Epsilon) - { - return from + (to - from) * amount; - } - - // Premultiplied. - return from * (1 - amount) + to; - } - /// /// Compresses a linear color signal to its sRGB equivalent. /// diff --git a/src/ImageProcessor/Colors/ColorSpacialTransforms.cs b/src/ImageProcessor/Colors/ColorSpacialTransforms.cs deleted file mode 100644 index 6a49708bf..000000000 --- a/src/ImageProcessor/Colors/ColorSpacialTransforms.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageProcessor -{ - using System.Numerics; - - public partial struct Color - { - public static Color Multiply(Color source, Color destination) - { - if (destination == Color.Black) - { - return Color.Black; - } - if (destination == Color.White) - { - return source; - } - return - new Color( - new Vector4( - source.backingVector.X * destination.backingVector.X, - source.backingVector.Y * destination.backingVector.Y, - source.backingVector.Z * destination.backingVector.Z, - source.backingVector.W * destination.backingVector.W)); - } - } -} diff --git a/src/ImageProcessor/Colors/ColorTransforms.cs b/src/ImageProcessor/Colors/ColorTransforms.cs index 6bd42c254..43bc03251 100644 --- a/src/ImageProcessor/Colors/ColorTransforms.cs +++ b/src/ImageProcessor/Colors/ColorTransforms.cs @@ -18,268 +18,56 @@ namespace ImageProcessor public partial struct Color { /// - /// Allows the implicit conversion of an instance of to a - /// . + /// Blends two colors by multiplication. + /// + /// The source color is multiplied by the destination color and replaces the destination. + /// The resultant color is always at least as dark as either the source or destination color. + /// Multiplying any color with black results in black. Multiplying any color with white preserves the + /// original color. + /// /// - /// The instance of to convert. + /// The source color. + /// The destination color. /// - /// An instance of . + /// The . /// - public static implicit operator Color(Bgra32 color) + public static Color Multiply(Color source, Color destination) { - 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(CieXyz color) - { - float x = color.X / 100F; - float y = color.Y / 100F; - float z = color.Z / 100F; - - // Then XYZ to RGB (multiplication by 100 was done above already) - float r = (x * 3.2406F) + (y * -1.5372F) + (z * -0.4986F); - float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F); - float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F); - - return Color.Compress(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) + if (destination == Color.Black) { - return new Color(v, v, v, 1); + return Color.Black; } - 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) + if (destination == Color.White) { - 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 source; } - return new Color(r, g, b); + return new Color(source.backingVector * destination.backingVector); } /// - /// Allows the implicit conversion of an instance of to a - /// . + /// Linearly interpolates from one color to another based on the given amount. /// - /// The instance of to convert. + /// The first color value. + /// The second color value. + /// + /// The weight value. At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// /// - /// An instance of . + /// The /// - public static implicit operator Color(Hsl color) + public static Color Lerp(Color source, Color destination, float amount) { - float rangedH = color.H / 360f; - float r = 0; - float g = 0; - float b = 0; - float s = color.S; - float l = color.L; + amount = amount.Clamp(0f, 1f); - if (Math.Abs(l) > Epsilon) - { - if (Math.Abs(s) < Epsilon) - { - r = g = b = l; - } - else - { - float temp2 = (l < 0.5f) ? l * (1f + s) : l + s - (l * s); - float temp1 = (2f * l) - temp2; - - r = GetColorComponent(temp1, temp2, rangedH + 0.3333333F); - g = GetColorComponent(temp1, temp2, rangedH); - b = GetColorComponent(temp1, temp2, rangedH - 0.3333333F); - } - } - - 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(CieLab cieLabColor) - { - // First convert back to XYZ... - float y = (cieLabColor.L + 16F) / 116F; - float x = (cieLabColor.A / 500F) + y; - float z = y - (cieLabColor.B / 200F); - - float x3 = x * x * x; - float y3 = y * y * y; - float z3 = z * z * z; - - x = x3 > 0.008856F ? x3 : (x - 0.137931F) / 7.787F; - y = (cieLabColor.L > 7.999625F) ? y3 : (cieLabColor.L / 903.3F); - z = (z3 > 0.008856F) ? z3 : (z - 0.137931F) / 7.787F; - - x *= 0.95047F; - z *= 1.08883F; - - // Then XYZ to RGB (multiplication by 100 was done above already) - float r = (x * 3.2406F) + (y * -1.5372F) + (z * -0.4986F); - float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F); - float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F); - - return Color.Compress(new Color(r, g, b)); - } - - /// - /// Gets the color component from the given values. - /// - /// The first value. - /// The second value. - /// The third value. - /// - /// The . - /// - private static float GetColorComponent(float first, float second, float third) - { - third = MoveIntoRange(third); - if (third < 0.1666667F) - { - return first + ((second - first) * 6.0f * third); - } - - if (third < 0.5) - { - return second; - } - - if (third < 0.6666667F) - { - return first + ((second - first) * (0.6666667F - third) * 6.0f); - } - - return first; - } - - /// - /// Moves the specific value within the acceptable range for - /// conversion. - /// Used for converting colors to this type. - /// - /// The value to shift. - /// - /// The . - /// - private static float MoveIntoRange(float value) - { - if (value < 0.0) - { - value += 1.0f; - } - else if (value > 1.0) + if (Math.Abs(source.A - 1) < Epsilon && Math.Abs(destination.A - 1) < Epsilon) { - value -= 1.0f; + return source + ((destination - source) * amount); } - return value; + // Premultiplied. + return (source * (1 - amount)) + destination; } } } diff --git a/src/ImageProcessor/Colors/ColorspaceTransforms.cs b/src/ImageProcessor/Colors/ColorspaceTransforms.cs new file mode 100644 index 000000000..334c967fb --- /dev/null +++ b/src/ImageProcessor/Colors/ColorspaceTransforms.cs @@ -0,0 +1,285 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor +{ + using System; + + /// + /// Represents a four-component color using red, green, blue, and alpha data. + /// Each component is stored in premultiplied format multiplied by the alpha component. + /// + /// + /// 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 partial struct Color + { + /// + /// 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(CieXyz color) + { + float x = color.X / 100F; + float y = color.Y / 100F; + float z = color.Z / 100F; + + // Then XYZ to RGB (multiplication by 100 was done above already) + float r = (x * 3.2406F) + (y * -1.5372F) + (z * -0.4986F); + float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F); + float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F); + + return Color.Compress(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); + } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// The instance of to convert. + /// + /// An instance of . + /// + public static implicit operator Color(Hsl color) + { + float rangedH = color.H / 360f; + float r = 0; + float g = 0; + float b = 0; + float s = color.S; + float l = color.L; + + if (Math.Abs(l) > Epsilon) + { + if (Math.Abs(s) < Epsilon) + { + r = g = b = l; + } + else + { + float temp2 = (l < 0.5f) ? l * (1f + s) : l + s - (l * s); + float temp1 = (2f * l) - temp2; + + r = GetColorComponent(temp1, temp2, rangedH + 0.3333333F); + g = GetColorComponent(temp1, temp2, rangedH); + b = GetColorComponent(temp1, temp2, rangedH - 0.3333333F); + } + } + + 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(CieLab cieLabColor) + { + // First convert back to XYZ... + float y = (cieLabColor.L + 16F) / 116F; + float x = (cieLabColor.A / 500F) + y; + float z = y - (cieLabColor.B / 200F); + + float x3 = x * x * x; + float y3 = y * y * y; + float z3 = z * z * z; + + x = x3 > 0.008856F ? x3 : (x - 0.137931F) / 7.787F; + y = (cieLabColor.L > 7.999625F) ? y3 : (cieLabColor.L / 903.3F); + z = (z3 > 0.008856F) ? z3 : (z - 0.137931F) / 7.787F; + + x *= 0.95047F; + z *= 1.08883F; + + // Then XYZ to RGB (multiplication by 100 was done above already) + float r = (x * 3.2406F) + (y * -1.5372F) + (z * -0.4986F); + float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F); + float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F); + + return Color.Compress(new Color(r, g, b)); + } + + /// + /// Gets the color component from the given values. + /// + /// The first value. + /// The second value. + /// The third value. + /// + /// The . + /// + private static float GetColorComponent(float first, float second, float third) + { + third = MoveIntoRange(third); + if (third < 0.1666667F) + { + return first + ((second - first) * 6.0f * third); + } + + if (third < 0.5) + { + return second; + } + + if (third < 0.6666667F) + { + return first + ((second - first) * (0.6666667F - third) * 6.0f); + } + + return first; + } + + /// + /// Moves the specific value within the acceptable range for + /// conversion. + /// Used for converting colors to this type. + /// + /// The value to shift. + /// + /// The . + /// + private static float MoveIntoRange(float value) + { + if (value < 0.0) + { + value += 1.0f; + } + else if (value > 1.0) + { + value -= 1.0f; + } + + return value; + } + } +} diff --git a/src/ImageProcessor/Samplers/Resampler.cs b/src/ImageProcessor/Samplers/Resampler.cs index a36d68b36..4fd105f16 100644 --- a/src/ImageProcessor/Samplers/Resampler.cs +++ b/src/ImageProcessor/Samplers/Resampler.cs @@ -7,11 +7,8 @@ namespace ImageProcessor.Samplers { using System; using System.Collections.Generic; - using System.Numerics; using System.Threading.Tasks; - using ImageProcessor.Filters; - /// /// Provides methods that allow the resampling of images using various algorithms. ///