Browse Source

Clean up and refactor transforms

Former-commit-id: a271439562519bf789b9921757f7104ff36f48d4
Former-commit-id: 278c182399dc0dad003820a7d0add538fe9bd79b
Former-commit-id: 3485efba5a8e001e0015bc435a1052494167deb2
pull/17/head
James Jackson-South 10 years ago
parent
commit
597eada0fb
  1. 24
      src/ImageProcessor/Colors/Color.cs
  2. 31
      src/ImageProcessor/Colors/ColorSpacialTransforms.cs
  3. 270
      src/ImageProcessor/Colors/ColorTransforms.cs
  4. 285
      src/ImageProcessor/Colors/ColorspaceTransforms.cs
  5. 3
      src/ImageProcessor/Samplers/Resampler.cs

24
src/ImageProcessor/Colors/Color.cs

@ -325,30 +325,6 @@ namespace ImageProcessor
return new Color((first.backingVector + second.backingVector) * .5f);
}
/// <summary>
/// Linearly interpolates from one color to another based on the given amount.
/// </summary>
/// <param name="from">The first color value.</param>
/// <param name="to">The second color value.</param>
/// <param name="amount">
/// The weight value. At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
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;
}
/// <summary>
/// Compresses a linear color signal to its sRGB equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>

31
src/ImageProcessor/Colors/ColorSpacialTransforms.cs

@ -1,31 +0,0 @@
// <copyright file="ColorSpacialTransforms.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
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));
}
}
}

270
src/ImageProcessor/Colors/ColorTransforms.cs

@ -18,268 +18,56 @@ namespace ImageProcessor
public partial struct Color
{
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="Bgra32"/>.
/// Blends two colors by multiplication.
/// <remarks>
/// 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.
/// </remarks>
/// </summary>
/// <param name="color">The instance of <see cref="Color"/> to convert.</param>
/// <param name="source">The source color.</param>
/// <param name="destination">The destination color.</param>
/// <returns>
/// An instance of <see cref="Bgra32"/>.
/// The <see cref="Color"/>.
/// </returns>
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);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Cmyk"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="cmykColor">The instance of <see cref="Cmyk"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="YCbCr"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="YCbCr"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="CieXyz"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="CieXyz"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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));
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Hsv"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Hsv"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Hsl"/> to a
/// <see cref="Color"/>.
/// Linearly interpolates from one color to another based on the given amount.
/// </summary>
/// <param name="color">The instance of <see cref="Hsl"/> to convert.</param>
/// <param name="source">The first color value.</param>
/// <param name="destination">The second color value.</param>
/// <param name="amount">
/// The weight value. At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// The <see cref="Color"/>
/// </returns>
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);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="CieLab"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="cieLabColor">The instance of <see cref="CieLab"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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));
}
/// <summary>
/// Gets the color component from the given values.
/// </summary>
/// <param name="first">The first value.</param>
/// <param name="second">The second value.</param>
/// <param name="third">The third value.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
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;
}
/// <summary>
/// Moves the specific value within the acceptable range for
/// conversion.
/// <remarks>Used for converting <see cref="Hsl"/> colors to this type.</remarks>
/// </summary>
/// <param name="value">The value to shift.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
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;
}
}
}

285
src/ImageProcessor/Colors/ColorspaceTransforms.cs

@ -0,0 +1,285 @@
// <copyright file="ColorspaceTransforms.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
using System;
/// <summary>
/// Represents a four-component color using red, green, blue, and alpha data.
/// Each component is stored in premultiplied format multiplied by the alpha component.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public partial struct Color
{
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="Bgra32"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Color"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Bgra32"/>.
/// </returns>
public static implicit operator Color(Bgra32 color)
{
return new Color(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Cmyk"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="cmykColor">The instance of <see cref="Cmyk"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="YCbCr"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="YCbCr"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="CieXyz"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="CieXyz"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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));
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Hsv"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Hsv"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Hsl"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Hsl"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="CieLab"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="cieLabColor">The instance of <see cref="CieLab"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
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));
}
/// <summary>
/// Gets the color component from the given values.
/// </summary>
/// <param name="first">The first value.</param>
/// <param name="second">The second value.</param>
/// <param name="third">The third value.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
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;
}
/// <summary>
/// Moves the specific value within the acceptable range for
/// conversion.
/// <remarks>Used for converting <see cref="Hsl"/> colors to this type.</remarks>
/// </summary>
/// <param name="value">The value to shift.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float MoveIntoRange(float value)
{
if (value < 0.0)
{
value += 1.0f;
}
else if (value > 1.0)
{
value -= 1.0f;
}
return value;
}
}
}

3
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;
/// <summary>
/// Provides methods that allow the resampling of images using various algorithms.
/// </summary>

Loading…
Cancel
Save