mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 8596939ae841cee506be5327c7bba085d28c0453 Former-commit-id: 9e70e88253d02d7ffec313b8182c1d4ad2d3f6bc Former-commit-id: 805a8600f0577b4572533d44841e8c7b9079fc82af/merge-core
10 changed files with 527 additions and 126 deletions
@ -0,0 +1,229 @@ |
|||
// <copyright file="ColorTransforms.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="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 + (1 / 3f)); |
|||
g = GetColorComponent(temp1, temp2, rangedH); |
|||
b = GetColorComponent(temp1, temp2, rangedH - (1 / 3f)); |
|||
} |
|||
} |
|||
|
|||
return 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 < 1.0 / 6.0) |
|||
{ |
|||
return first + ((second - first) * 6.0f * third); |
|||
} |
|||
|
|||
if (third < 0.5) |
|||
{ |
|||
return second; |
|||
} |
|||
|
|||
if (third < 2.0 / 3.0) |
|||
{ |
|||
return first + ((second - first) * ((2.0f / 3.0f) - 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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,207 @@ |
|||
// <copyright file="Hsl.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; |
|||
using System.ComponentModel; |
|||
using System.Numerics; |
|||
|
|||
/// <summary>
|
|||
/// Represents a Hsl (hue, saturation, lightness) color.
|
|||
/// </summary>
|
|||
public struct Hsl : IEquatable<Hsl> |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a <see cref="Hsl"/> that has H, S, and L values set to zero.
|
|||
/// </summary>
|
|||
public static readonly Hsl Empty = default(Hsl); |
|||
|
|||
/// <summary>
|
|||
/// The epsilon for comparing floating point numbers.
|
|||
/// </summary>
|
|||
private const float Epsilon = 0.0001f; |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private Vector3 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Hsl"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="h">The h hue component.</param>
|
|||
/// <param name="s">The s saturation component.</param>
|
|||
/// <param name="l">The l value (lightness) component.</param>
|
|||
public Hsl(float h, float s, float l) |
|||
{ |
|||
this.backingVector.X = h.Clamp(0, 360); |
|||
this.backingVector.Y = s.Clamp(0, 1); |
|||
this.backingVector.Z = l.Clamp(0, 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the hue component.
|
|||
/// <remarks>A value ranging between 0 and 360.</remarks>
|
|||
/// </summary>
|
|||
public float H => this.backingVector.X; |
|||
|
|||
/// <summary>
|
|||
/// Gets the saturation component.
|
|||
/// <remarks>A value ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float S => this.backingVector.Y; |
|||
|
|||
/// <summary>
|
|||
/// Gets the lightness component.
|
|||
/// <remarks>A value ranging between 0 and 1.</remarks>
|
|||
/// </summary>
|
|||
public float L => this.backingVector.Z; |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this <see cref="Hsl"/> is empty.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IsEmpty => this.backingVector.Equals(default(Vector3)); |
|||
|
|||
/// <summary>
|
|||
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
|
|||
/// <see cref="Hsl"/>.
|
|||
/// </summary>
|
|||
/// <param name="color">The instance of <see cref="Color"/> to convert.</param>
|
|||
/// <returns>
|
|||
/// An instance of <see cref="Hsl"/>.
|
|||
/// </returns>
|
|||
public static implicit operator Hsl(Color color) |
|||
{ |
|||
color = Color.ToNonPremultiplied(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)); |
|||
float chroma = max - min; |
|||
float h = 0; |
|||
float s = 0; |
|||
float l = (max + min) / 2; |
|||
|
|||
if (Math.Abs(chroma) < Epsilon) |
|||
{ |
|||
return new Hsl(0, s, l); |
|||
} |
|||
|
|||
if (Math.Abs(r - max) < Epsilon) |
|||
{ |
|||
h = (g - b) / chroma; |
|||
} |
|||
else if (Math.Abs(g - max) < Epsilon) |
|||
{ |
|||
h = 2 + ((b - r) / chroma); |
|||
} |
|||
else if (Math.Abs(b - max) < Epsilon) |
|||
{ |
|||
h = 4 + ((r - g) / chroma); |
|||
} |
|||
|
|||
h *= 60; |
|||
if (h < 0.0) |
|||
{ |
|||
h += 360; |
|||
} |
|||
|
|||
if (l <= .5f) |
|||
{ |
|||
s = chroma / (max + min); |
|||
} |
|||
else { |
|||
s = chroma / (2 - chroma); |
|||
} |
|||
|
|||
return new Hsl(h, s, l); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Hsl"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Hsl"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Hsl"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(Hsl left, Hsl right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Hsl"/> objects for inequality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Hsl"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Hsl"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(Hsl left, Hsl right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
if (obj is Hsl) |
|||
{ |
|||
Hsl color = (Hsl)obj; |
|||
|
|||
return this.backingVector == color.backingVector; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return GetHashCode(this); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override string ToString() |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return "Hsl [ Empty ]"; |
|||
} |
|||
|
|||
return $"Hsl [ H={this.H:#0.##}, S={this.S:#0.##}, L={this.L:#0.##} ]"; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public bool Equals(Hsl other) |
|||
{ |
|||
return this.backingVector.Equals(other.backingVector); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this instance.
|
|||
/// </summary>
|
|||
/// <param name="color">
|
|||
/// The instance of <see cref="Hsl"/> to return the hash code for.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// A 32-bit signed integer that is the hash code for this instance.
|
|||
/// </returns>
|
|||
private static int GetHashCode(Hsl color) => color.backingVector.GetHashCode(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue