mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
236 changed files with 26515 additions and 1606 deletions
@ -1,129 +1,129 @@ |
|||||
// <copyright file="DrawImage.cs" company="James Jackson-South">
|
// <copyright file="DrawImage.cs" company="James Jackson-South">
|
||||
// Copyright (c) James Jackson-South and contributors.
|
// Copyright (c) James Jackson-South and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
// </copyright>
|
||||
|
|
||||
namespace ImageSharp |
namespace ImageSharp |
||||
{ |
{ |
||||
using Drawing.Processors; |
using Drawing.Processors; |
||||
using ImageSharp.PixelFormats; |
using ImageSharp.PixelFormats; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Extension methods for the <see cref="Image{TPixel}"/> type.
|
/// Extension methods for the <see cref="Image{TPixel}"/> type.
|
||||
/// </summary>
|
/// </summary>
|
||||
public static partial class ImageExtensions |
public static partial class ImageExtensions |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Draws the given image together with the current one by blending their pixels.
|
/// Draws the given image together with the current one by blending their pixels.
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <param name="source">The image this method extends.</param>
|
/// <param name="source">The image this method extends.</param>
|
||||
/// <param name="image">The image to blend with the currently processing image.</param>
|
/// <param name="image">The image to blend with the currently processing image.</param>
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
/// <param name="size">The size to draw the blended image.</param>
|
/// <param name="size">The size to draw the blended image.</param>
|
||||
/// <param name="location">The location to draw the blended image.</param>
|
/// <param name="location">The location to draw the blended image.</param>
|
||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options.</param>
|
||||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, Size size, Point location, GraphicsOptions options) |
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, Size size, Point location, GraphicsOptions options) |
||||
where TPixel : struct, IPixel<TPixel> |
where TPixel : struct, IPixel<TPixel> |
||||
{ |
{ |
||||
if (size == default(Size)) |
if (size == default(Size)) |
||||
{ |
{ |
||||
size = new Size(image.Width, image.Height); |
size = new Size(image.Width, image.Height); |
||||
} |
} |
||||
|
|
||||
if (location == default(Point)) |
if (location == default(Point)) |
||||
{ |
{ |
||||
location = Point.Empty; |
location = Point.Empty; |
||||
} |
} |
||||
|
|
||||
source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, size, location, options), source.Bounds); |
source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, size, location, options), source.Bounds); |
||||
return source; |
return source; |
||||
} |
} |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Draws the given image together with the current one by blending their pixels.
|
/// Draws the given image together with the current one by blending their pixels.
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
/// <param name="source">The image this method extends.</param>
|
/// <param name="source">The image this method extends.</param>
|
||||
/// <param name="image">The image to blend with the currently processing image.</param>
|
/// <param name="image">The image to blend with the currently processing image.</param>
|
||||
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
|
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
|
||||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, float percent) |
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, float percent) |
||||
where TPixel : struct, IPixel<TPixel> |
where TPixel : struct, IPixel<TPixel> |
||||
{ |
{ |
||||
GraphicsOptions options = GraphicsOptions.Default; |
GraphicsOptions options = GraphicsOptions.Default; |
||||
options.BlendPercentage = percent; |
options.BlendPercentage = percent; |
||||
return DrawImage(source, image, default(Size), default(Point), options); |
return DrawImage(source, image, default(Size), default(Point), options); |
||||
} |
} |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Draws the given image together with the current one by blending their pixels.
|
/// Draws the given image together with the current one by blending their pixels.
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
/// <param name="source">The image this method extends.</param>
|
/// <param name="source">The image this method extends.</param>
|
||||
/// <param name="image">The image to blend with the currently processing image.</param>
|
/// <param name="image">The image to blend with the currently processing image.</param>
|
||||
/// <param name="blender">The blending mode.</param>
|
/// <param name="blender">The blending mode.</param>
|
||||
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
|
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
|
||||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float percent) |
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float percent) |
||||
where TPixel : struct, IPixel<TPixel> |
where TPixel : struct, IPixel<TPixel> |
||||
{ |
{ |
||||
GraphicsOptions options = GraphicsOptions.Default; |
GraphicsOptions options = GraphicsOptions.Default; |
||||
options.BlendPercentage = percent; |
options.BlendPercentage = percent; |
||||
options.BlenderMode = blender; |
options.BlenderMode = blender; |
||||
return DrawImage(source, image, default(Size), default(Point), options); |
return DrawImage(source, image, default(Size), default(Point), options); |
||||
} |
} |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Draws the given image together with the current one by blending their pixels.
|
/// Draws the given image together with the current one by blending their pixels.
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
/// <param name="source">The image this method extends.</param>
|
/// <param name="source">The image this method extends.</param>
|
||||
/// <param name="image">The image to blend with the currently processing image.</param>
|
/// <param name="image">The image to blend with the currently processing image.</param>
|
||||
/// <param name="options">The options, including the blending type and belnding amount.</param>
|
/// <param name="options">The options, including the blending type and belnding amount.</param>
|
||||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, GraphicsOptions options) |
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, GraphicsOptions options) |
||||
where TPixel : struct, IPixel<TPixel> |
where TPixel : struct, IPixel<TPixel> |
||||
{ |
{ |
||||
return DrawImage(source, image, default(Size), default(Point), options); |
return DrawImage(source, image, default(Size), default(Point), options); |
||||
} |
} |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Draws the given image together with the current one by blending their pixels.
|
/// Draws the given image together with the current one by blending their pixels.
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <param name="source">The image this method extends.</param>
|
/// <param name="source">The image this method extends.</param>
|
||||
/// <param name="image">The image to blend with the currently processing image.</param>
|
/// <param name="image">The image to blend with the currently processing image.</param>
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
|
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
|
||||
/// <param name="size">The size to draw the blended image.</param>
|
/// <param name="size">The size to draw the blended image.</param>
|
||||
/// <param name="location">The location to draw the blended image.</param>
|
/// <param name="location">The location to draw the blended image.</param>
|
||||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, float percent, Size size, Point location) |
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, float percent, Size size, Point location) |
||||
where TPixel : struct, IPixel<TPixel> |
where TPixel : struct, IPixel<TPixel> |
||||
{ |
{ |
||||
GraphicsOptions options = GraphicsOptions.Default; |
GraphicsOptions options = GraphicsOptions.Default; |
||||
options.BlendPercentage = percent; |
options.BlendPercentage = percent; |
||||
return source.DrawImage(image, size, location, options); |
return source.DrawImage(image, size, location, options); |
||||
} |
} |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Draws the given image together with the current one by blending their pixels.
|
/// Draws the given image together with the current one by blending their pixels.
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <param name="source">The image this method extends.</param>
|
/// <param name="source">The image this method extends.</param>
|
||||
/// <param name="image">The image to blend with the currently processing image.</param>
|
/// <param name="image">The image to blend with the currently processing image.</param>
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
/// <param name="blender">The type of bending to apply.</param>
|
/// <param name="blender">The type of bending to apply.</param>
|
||||
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
|
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
|
||||
/// <param name="size">The size to draw the blended image.</param>
|
/// <param name="size">The size to draw the blended image.</param>
|
||||
/// <param name="location">The location to draw the blended image.</param>
|
/// <param name="location">The location to draw the blended image.</param>
|
||||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float percent, Size size, Point location) |
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float percent, Size size, Point location) |
||||
where TPixel : struct, IPixel<TPixel> |
where TPixel : struct, IPixel<TPixel> |
||||
{ |
{ |
||||
GraphicsOptions options = GraphicsOptions.Default; |
GraphicsOptions options = GraphicsOptions.Default; |
||||
options.BlenderMode = blender; |
options.BlenderMode = blender; |
||||
options.BlendPercentage = percent; |
options.BlendPercentage = percent; |
||||
return source.DrawImage(image, size, location, options); |
return source.DrawImage(image, size, location, options); |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,247 @@ |
|||||
|
// <copyright file="CieLch.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color.
|
||||
|
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC"/>
|
||||
|
/// </summary>
|
||||
|
internal struct CieLch : IColorVector, IEquatable<CieLch>, IAlmostEquatable<CieLch, float> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// D50 standard illuminant.
|
||||
|
/// Used when reference white is not specified explicitly.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a <see cref="CieLch"/> that has L, C, H values set to zero.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieLch Empty = default(CieLch); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The backing vector for SIMD support.
|
||||
|
/// </summary>
|
||||
|
private readonly Vector3 backingVector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLch"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">The lightness dimension.</param>
|
||||
|
/// <param name="c">The chroma, relative saturation.</param>
|
||||
|
/// <param name="h">The hue in degrees.</param>
|
||||
|
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLch(float l, float c, float h) |
||||
|
: this(new Vector3(l, c, h), DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLch"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">The lightness dimension.</param>
|
||||
|
/// <param name="c">The chroma, relative saturation.</param>
|
||||
|
/// <param name="h">The hue in degrees.</param>
|
||||
|
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLch(float l, float c, float h, CieXyz whitePoint) |
||||
|
: this(new Vector3(l, c, h), whitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLch"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l, c, h components.</param>
|
||||
|
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLch(Vector3 vector) |
||||
|
: this(vector, DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLch"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l, c, h components.</param>
|
||||
|
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLch(Vector3 vector, CieXyz whitePoint) |
||||
|
: this() |
||||
|
{ |
||||
|
this.backingVector = vector; |
||||
|
this.WhitePoint = whitePoint; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the reference white point of this color
|
||||
|
/// </summary>
|
||||
|
public CieXyz WhitePoint |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the lightness dimension.
|
||||
|
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
|
||||
|
/// </summary>
|
||||
|
public float L |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.X; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the a chroma component.
|
||||
|
/// <remarks>A value ranging from 0 to 100.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float C |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the h° hue component in degrees.
|
||||
|
/// <remarks>A value ranging from 0 to 360.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float H |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Z; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this <see cref="CieLch"/> is empty.
|
||||
|
/// </summary>
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public bool IsEmpty => this.Equals(Empty); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Vector3 Vector |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieLch"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieLch"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieLch"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(CieLch left, CieLch right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieLch"/> objects for inequality
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieLch"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieLch"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(CieLch left, CieLch right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
unchecked |
||||
|
{ |
||||
|
int hashCode = this.WhitePoint.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ this.backingVector.GetHashCode(); |
||||
|
return hashCode; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsEmpty) |
||||
|
{ |
||||
|
return "CieLch [Empty]"; |
||||
|
} |
||||
|
|
||||
|
return $"CieLch [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}]"; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is CieLch) |
||||
|
{ |
||||
|
return this.Equals((CieLch)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool Equals(CieLch other) |
||||
|
{ |
||||
|
return this.backingVector.Equals(other.backingVector) |
||||
|
&& this.WhitePoint.Equals(other.WhitePoint); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool AlmostEquals(CieLch other, float precision) |
||||
|
{ |
||||
|
var result = Vector3.Abs(this.backingVector - other.backingVector); |
||||
|
|
||||
|
return this.WhitePoint.Equals(other.WhitePoint) |
||||
|
&& result.X <= precision |
||||
|
&& result.Y <= precision |
||||
|
&& result.Z <= precision; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the saturation of the color (chroma normalized by lightness)
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// A value ranging from 0 to 100.
|
||||
|
/// </remarks>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Saturation() |
||||
|
{ |
||||
|
float result = 100 * (this.C / this.L); |
||||
|
|
||||
|
if (float.IsNaN(result)) |
||||
|
{ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,247 @@ |
|||||
|
// <copyright file="CieLchuv.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color.
|
||||
|
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CieLchuv_or_CIEHLC"/>
|
||||
|
/// </summary>
|
||||
|
internal struct CieLchuv : IColorVector, IEquatable<CieLchuv>, IAlmostEquatable<CieLchuv, float> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// D50 standard illuminant.
|
||||
|
/// Used when reference white is not specified explicitly.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a <see cref="CieLchuv"/> that has L, C, H values set to zero.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieLchuv Empty = default(CieLchuv); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The backing vector for SIMD support.
|
||||
|
/// </summary>
|
||||
|
private readonly Vector3 backingVector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLchuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">The lightness dimension.</param>
|
||||
|
/// <param name="c">The chroma, relative saturation.</param>
|
||||
|
/// <param name="h">The hue in degrees.</param>
|
||||
|
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLchuv(float l, float c, float h) |
||||
|
: this(new Vector3(l, c, h), DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLchuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">The lightness dimension.</param>
|
||||
|
/// <param name="c">The chroma, relative saturation.</param>
|
||||
|
/// <param name="h">The hue in degrees.</param>
|
||||
|
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLchuv(float l, float c, float h, CieXyz whitePoint) |
||||
|
: this(new Vector3(l, c, h), whitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLchuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l, c, h components.</param>
|
||||
|
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLchuv(Vector3 vector) |
||||
|
: this(vector, DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLchuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l, c, h components.</param>
|
||||
|
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLchuv(Vector3 vector, CieXyz whitePoint) |
||||
|
: this() |
||||
|
{ |
||||
|
this.backingVector = vector; |
||||
|
this.WhitePoint = whitePoint; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the reference white point of this color
|
||||
|
/// </summary>
|
||||
|
public CieXyz WhitePoint |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the lightness dimension.
|
||||
|
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
|
||||
|
/// </summary>
|
||||
|
public float L |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.X; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the a chroma component.
|
||||
|
/// <remarks>A value ranging from 0 to 100.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float C |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the h° hue component in degrees.
|
||||
|
/// <remarks>A value ranging from 0 to 360.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float H |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Z; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this <see cref="CieLchuv"/> is empty.
|
||||
|
/// </summary>
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public bool IsEmpty => this.Equals(Empty); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Vector3 Vector |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieLchuv"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieLchuv"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieLchuv"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(CieLchuv left, CieLchuv right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieLchuv"/> objects for inequality
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieLchuv"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieLchuv"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(CieLchuv left, CieLchuv right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
unchecked |
||||
|
{ |
||||
|
int hashCode = this.WhitePoint.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ this.backingVector.GetHashCode(); |
||||
|
return hashCode; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsEmpty) |
||||
|
{ |
||||
|
return "CieLchuv [Empty]"; |
||||
|
} |
||||
|
|
||||
|
return $"CieLchuv [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}]"; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is CieLchuv) |
||||
|
{ |
||||
|
return this.Equals((CieLchuv)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool Equals(CieLchuv other) |
||||
|
{ |
||||
|
return this.backingVector.Equals(other.backingVector) |
||||
|
&& this.WhitePoint.Equals(other.WhitePoint); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool AlmostEquals(CieLchuv other, float precision) |
||||
|
{ |
||||
|
var result = Vector3.Abs(this.backingVector - other.backingVector); |
||||
|
|
||||
|
return this.WhitePoint.Equals(other.WhitePoint) |
||||
|
&& result.X <= precision |
||||
|
&& result.Y <= precision |
||||
|
&& result.Z <= precision; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the saturation of the color (chroma normalized by lightness)
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// A value ranging from 0 to 100.
|
||||
|
/// </remarks>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Saturation() |
||||
|
{ |
||||
|
float result = 100 * (this.C / this.L); |
||||
|
|
||||
|
if (float.IsNaN(result)) |
||||
|
{ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,229 @@ |
|||||
|
// <copyright file="CieLuv.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The CIE 1976 (L*, u*, v*) color space, commonly known by its abbreviation CIELUV, is a color space adopted by the International
|
||||
|
/// Commission on Illumination (CIE) in 1976, as a simple-to-compute transformation of the 1931 CIE XYZ color space, but which
|
||||
|
/// attempted perceptual uniformity
|
||||
|
/// <see href="https://en.wikipedia.org/wiki/CIELUV"/>
|
||||
|
/// </summary>
|
||||
|
internal struct CieLuv : IColorVector, IEquatable<CieLuv>, IAlmostEquatable<CieLuv, float> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// D65 standard illuminant.
|
||||
|
/// Used when reference white is not specified explicitly.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a <see cref="CieLuv"/> that has L, U, and V values set to zero.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieLuv Empty = default(CieLuv); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The backing vector for SIMD support.
|
||||
|
/// </summary>
|
||||
|
private readonly Vector3 backingVector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">The lightness dimension.</param>
|
||||
|
/// <param name="u">The blue-yellow chromaticity coordinate of the given whitepoint.</param>
|
||||
|
/// <param name="v">The red-green chromaticity coordinate of the given whitepoint.</param>
|
||||
|
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv(float l, float u, float v) |
||||
|
: this(new Vector3(l, u, v), DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">The lightness dimension.</param>
|
||||
|
/// <param name="u">The blue-yellow chromaticity coordinate of the given whitepoint.</param>
|
||||
|
/// <param name="v">The red-green chromaticity coordinate of the given whitepoint.</param>
|
||||
|
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv(float l, float u, float v, CieXyz whitePoint) |
||||
|
: this(new Vector3(l, u, v), whitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l, u, v components.</param>
|
||||
|
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv(Vector3 vector) |
||||
|
: this(vector, DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieLuv"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l, u, v components.</param>
|
||||
|
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv(Vector3 vector, CieXyz whitePoint) |
||||
|
: this() |
||||
|
{ |
||||
|
this.backingVector = vector; |
||||
|
this.WhitePoint = whitePoint; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the reference white point of this color
|
||||
|
/// </summary>
|
||||
|
public CieXyz WhitePoint |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the lightness dimension
|
||||
|
/// <remarks>A value usually ranging between 0 and 100.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float L |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.X; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue-yellow chromaticity coordinate of the given whitepoint.
|
||||
|
/// <remarks>A value usually ranging between -100 and 100.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float U |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red-green chromaticity coordinate of the given whitepoint.
|
||||
|
/// <remarks>A value usually ranging between -100 and 100.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float V |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Z; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this <see cref="CieLuv"/> is empty.
|
||||
|
/// </summary>
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public bool IsEmpty => this.Equals(Empty); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Vector3 Vector |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieLuv"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieLuv"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieLuv"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(CieLuv left, CieLuv right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieLuv"/> objects for inequality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieLuv"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieLuv"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(CieLuv left, CieLuv right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
unchecked |
||||
|
{ |
||||
|
int hashCode = this.WhitePoint.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ this.backingVector.GetHashCode(); |
||||
|
return hashCode; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsEmpty) |
||||
|
{ |
||||
|
return "CieLuv [ Empty ]"; |
||||
|
} |
||||
|
|
||||
|
return $"CieLuv [ L={this.L:#0.##}, U={this.U:#0.##}, V={this.V:#0.##} ]"; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is CieLuv) |
||||
|
{ |
||||
|
return this.Equals((CieLuv)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool Equals(CieLuv other) |
||||
|
{ |
||||
|
return this.backingVector.Equals(other.backingVector) |
||||
|
&& this.WhitePoint.Equals(other.WhitePoint); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool AlmostEquals(CieLuv other, float precision) |
||||
|
{ |
||||
|
var result = Vector3.Abs(this.backingVector - other.backingVector); |
||||
|
|
||||
|
return this.WhitePoint.Equals(other.WhitePoint) |
||||
|
&& result.X <= precision |
||||
|
&& result.Y <= precision |
||||
|
&& result.Z <= precision; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,161 @@ |
|||||
|
// <copyright file="CieXyChromaticityCoordinates.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents the coordinates of CIEXY chromaticity space
|
||||
|
/// </summary>
|
||||
|
internal struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates>, IAlmostEquatable<CieXyChromaticityCoordinates, float> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents a <see cref="CieXyChromaticityCoordinates"/> that has X, Y values set to zero.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyChromaticityCoordinates Empty = default(CieXyChromaticityCoordinates); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The backing vector for SIMD support.
|
||||
|
/// </summary>
|
||||
|
private readonly Vector2 backingVector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">Chromaticity coordinate x (usually from 0 to 1)</param>
|
||||
|
/// <param name="y">Chromaticity coordinate y (usually from 0 to 1)</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyChromaticityCoordinates(float x, float y) |
||||
|
: this(new Vector2(x, y)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector containing the XY Chromaticity coordinates</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyChromaticityCoordinates(Vector2 vector) |
||||
|
{ |
||||
|
this.backingVector = vector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chromaticity X-coordinate.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Ranges usually from 0 to 1.
|
||||
|
/// </remarks>
|
||||
|
public float X |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.X; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chromaticity Y-coordinate
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Ranges usually from 0 to 1.
|
||||
|
/// </remarks>
|
||||
|
public float Y |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this <see cref="CieXyChromaticityCoordinates"/> is empty.
|
||||
|
/// </summary>
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public bool IsEmpty => this.Equals(Empty); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieXyChromaticityCoordinates"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for inequality
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieXyChromaticityCoordinates"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return this.backingVector.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsEmpty) |
||||
|
{ |
||||
|
return "CieXyChromaticityCoordinates [Empty]"; |
||||
|
} |
||||
|
|
||||
|
return $"CieXyChromaticityCoordinates [ X={this.X:#0.##}, Y={this.Y:#0.##}]"; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is CieXyChromaticityCoordinates) |
||||
|
{ |
||||
|
return this.Equals((CieXyChromaticityCoordinates)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool Equals(CieXyChromaticityCoordinates other) |
||||
|
{ |
||||
|
return this.backingVector.Equals(other.backingVector); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool AlmostEquals(CieXyChromaticityCoordinates other, float precision) |
||||
|
{ |
||||
|
var result = Vector2.Abs(this.backingVector - other.backingVector); |
||||
|
|
||||
|
return result.X <= precision |
||||
|
&& result.Y <= precision; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,179 @@ |
|||||
|
// <copyright file="CieXyy.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents an CIE xyY 1931 color
|
||||
|
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space"/>
|
||||
|
/// </summary>
|
||||
|
internal struct CieXyy : IColorVector, IEquatable<CieXyy>, IAlmostEquatable<CieXyy, float> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents a <see cref="CieXyy"/> that has X, Y, and Y values set to zero.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyy Empty = default(CieXyy); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The backing vector for SIMD support.
|
||||
|
/// </summary>
|
||||
|
private readonly Vector3 backingVector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyy"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">The x chroma component.</param>
|
||||
|
/// <param name="y">The y chroma component.</param>
|
||||
|
/// <param name="yl">The y luminance component.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyy(float x, float y, float yl) |
||||
|
: this(new Vector3(x, y, yl)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyy"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the x, y, Y components.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyy(Vector3 vector) |
||||
|
: this() |
||||
|
{ |
||||
|
// Not clamping as documentation about this space seems to indicate "usual" ranges
|
||||
|
this.backingVector = vector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the X chrominance component.
|
||||
|
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float X |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.X; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the Y chrominance component.
|
||||
|
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float Y |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the Y luminance component.
|
||||
|
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float Yl |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Z; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this <see cref="CieXyy"/> is empty.
|
||||
|
/// </summary>
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public bool IsEmpty => this.Equals(Empty); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Vector3 Vector |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieXyy"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieXyy"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieXyy"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(CieXyy left, CieXyy right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieXyy"/> objects for inequality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieXyy"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieXyy"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(CieXyy left, CieXyy right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return this.backingVector.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsEmpty) |
||||
|
{ |
||||
|
return "CieXyy [ Empty ]"; |
||||
|
} |
||||
|
|
||||
|
return $"CieXyy [ X={this.X:#0.##}, Y={this.Y:#0.##}, Yl={this.Yl:#0.##} ]"; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is CieXyy) |
||||
|
{ |
||||
|
return this.Equals((CieXyy)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool Equals(CieXyy other) |
||||
|
{ |
||||
|
return this.backingVector.Equals(other.backingVector); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool AlmostEquals(CieXyy other, float precision) |
||||
|
{ |
||||
|
var result = Vector3.Abs(this.backingVector - other.backingVector); |
||||
|
|
||||
|
return result.X <= precision |
||||
|
&& result.Y <= precision |
||||
|
&& result.Z <= precision; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// <copyright file="CieConstants.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Constants use for Cie conversion calculations
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html"/>
|
||||
|
/// </summary>
|
||||
|
internal static class CieConstants |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 216F / 24389F
|
||||
|
/// </summary>
|
||||
|
public const float Epsilon = 0.008856452F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 24389F / 27F
|
||||
|
/// </summary>
|
||||
|
public const float Kappa = 903.2963F; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,198 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Performs chromatic adaptation on the various color spaces.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Performs chromatic adaptation of given <see cref="CieXyz"/> color.
|
||||
|
/// Target white point is <see cref="WhitePoint"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to adapt</param>
|
||||
|
/// <param name="sourceWhitePoint">The white point to adapt for</param>
|
||||
|
/// <returns>The adapted color</returns>
|
||||
|
public CieXyz Adapt(CieXyz color, CieXyz sourceWhitePoint) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
Guard.NotNull(sourceWhitePoint, nameof(sourceWhitePoint)); |
||||
|
|
||||
|
if (!this.IsChromaticAdaptationPerformed) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); |
||||
|
} |
||||
|
|
||||
|
return this.ChromaticAdaptation.Transform(color, sourceWhitePoint, this.WhitePoint); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adapts <see cref="CieLab"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to adapt</param>
|
||||
|
/// <returns>The adapted color</returns>
|
||||
|
public CieLab Adapt(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
if (!this.IsChromaticAdaptationPerformed) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); |
||||
|
} |
||||
|
|
||||
|
if (color.WhitePoint.Equals(this.TargetLabWhitePoint)) |
||||
|
{ |
||||
|
return color; |
||||
|
} |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adapts <see cref="CieLch"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to adapt</param>
|
||||
|
/// <returns>The adapted color</returns>
|
||||
|
public CieLch Adapt(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
if (!this.IsChromaticAdaptationPerformed) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); |
||||
|
} |
||||
|
|
||||
|
if (color.WhitePoint.Equals(this.TargetLabWhitePoint)) |
||||
|
{ |
||||
|
return color; |
||||
|
} |
||||
|
|
||||
|
CieLab labColor = this.ToCieLab(color); |
||||
|
return this.ToCieLch(labColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adapts <see cref="CieLchuv"/> color from the source white point to white point set in <see cref="TargetLabWhitePoint"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to adapt</param>
|
||||
|
/// <returns>The adapted color</returns>
|
||||
|
public CieLchuv Adapt(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
if (!this.IsChromaticAdaptationPerformed) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); |
||||
|
} |
||||
|
|
||||
|
if (color.WhitePoint.Equals(this.TargetLabWhitePoint)) |
||||
|
{ |
||||
|
return color; |
||||
|
} |
||||
|
|
||||
|
CieLuv luvColor = this.ToCieLuv(color); |
||||
|
return this.ToCieLchuv(luvColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adapts <see cref="CieLuv"/> color from the source white point to white point set in <see cref="TargetLuvWhitePoint"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to adapt</param>
|
||||
|
/// <returns>The adapted color</returns>
|
||||
|
public CieLuv Adapt(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
if (!this.IsChromaticAdaptationPerformed) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); |
||||
|
} |
||||
|
|
||||
|
if (color.WhitePoint.Equals(this.TargetLuvWhitePoint)) |
||||
|
{ |
||||
|
return color; |
||||
|
} |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adapts <see cref="HunterLab"/> color from the source white point to white point set in <see cref="TargetHunterLabWhitePoint"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to adapt</param>
|
||||
|
/// <returns>The adapted color</returns>
|
||||
|
public HunterLab Adapt(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
if (!this.IsChromaticAdaptationPerformed) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); |
||||
|
} |
||||
|
|
||||
|
if (color.WhitePoint.Equals(this.TargetHunterLabWhitePoint)) |
||||
|
{ |
||||
|
return color; |
||||
|
} |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adapts a <see cref="LinearRgb"/> color from the source working space to working space set in <see cref="TargetRgbWorkingSpace"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to adapt</param>
|
||||
|
/// <returns>The adapted color</returns>
|
||||
|
public LinearRgb Adapt(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
if (!this.IsChromaticAdaptationPerformed) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); |
||||
|
} |
||||
|
|
||||
|
if (color.WorkingSpace.Equals(this.TargetRgbWorkingSpace)) |
||||
|
{ |
||||
|
return color; |
||||
|
} |
||||
|
|
||||
|
// Conversion to XYZ
|
||||
|
LinearRgbToCieXyzConverter converterToXYZ = this.GetLinearRgbToCieXyzConverter(color.WorkingSpace); |
||||
|
CieXyz unadapted = converterToXYZ.Convert(color); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieXyz adapted = this.ChromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint); |
||||
|
|
||||
|
// Conversion back to RGB
|
||||
|
CieXyzToLinearRgbConverter converterToRGB = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace); |
||||
|
return converterToRGB.Convert(adapted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adapts an <see cref="Rgb"/> color from the source working space to working space set in <see cref="TargetRgbWorkingSpace"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to adapt</param>
|
||||
|
/// <returns>The adapted color</returns>
|
||||
|
public Rgb Adapt(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
LinearRgb linearInput = this.ToLinearRgb(color); |
||||
|
LinearRgb linearOutput = this.Adapt(linearInput); |
||||
|
return this.ToRgb(linearOutput); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,205 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.CieLab; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.CieLch; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="CieLab"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The converter for converting between CieLch to CieLab.
|
||||
|
/// </summary>
|
||||
|
private static readonly CieLchToCieLabConverter CieLchToCieLabConverter = new CieLchToCieLabConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion (perserving white point)
|
||||
|
CieLab unadapted = CieLchToCieLabConverter.Convert(color); |
||||
|
|
||||
|
if (!this.IsChromaticAdaptationPerformed) |
||||
|
{ |
||||
|
return unadapted; |
||||
|
} |
||||
|
|
||||
|
// Adaptation
|
||||
|
return this.Adapt(unadapted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed |
||||
|
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint) |
||||
|
: color; |
||||
|
|
||||
|
// Conversion
|
||||
|
CieXyzToCieLabConverter converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint); |
||||
|
return converter.Convert(adapted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLab ToCieLab(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLab(xyzColor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,192 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.CieLch; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="CieLch"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The converter for converting between CieLab to CieLch.
|
||||
|
/// </summary>
|
||||
|
private static readonly CieLabToCieLchConverter CieLabToCieLchConverter = new CieLabToCieLchConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieLab adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color; |
||||
|
|
||||
|
// Conversion
|
||||
|
return CieLabToCieLchConverter.Convert(adapted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieLab labColor = this.ToCieLab(color); |
||||
|
return this.ToCieLch(labColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLch"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLch"/></returns>
|
||||
|
public CieLch ToCieLch(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLch(xyzColor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,192 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="CieLchuv"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The converter for converting between CieLab to CieLchuv.
|
||||
|
/// </summary>
|
||||
|
private static readonly CieLuvToCieLchuvConverter CieLuvToCieLchuvConverter = new CieLuvToCieLchuvConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieLuv adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color; |
||||
|
|
||||
|
// Conversion
|
||||
|
return CieLuvToCieLchuvConverter.Convert(adapted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieLab labColor = this.ToCieLab(color); |
||||
|
return this.ToCieLchuv(labColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLchuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLchuv"/></returns>
|
||||
|
public CieLchuv ToCieLchuv(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
CieXyz xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLchuv(xyzColor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,202 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="CieLuv"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
private static readonly CieLchuvToCieLuvConverter CieLchuvToCieLuvConverter = new CieLchuvToCieLuvConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLab"/></returns>
|
||||
|
public CieLuv ToCieLuv(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion (perserving white point)
|
||||
|
CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color); |
||||
|
|
||||
|
if (!this.IsChromaticAdaptationPerformed) |
||||
|
{ |
||||
|
return unadapted; |
||||
|
} |
||||
|
|
||||
|
// Adaptation
|
||||
|
return this.Adapt(unadapted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed |
||||
|
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint) |
||||
|
: color; |
||||
|
|
||||
|
// Conversion
|
||||
|
var converter = new CieXyzToCieLuvConverter(this.TargetLuvWhitePoint); |
||||
|
return converter.Convert(adapted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="CieLuv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieLuv"/></returns>
|
||||
|
public CieLuv ToCieLuv(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToCieLuv(xyzColor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,197 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.CieXyy; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="CieXyy"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
private static readonly CieXyzAndCieXyyConverter CieXyzAndCieXyyConverter = new CieXyzAndCieXyyConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
return CieXyzAndCieXyyConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="CieXyy"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyy"/></returns>
|
||||
|
public CieXyy ToCieXyy(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCieXyy(xyzColor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,253 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.CieLab; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="CieXyz"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter(); |
||||
|
|
||||
|
private static readonly CieLuvToCieXyzConverter CieLuvToCieXyzConverter = new CieLuvToCieXyzConverter(); |
||||
|
|
||||
|
private static readonly HunterLabToCieXyzConverter HunterLabToCieXyzConverter = new HunterLabToCieXyzConverter(); |
||||
|
|
||||
|
private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
CieXyz unadapted = CieLabToCieXyzConverter.Convert(color); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed |
||||
|
? unadapted |
||||
|
: this.Adapt(unadapted, color.WhitePoint); |
||||
|
|
||||
|
return adapted; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion to Lab
|
||||
|
CieLab labColor = CieLchToCieLabConverter.Convert(color); |
||||
|
|
||||
|
// Conversion to XYZ (incl. adaptation)
|
||||
|
return this.ToCieXyz(labColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion to Luv
|
||||
|
CieLuv luvColor = CieLchuvToCieLuvConverter.Convert(color); |
||||
|
|
||||
|
// Conversion to XYZ (incl. adaptation)
|
||||
|
return this.ToCieXyz(luvColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
CieXyz unadapted = CieLuvToCieXyzConverter.Convert(color); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed |
||||
|
? unadapted |
||||
|
: this.Adapt(unadapted, color.WhitePoint); |
||||
|
|
||||
|
return adapted; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
return CieXyzAndCieXyyConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return this.ToCieXyz(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return this.ToCieXyz(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return this.ToCieXyz(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed |
||||
|
? unadapted |
||||
|
: this.Adapt(unadapted, color.WhitePoint); |
||||
|
|
||||
|
return adapted; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
LinearRgbToCieXyzConverter converter = this.GetLinearRgbToCieXyzConverter(color.WorkingSpace); |
||||
|
CieXyz unadapted = converter.Convert(color); |
||||
|
|
||||
|
// Adaptation
|
||||
|
return color.WorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed |
||||
|
? unadapted |
||||
|
: this.Adapt(unadapted, color.WorkingSpace.WhitePoint); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
return this.cachedCieXyzAndLmsConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
LinearRgb linear = RgbToLinearRgbConverter.Convert(color); |
||||
|
return this.ToCieXyz(linear); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="CieXyz"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
public CieXyz ToCieXyz(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return this.ToCieXyz(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the correct converter for the given rgb working space.
|
||||
|
/// </summary>
|
||||
|
/// <param name="workingSpace">The source working space</param>
|
||||
|
/// <returns>The <see cref="LinearRgbToCieXyzConverter"/></returns>
|
||||
|
private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(IRgbWorkingSpace workingSpace) |
||||
|
{ |
||||
|
if (this.linearRgbToCieXyzConverter != null && this.linearRgbToCieXyzConverter.SourceWorkingSpace.Equals(workingSpace)) |
||||
|
{ |
||||
|
return this.linearRgbToCieXyzConverter; |
||||
|
} |
||||
|
|
||||
|
return this.linearRgbToCieXyzConverter = new LinearRgbToCieXyzConverter(workingSpace); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,198 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Cmyk; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="Cmyk"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCmyk(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCmyk(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCmyk(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCmyk(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCmyk(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return CmykAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return CmykAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return CmykAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCmyk(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return CmykAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToCmyk(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
return CmykAndRgbConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="Cmyk"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Cmyk"/></returns>
|
||||
|
public Cmyk ToCmyk(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return CmykAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,198 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Hsl; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="Hsl"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
private static readonly HslAndRgbConverter HslAndRgbConverter = new HslAndRgbConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsl(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsl(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsl(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsl(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsl(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return HslAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return HslAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return HslAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsl(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return HslAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsl(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
return HslAndRgbConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="Hsl"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsl"/></returns>
|
||||
|
public Hsl ToHsl(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return HslAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,198 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Hsv; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="Hsv"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
private static readonly HsvAndRgbConverter HsvAndRgbConverter = new HsvAndRgbConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return HsvAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return HsvAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return HsvAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return HsvAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToHsv(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
return HsvAndRgbConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="Hsv"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Hsv"/></returns>
|
||||
|
public Hsv ToHsv(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return HsvAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,189 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="HunterLab"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieXyz adapted = !this.WhitePoint.Equals(this.TargetHunterLabWhitePoint) && this.IsChromaticAdaptationPerformed |
||||
|
? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetHunterLabWhitePoint) |
||||
|
: color; |
||||
|
|
||||
|
// Conversion
|
||||
|
return new CieXyzToHunterLabConverter(this.TargetHunterLabWhitePoint).Convert(adapted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="HunterLab"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="HunterLab"/></returns>
|
||||
|
public HunterLab ToHunterLab(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToHunterLab(xyzColor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,209 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="LinearRgb"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter(); |
||||
|
|
||||
|
private CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLinearRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLinearRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLinearRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLinearRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLinearRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Adaptation
|
||||
|
CieXyz adapted = this.TargetRgbWorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed |
||||
|
? color |
||||
|
: this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint); |
||||
|
|
||||
|
// Conversion
|
||||
|
CieXyzToLinearRgbConverter xyzConverter = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace); |
||||
|
return xyzConverter.Convert(adapted); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
return this.ToLinearRgb(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
return this.ToLinearRgb(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
return this.ToLinearRgb(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLinearRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLinearRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
return RgbToLinearRgbConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="LinearRgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="LinearRgb"/></returns>
|
||||
|
public LinearRgb ToLinearRgb(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
return this.ToLinearRgb(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the correct converter for the given rgb working space.
|
||||
|
/// </summary>
|
||||
|
/// <param name="workingSpace">The target working space</param>
|
||||
|
/// <returns>The <see cref="CieXyzToLinearRgbConverter"/></returns>
|
||||
|
private CieXyzToLinearRgbConverter GetCieXyxToLinearRgbConverter(IRgbWorkingSpace workingSpace) |
||||
|
{ |
||||
|
if (this.cieXyzToLinearRgbConverter != null && this.cieXyzToLinearRgbConverter.TargetWorkingSpace.Equals(workingSpace)) |
||||
|
{ |
||||
|
return this.cieXyzToLinearRgbConverter; |
||||
|
} |
||||
|
|
||||
|
return this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(workingSpace); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,184 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="Lms"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
return this.cachedCieXyzAndLmsConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="Lms"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Lms"/></returns>
|
||||
|
public Lms ToLms(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToLms(xyzColor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,193 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="Rgb"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
var linear = this.ToLinearRgb(color); |
||||
|
|
||||
|
// Compand
|
||||
|
return this.ToRgb(linear); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
return CmykAndRgbConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
return HsvAndRgbConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
return HslAndRgbConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
return LinearRgbToRgbConverter.Convert(color); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
return this.ToRgb(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="YCbCr"/> into a <see cref="Rgb"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="Rgb"/></returns>
|
||||
|
public Rgb ToRgb(YCbCr color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
// Conversion
|
||||
|
Rgb rgb = YCbCrAndRgbConverter.Convert(color); |
||||
|
|
||||
|
// Adaptation
|
||||
|
// TODO: Check this!
|
||||
|
return rgb.WorkingSpace.Equals(this.TargetRgbWorkingSpace) ? rgb : this.Adapt(rgb); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,198 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.YCbCr; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Allows conversion to <see cref="YCbCr"/>.
|
||||
|
/// </content>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLab"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(CieLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToYCbCr(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLch"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(CieLch color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToYCbCr(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLchuv"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(CieLchuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToYCbCr(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieLuv"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(CieLuv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToYCbCr(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyy"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(CieXyy color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToYCbCr(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="CieXyz"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(CieXyz color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return YCbCrAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Cmyk"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(Cmyk color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return YCbCrAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsl"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(Hsl color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return YCbCrAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Hsv"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(Hsv color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return YCbCrAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="HunterLab"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(HunterLab color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToYCbCr(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="LinearRgb"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(LinearRgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var rgb = this.ToRgb(color); |
||||
|
|
||||
|
return YCbCrAndRgbConverter.Convert(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Lms"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(Lms color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
var xyzColor = this.ToCieXyz(color); |
||||
|
|
||||
|
return this.ToYCbCr(xyzColor); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="Rgb"/> into a <see cref="YCbCr"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">The color to convert.</param>
|
||||
|
/// <returns>The <see cref="YCbCr"/></returns>
|
||||
|
public YCbCr ToYCbCr(Rgb color) |
||||
|
{ |
||||
|
Guard.NotNull(color, nameof(color)); |
||||
|
|
||||
|
return YCbCrAndRgbConverter.Convert(color); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,104 @@ |
|||||
|
// <copyright file="ColorSpaceConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Lms; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts between color spaces ensuring that the color is adapted using chromatic adaptation.
|
||||
|
/// </summary>
|
||||
|
internal partial class ColorSpaceConverter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The default whitepoint used for converting to CieLab
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; |
||||
|
|
||||
|
private Matrix4x4 transformationMatrix; |
||||
|
|
||||
|
private CieXyzAndLmsConverter cachedCieXyzAndLmsConverter; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ColorSpaceConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
public ColorSpaceConverter() |
||||
|
{ |
||||
|
// Note the order here this is important.
|
||||
|
this.WhitePoint = DefaultWhitePoint; |
||||
|
this.LmsAdaptationMatrix = CieXyzAndLmsConverter.DefaultTransformationMatrix; |
||||
|
this.ChromaticAdaptation = new VonKriesChromaticAdaptation(this.cachedCieXyzAndLmsConverter); |
||||
|
this.TargetLuvWhitePoint = CieLuv.DefaultWhitePoint; |
||||
|
this.TargetLabWhitePoint = CieLab.DefaultWhitePoint; |
||||
|
this.TargetHunterLabWhitePoint = HunterLab.DefaultWhitePoint; |
||||
|
this.TargetRgbWorkingSpace = Rgb.DefaultWorkingSpace; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space.
|
||||
|
/// When null, no adaptation will be performed.
|
||||
|
/// </summary>
|
||||
|
public CieXyz WhitePoint { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information)
|
||||
|
/// Defaults to: <see cref="CieLuv.DefaultWhitePoint"/>.
|
||||
|
/// </summary>
|
||||
|
public CieXyz TargetLuvWhitePoint { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information)
|
||||
|
/// Defaults to: <see cref="CieLab.DefaultWhitePoint"/>.
|
||||
|
/// </summary>
|
||||
|
public CieXyz TargetLabWhitePoint { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information)
|
||||
|
/// Defaults to: <see cref="HunterLab.DefaultWhitePoint"/>.
|
||||
|
/// </summary>
|
||||
|
public CieXyz TargetHunterLabWhitePoint { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information)
|
||||
|
/// Defaults to: <see cref="Rgb.DefaultWorkingSpace"/>.
|
||||
|
/// </summary>
|
||||
|
public IRgbWorkingSpace TargetRgbWorkingSpace { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed.
|
||||
|
/// </summary>
|
||||
|
public IChromaticAdaptation ChromaticAdaptation { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets transformation matrix used in conversion to <see cref="Lms"/>,
|
||||
|
/// also used in the default Von Kries Chromatic Adaptation method.
|
||||
|
/// </summary>
|
||||
|
public Matrix4x4 LmsAdaptationMatrix |
||||
|
{ |
||||
|
get => this.transformationMatrix; |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
this.transformationMatrix = value; |
||||
|
if (this.cachedCieXyzAndLmsConverter == null) |
||||
|
{ |
||||
|
this.cachedCieXyzAndLmsConverter = new CieXyzAndLmsConverter(value); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.cachedCieXyzAndLmsConverter.TransformationMatrix = value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether chromatic adaptation has been performed.
|
||||
|
/// </summary>
|
||||
|
private bool IsChromaticAdaptationPerformed => this.ChromaticAdaptation != null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
// <copyright file="IChromaticAdaptation.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Chromatic adaptation.
|
||||
|
/// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M]
|
||||
|
/// which is dependent on the source reference white (XWS, YWS, ZWS) and the destination reference white (XWD, YWD, ZWD).
|
||||
|
/// </summary>
|
||||
|
internal interface IChromaticAdaptation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Performs a linear transformation of a source color in to the destination color.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates).</remarks>
|
||||
|
/// <param name="sourceColor">The source color.</param>
|
||||
|
/// <param name="sourceWhitePoint">The source white point.</param>
|
||||
|
/// <param name="targetWhitePoint">The target white point.</param>
|
||||
|
/// <returns>The <see cref="CieXyz"/></returns>
|
||||
|
CieXyz Transform(CieXyz sourceColor, CieXyz sourceWhitePoint, CieXyz targetWhitePoint); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
// <copyright file="IColorConversion.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converts color between two color spaces.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The input color type.</typeparam>
|
||||
|
/// <typeparam name="TResult">The result color type.</typeparam>
|
||||
|
internal interface IColorConversion<in T, out TResult> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Performs the conversion from the input to an instance of the output type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">The input color instance.</param>
|
||||
|
/// <returns>The converted result</returns>
|
||||
|
TResult Convert(T input); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
// <copyright file="CieLabToCieXyzConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLab |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts from <see cref="CieLab"/> to <see cref="CieXyz"/>.
|
||||
|
/// </summary>
|
||||
|
internal class CieLabToCieXyzConverter : IColorConversion<CieLab, CieXyz> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyz Convert(CieLab input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
|
||||
|
float l = input.L, a = input.A, b = input.B; |
||||
|
float fy = (l + 16) / 116F; |
||||
|
float fx = (a / 500F) + fy; |
||||
|
float fz = fy - (b / 200F); |
||||
|
|
||||
|
float fx3 = MathF.Pow(fx, 3F); |
||||
|
float fz3 = MathF.Pow(fz, 3F); |
||||
|
|
||||
|
float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa; |
||||
|
float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? MathF.Pow((l + 16F) / 116F, 3F) : l / CieConstants.Kappa; |
||||
|
float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa; |
||||
|
|
||||
|
float wx = input.WhitePoint.X, wy = input.WhitePoint.Y, wz = input.WhitePoint.Z; |
||||
|
|
||||
|
// Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white)
|
||||
|
xr = xr.Clamp(0, 1F); |
||||
|
yr = yr.Clamp(0, 1F); |
||||
|
zr = zr.Clamp(0, 1F); |
||||
|
|
||||
|
float x = xr * wx; |
||||
|
float y = yr * wy; |
||||
|
float z = zr * wz; |
||||
|
|
||||
|
return new CieXyz(x, y, z); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
// <copyright file="CieXyzToCieLabConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLab |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts from <see cref="CieXyz"/> to <see cref="CieLab"/>.
|
||||
|
/// </summary>
|
||||
|
internal class CieXyzToCieLabConverter : IColorConversion<CieXyz, CieLab> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyzToCieLabConverter() |
||||
|
: this(CieLab.DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzToCieLabConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="labWhitePoint">The target reference lab white point</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyzToCieLabConverter(CieXyz labWhitePoint) |
||||
|
{ |
||||
|
this.LabWhitePoint = labWhitePoint; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the target reference whitepoint. When not set, <see cref="CieLab.DefaultWhitePoint"/> is used.
|
||||
|
/// </summary>
|
||||
|
public CieXyz LabWhitePoint |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLab Convert(CieXyz input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
|
||||
|
float wx = this.LabWhitePoint.X, wy = this.LabWhitePoint.Y, wz = this.LabWhitePoint.Z; |
||||
|
|
||||
|
float xr = input.X / wx, yr = input.Y / wy, zr = input.Z / wz; |
||||
|
|
||||
|
float fx = xr > CieConstants.Epsilon ? MathF.Pow(xr, 0.3333333F) : ((CieConstants.Kappa * xr) + 16F) / 116F; |
||||
|
float fy = yr > CieConstants.Epsilon ? MathF.Pow(yr, 0.3333333F) : ((CieConstants.Kappa * yr) + 16F) / 116F; |
||||
|
float fz = zr > CieConstants.Epsilon ? MathF.Pow(zr, 0.3333333F) : ((CieConstants.Kappa * zr) + 16F) / 116F; |
||||
|
|
||||
|
float l = (116F * fy) - 16F; |
||||
|
float a = 500F * (fx - fy); |
||||
|
float b = 200F * (fy - fz); |
||||
|
|
||||
|
return new CieLab(l, a, b, this.LabWhitePoint); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
// <copyright file="CieLchToCieLabConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
|
||||
|
/// </summary>
|
||||
|
internal class CieLchToCieLabConverter : IColorConversion<CieLch, CieLab> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLab Convert(CieLch input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here:
|
||||
|
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
|
||||
|
float l = input.L, c = input.C, hDegrees = input.H; |
||||
|
float hRadians = MathF.DegreeToRadian(hDegrees); |
||||
|
|
||||
|
float a = c * MathF.Cos(hRadians); |
||||
|
float b = c * MathF.Sin(hRadians); |
||||
|
|
||||
|
return new CieLab(l, a, b, input.WhitePoint); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
// <copyright file="CieLabToCieLchConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
|
||||
|
/// </summary>
|
||||
|
internal class CieLabToCieLchConverter : IColorConversion<CieLab, CieLch> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLch Convert(CieLab input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here:
|
||||
|
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
|
||||
|
float l = input.L, a = input.A, b = input.B; |
||||
|
float c = MathF.Sqrt((a * a) + (b * b)); |
||||
|
float hRadians = MathF.Atan2(b, a); |
||||
|
float hDegrees = MathF.RadianToDegree(hRadians); |
||||
|
|
||||
|
// Wrap the angle round at 360.
|
||||
|
hDegrees = hDegrees % 360; |
||||
|
|
||||
|
// Make sure it's not negative.
|
||||
|
while (hDegrees < 0) |
||||
|
{ |
||||
|
hDegrees += 360; |
||||
|
} |
||||
|
|
||||
|
return new CieLch(l, c, hDegrees, input.WhitePoint); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
// <copyright file="CieLchuvToCieLuvConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
|
||||
|
/// </summary>
|
||||
|
internal class CieLchuvToCieLuvConverter : IColorConversion<CieLchuv, CieLuv> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv Convert(CieLchuv input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here:
|
||||
|
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
|
||||
|
float l = input.L, c = input.C, hDegrees = input.H; |
||||
|
float hRadians = MathF.DegreeToRadian(hDegrees); |
||||
|
|
||||
|
float u = c * MathF.Cos(hRadians); |
||||
|
float v = c * MathF.Sin(hRadians); |
||||
|
|
||||
|
return new CieLuv(l, u, v, input.WhitePoint); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
// <copyright file="CieLuvToCieLchuvConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
|
||||
|
/// </summary>
|
||||
|
internal class CieLuvToCieLchuvConverter : IColorConversion<CieLuv, CieLchuv> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLchuv Convert(CieLuv input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here:
|
||||
|
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
|
||||
|
float l = input.L, a = input.U, b = input.V; |
||||
|
float c = MathF.Sqrt((a * a) + (b * b)); |
||||
|
float hRadians = MathF.Atan2(b, a); |
||||
|
float hDegrees = MathF.RadianToDegree(hRadians); |
||||
|
|
||||
|
// Wrap the angle round at 360.
|
||||
|
hDegrees = hDegrees % 360; |
||||
|
|
||||
|
// Make sure it's not negative.
|
||||
|
while (hDegrees < 0) |
||||
|
{ |
||||
|
hDegrees += 360; |
||||
|
} |
||||
|
|
||||
|
return new CieLchuv(l, c, hDegrees, input.WhitePoint); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
// <copyright file="CieLuvToCieXyzConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts from <see cref="CieLuv"/> to <see cref="CieXyz"/>.
|
||||
|
/// </summary>
|
||||
|
internal class CieLuvToCieXyzConverter : IColorConversion<CieLuv, CieXyz> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyz Convert(CieLuv input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html
|
||||
|
float l = input.L, u = input.U, v = input.V; |
||||
|
|
||||
|
float u0 = ComputeU0(input.WhitePoint); |
||||
|
float v0 = ComputeV0(input.WhitePoint); |
||||
|
|
||||
|
float y = l > CieConstants.Kappa * CieConstants.Epsilon |
||||
|
? MathF.Pow((l + 16) / 116, 3) |
||||
|
: l / CieConstants.Kappa; |
||||
|
|
||||
|
float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3; |
||||
|
float b = -5 * y; |
||||
|
float c = -0.3333333F; |
||||
|
float d = y * ((39 * l / (v + (13 * l * v0))) - 5); |
||||
|
|
||||
|
float x = (d - b) / (a - c); |
||||
|
float z = (x * a) + b; |
||||
|
|
||||
|
if (float.IsNaN(x) || x < 0) |
||||
|
{ |
||||
|
x = 0; |
||||
|
} |
||||
|
|
||||
|
if (float.IsNaN(y) || y < 0) |
||||
|
{ |
||||
|
y = 0; |
||||
|
} |
||||
|
|
||||
|
if (float.IsNaN(z) || z < 0) |
||||
|
{ |
||||
|
z = 0; |
||||
|
} |
||||
|
|
||||
|
return new CieXyz(x, y, z); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the blue-yellow chromacity based on the given whitepoint.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">The whitepoint</param>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static float ComputeU0(CieXyz input) |
||||
|
{ |
||||
|
return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the red-green chromacity based on the given whitepoint.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">The whitepoint</param>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static float ComputeV0(CieXyz input) |
||||
|
{ |
||||
|
return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,102 @@ |
|||||
|
// <copyright file="CieXyzToCieLuvConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts from <see cref="CieXyz"/> to <see cref="CieLuv"/>.
|
||||
|
/// </summary>
|
||||
|
internal class CieXyzToCieLuvConverter : IColorConversion<CieXyz, CieLuv> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyzToCieLuvConverter() |
||||
|
: this(CieLuv.DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzToCieLuvConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="luvWhitePoint">The target reference luv white point</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyzToCieLuvConverter(CieXyz luvWhitePoint) |
||||
|
{ |
||||
|
this.LuvWhitePoint = luvWhitePoint; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the target reference whitepoint. When not set, <see cref="CieLuv.DefaultWhitePoint"/> is used.
|
||||
|
/// </summary>
|
||||
|
public CieXyz LuvWhitePoint |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieLuv Convert(CieXyz input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html
|
||||
|
float yr = input.Y / this.LuvWhitePoint.Y; |
||||
|
float up = ComputeUp(input); |
||||
|
float vp = ComputeVp(input); |
||||
|
float upr = ComputeUp(this.LuvWhitePoint); |
||||
|
float vpr = ComputeVp(this.LuvWhitePoint); |
||||
|
|
||||
|
float l = yr > CieConstants.Epsilon ? ((116 * MathF.Pow(yr, 0.3333333F)) - 16F) : (CieConstants.Kappa * yr); |
||||
|
|
||||
|
if (float.IsNaN(l) || l < 0) |
||||
|
{ |
||||
|
l = 0; |
||||
|
} |
||||
|
|
||||
|
float u = 13 * l * (up - upr); |
||||
|
float v = 13 * l * (vp - vpr); |
||||
|
|
||||
|
if (float.IsNaN(u)) |
||||
|
{ |
||||
|
u = 0; |
||||
|
} |
||||
|
|
||||
|
if (float.IsNaN(v)) |
||||
|
{ |
||||
|
v = 0; |
||||
|
} |
||||
|
|
||||
|
return new CieLuv(l, u, v, this.LuvWhitePoint); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the blue-yellow chromacity based on the given whitepoint.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">The whitepoint</param>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static float ComputeUp(CieXyz input) |
||||
|
{ |
||||
|
return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the red-green chromacity based on the given whitepoint.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">The whitepoint</param>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
private static float ComputeVp(CieXyz input) |
||||
|
{ |
||||
|
return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
// <copyright file="CieXyzAndCieXyyConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieXyy |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between CIE XYZ and CIE xyY
|
||||
|
/// <see href="http://www.brucelindbloom.com/"/> for formulas.
|
||||
|
/// </summary>
|
||||
|
internal class CieXyzAndCieXyyConverter : IColorConversion<CieXyz, CieXyy>, IColorConversion<CieXyy, CieXyz> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyy Convert(CieXyz input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
float x = input.X / (input.X + input.Y + input.Z); |
||||
|
float y = input.Y / (input.X + input.Y + input.Z); |
||||
|
|
||||
|
if (float.IsNaN(x) || float.IsNaN(y)) |
||||
|
{ |
||||
|
return new CieXyy(0, 0, input.Y); |
||||
|
} |
||||
|
|
||||
|
return new CieXyy(x, y, input.Y); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyz Convert(CieXyy input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
if (MathF.Abs(input.Y) < Constants.Epsilon) |
||||
|
{ |
||||
|
return new CieXyz(0, 0, input.Yl); |
||||
|
} |
||||
|
|
||||
|
float x = (input.X * input.Yl) / input.Y; |
||||
|
float y = input.Yl; |
||||
|
float z = ((1 - input.X - input.Y) * y) / input.Y; |
||||
|
|
||||
|
return new CieXyz(x, y, z); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
// <copyright file="CmykAndRgbConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Cmyk |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between CMYK and Rgb
|
||||
|
/// </summary>
|
||||
|
internal class CmykAndRgbConverter : IColorConversion<Cmyk, Rgb>, IColorConversion<Rgb, Cmyk> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Rgb Convert(Cmyk input) |
||||
|
{ |
||||
|
float r = (1F - input.C) * (1F - input.K); |
||||
|
float g = (1F - input.M) * (1F - input.K); |
||||
|
float b = (1F - input.Y) * (1F - input.K); |
||||
|
|
||||
|
return new Rgb(r, g, b); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Cmyk Convert(Rgb input) |
||||
|
{ |
||||
|
// To CMYK
|
||||
|
float c = 1F - input.R; |
||||
|
float m = 1F - input.G; |
||||
|
float y = 1F - input.B; |
||||
|
|
||||
|
// To CMYK
|
||||
|
float k = MathF.Min(c, MathF.Min(m, y)); |
||||
|
|
||||
|
if (MathF.Abs(k - 1F) < Constants.Epsilon) |
||||
|
{ |
||||
|
return new Cmyk(0, 0, 0, 1F); |
||||
|
} |
||||
|
|
||||
|
c = (c - k) / (1F - k); |
||||
|
m = (m - k) / (1F - k); |
||||
|
y = (y - k) / (1F - k); |
||||
|
|
||||
|
return new Cmyk(c, m, y, k); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,159 @@ |
|||||
|
// <copyright file="HslAndRgbConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Hsl |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between HSL and Rgb
|
||||
|
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
|
||||
|
/// </summary>
|
||||
|
internal class HslAndRgbConverter : IColorConversion<Hsl, Rgb>, IColorConversion<Rgb, Hsl> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Rgb Convert(Hsl input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
float rangedH = input.H / 360F; |
||||
|
float r = 0; |
||||
|
float g = 0; |
||||
|
float b = 0; |
||||
|
float s = input.S; |
||||
|
float l = input.L; |
||||
|
|
||||
|
if (MathF.Abs(l) > Constants.Epsilon) |
||||
|
{ |
||||
|
if (MathF.Abs(s) < Constants.Epsilon) |
||||
|
{ |
||||
|
r = g = b = l; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
float temp2 = (l < .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 Rgb(r, g, b); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Hsl Convert(Rgb input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
float r = input.R; |
||||
|
float g = input.G; |
||||
|
float b = input.B; |
||||
|
|
||||
|
float max = MathF.Max(r, MathF.Max(g, b)); |
||||
|
float min = MathF.Min(r, MathF.Min(g, b)); |
||||
|
float chroma = max - min; |
||||
|
float h = 0F; |
||||
|
float s = 0F; |
||||
|
float l = (max + min) / 2F; |
||||
|
|
||||
|
if (MathF.Abs(chroma) < Constants.Epsilon) |
||||
|
{ |
||||
|
return new Hsl(0F, s, l); |
||||
|
} |
||||
|
|
||||
|
if (MathF.Abs(r - max) < Constants.Epsilon) |
||||
|
{ |
||||
|
h = (g - b) / chroma; |
||||
|
} |
||||
|
else if (MathF.Abs(g - max) < Constants.Epsilon) |
||||
|
{ |
||||
|
h = 2F + ((b - r) / chroma); |
||||
|
} |
||||
|
else if (MathF.Abs(b - max) < Constants.Epsilon) |
||||
|
{ |
||||
|
h = 4F + ((r - g) / chroma); |
||||
|
} |
||||
|
|
||||
|
h *= 60F; |
||||
|
if (h < 0F) |
||||
|
{ |
||||
|
h += 360F; |
||||
|
} |
||||
|
|
||||
|
if (l <= .5F) |
||||
|
{ |
||||
|
s = chroma / (max + min); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
s = chroma / (2F - chroma); |
||||
|
} |
||||
|
|
||||
|
return new Hsl(h, s, l); |
||||
|
} |
||||
|
|
||||
|
/// <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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static float GetColorComponent(float first, float second, float third) |
||||
|
{ |
||||
|
third = MoveIntoRange(third); |
||||
|
if (third < 0.1666667F) |
||||
|
{ |
||||
|
return first + ((second - first) * 6F * third); |
||||
|
} |
||||
|
|
||||
|
if (third < .5F) |
||||
|
{ |
||||
|
return second; |
||||
|
} |
||||
|
|
||||
|
if (third < 0.6666667F) |
||||
|
{ |
||||
|
return first + ((second - first) * (0.6666667F - third) * 6F); |
||||
|
} |
||||
|
|
||||
|
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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private static float MoveIntoRange(float value) |
||||
|
{ |
||||
|
if (value < 0F) |
||||
|
{ |
||||
|
value += 1F; |
||||
|
} |
||||
|
else if (value > 1F) |
||||
|
{ |
||||
|
value -= 1F; |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,130 @@ |
|||||
|
// <copyright file="HsvAndRgbConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Hsv |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between HSV and Rgb
|
||||
|
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
|
||||
|
/// </summary>
|
||||
|
internal class HsvAndRgbConverter : IColorConversion<Hsv, Rgb>, IColorConversion<Rgb, Hsv> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Rgb Convert(Hsv input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
float s = input.S; |
||||
|
float v = input.V; |
||||
|
|
||||
|
if (MathF.Abs(s) < Constants.Epsilon) |
||||
|
{ |
||||
|
return new Rgb(v, v, v); |
||||
|
} |
||||
|
|
||||
|
float h = (MathF.Abs(input.H - 360) < Constants.Epsilon) ? 0 : input.H / 60; |
||||
|
int i = (int)Math.Truncate(h); |
||||
|
float f = h - i; |
||||
|
|
||||
|
float p = v * (1F - s); |
||||
|
float q = v * (1F - (s * f)); |
||||
|
float t = v * (1F - (s * (1F - 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 Rgb(r, g, b); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Hsv Convert(Rgb input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
float r = input.R; |
||||
|
float g = input.G; |
||||
|
float b = input.B; |
||||
|
|
||||
|
float max = MathF.Max(r, MathF.Max(g, b)); |
||||
|
float min = MathF.Min(r, MathF.Min(g, b)); |
||||
|
float chroma = max - min; |
||||
|
float h = 0; |
||||
|
float s = 0; |
||||
|
float v = max; |
||||
|
|
||||
|
if (MathF.Abs(chroma) < Constants.Epsilon) |
||||
|
{ |
||||
|
return new Hsv(0, s, v); |
||||
|
} |
||||
|
|
||||
|
if (MathF.Abs(r - max) < Constants.Epsilon) |
||||
|
{ |
||||
|
h = (g - b) / chroma; |
||||
|
} |
||||
|
else if (MathF.Abs(g - max) < Constants.Epsilon) |
||||
|
{ |
||||
|
h = 2 + ((b - r) / chroma); |
||||
|
} |
||||
|
else if (MathF.Abs(b - max) < Constants.Epsilon) |
||||
|
{ |
||||
|
h = 4 + ((r - g) / chroma); |
||||
|
} |
||||
|
|
||||
|
h *= 60; |
||||
|
if (h < 0.0) |
||||
|
{ |
||||
|
h += 360; |
||||
|
} |
||||
|
|
||||
|
s = chroma / v; |
||||
|
|
||||
|
return new Hsv(h, s, v); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
// <copyright file="CieXyzAndHunterLabConverterBase.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The base class for converting between <see cref="HunterLab"/> and <see cref="CieXyz"/> color spaces.
|
||||
|
/// </summary>
|
||||
|
internal abstract class CieXyzAndHunterLabConverterBase |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Returns the Ka coefficient that depends upon the whitepoint illuminant.
|
||||
|
/// </summary>
|
||||
|
/// <param name="whitePoint">The whitepoint</param>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static float ComputeKa(CieXyz whitePoint) |
||||
|
{ |
||||
|
DebugGuard.NotNull(whitePoint, nameof(whitePoint)); |
||||
|
|
||||
|
if (whitePoint.Equals(Illuminants.C)) |
||||
|
{ |
||||
|
return 175F; |
||||
|
} |
||||
|
|
||||
|
return 100F * (175F / 198.04F) * (whitePoint.X + whitePoint.Y); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the Kb coefficient that depends upon the whitepoint illuminant.
|
||||
|
/// </summary>
|
||||
|
/// <param name="whitePoint">The whitepoint</param>
|
||||
|
/// <returns>The <see cref="float"/></returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static float ComputeKb(CieXyz whitePoint) |
||||
|
{ |
||||
|
DebugGuard.NotNull(whitePoint, nameof(whitePoint)); |
||||
|
|
||||
|
if (whitePoint == Illuminants.C) |
||||
|
{ |
||||
|
return 70F; |
||||
|
} |
||||
|
|
||||
|
return 100F * (70F / 218.11F) * (whitePoint.Y + whitePoint.Z); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
// <copyright file="CieXyzToHunterLabConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between CieXyz and HunterLab
|
||||
|
/// </summary>
|
||||
|
internal class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase, IColorConversion<CieXyz, HunterLab> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyzToHunterLabConverter() |
||||
|
: this(HunterLab.DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzToHunterLabConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="labWhitePoint">The hunter Lab white point.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyzToHunterLabConverter(CieXyz labWhitePoint) |
||||
|
{ |
||||
|
this.HunterLabWhitePoint = labWhitePoint; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the target reference white. When not set, <see cref="HunterLab.DefaultWhitePoint"/> is used.
|
||||
|
/// </summary>
|
||||
|
public CieXyz HunterLabWhitePoint |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public HunterLab Convert(CieXyz input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
|
||||
|
float x = input.X, y = input.Y, z = input.Z; |
||||
|
float xn = this.HunterLabWhitePoint.X, yn = this.HunterLabWhitePoint.Y, zn = this.HunterLabWhitePoint.Z; |
||||
|
|
||||
|
float ka = ComputeKa(this.HunterLabWhitePoint); |
||||
|
float kb = ComputeKb(this.HunterLabWhitePoint); |
||||
|
|
||||
|
float l = 100 * MathF.Sqrt(y / yn); |
||||
|
float a = ka * (((x / xn) - (y / yn)) / MathF.Sqrt(y / yn)); |
||||
|
float b = kb * (((y / yn) - (z / zn)) / MathF.Sqrt(y / yn)); |
||||
|
|
||||
|
if (float.IsNaN(a)) |
||||
|
{ |
||||
|
a = 0; |
||||
|
} |
||||
|
|
||||
|
if (float.IsNaN(b)) |
||||
|
{ |
||||
|
b = 0; |
||||
|
} |
||||
|
|
||||
|
return new HunterLab(l, a, b, this.HunterLabWhitePoint); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
// <copyright file="HunterLabToCieXyzConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between HunterLab and CieXyz
|
||||
|
/// </summary>
|
||||
|
internal class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase, IColorConversion<HunterLab, CieXyz> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyz Convert(HunterLab input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
|
||||
|
float l = input.L, a = input.A, b = input.B; |
||||
|
float xn = input.WhitePoint.X, yn = input.WhitePoint.Y, zn = input.WhitePoint.Z; |
||||
|
|
||||
|
float ka = ComputeKa(input.WhitePoint); |
||||
|
float kb = ComputeKb(input.WhitePoint); |
||||
|
|
||||
|
float y = MathF.Pow(l / 100F, 2) * yn; |
||||
|
float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn; |
||||
|
float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn); |
||||
|
|
||||
|
return new CieXyz(x, y, z); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
// <copyright file="CieXyzAndLmsConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Lms |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between CIE XYZ and LMS
|
||||
|
/// </summary>
|
||||
|
internal class CieXyzAndLmsConverter : IColorConversion<CieXyz, Lms>, IColorConversion<Lms, CieXyz> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Default transformation matrix used, when no other is set. (Bradford)
|
||||
|
/// <see cref="LmsAdaptationMatrix"/>
|
||||
|
/// </summary>
|
||||
|
public static readonly Matrix4x4 DefaultTransformationMatrix = LmsAdaptationMatrix.Bradford; |
||||
|
|
||||
|
private Matrix4x4 inverseTransformationMatrix; |
||||
|
private Matrix4x4 transformationMatrix; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzAndLmsConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyzAndLmsConverter() |
||||
|
: this(DefaultTransformationMatrix) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzAndLmsConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="transformationMatrix">
|
||||
|
/// Definition of the cone response domain (see <see cref="LmsAdaptationMatrix"/>),
|
||||
|
/// if not set <see cref="DefaultTransformationMatrix"/> will be used.
|
||||
|
/// </param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix) |
||||
|
{ |
||||
|
this.TransformationMatrix = transformationMatrix; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the transformation matrix used for the conversion (definition of the cone response domain).
|
||||
|
/// <see cref="LmsAdaptationMatrix"/>
|
||||
|
/// </summary>
|
||||
|
public Matrix4x4 TransformationMatrix |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.transformationMatrix; |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
this.transformationMatrix = value; |
||||
|
Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Lms Convert(CieXyz input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
Vector3 vector = Vector3.Transform(input.Vector, this.transformationMatrix); |
||||
|
return new Lms(vector); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public CieXyz Convert(Lms input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
Vector3 vector = Vector3.Transform(input.Vector, this.inverseTransformationMatrix); |
||||
|
return new CieXyz(vector); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
// <copyright file="LmsAdaptationMatrix.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Lms |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// AdaptionMatrix3X3 used for transformation from XYZ to LMS, defining the cone response domain.
|
||||
|
/// Used in <see cref="IChromaticAdaptation"/>
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Matrix data obtained from:
|
||||
|
/// Two New von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization
|
||||
|
/// S. Bianco, R. Schettini
|
||||
|
/// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy
|
||||
|
/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf
|
||||
|
/// </remarks>
|
||||
|
public static class LmsAdaptationMatrix |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65)
|
||||
|
/// </summary>
|
||||
|
public static readonly Matrix4x4 VonKriesHPEAdjusted |
||||
|
= Matrix4x4.Transpose(new Matrix4x4 |
||||
|
{ |
||||
|
M11 = 0.40024F, M12 = 0.7076F, M13 = -0.08081F, |
||||
|
M21 = -0.2263F, M22 = 1.16532F, M23 = 0.0457F, |
||||
|
M31 = 0, M32 = 0, M33 = 0.91822F, |
||||
|
M44 = 1F // Important for inverse transforms.
|
||||
|
}); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy)
|
||||
|
/// </summary>
|
||||
|
public static readonly Matrix4x4 VonKriesHPE |
||||
|
= Matrix4x4.Transpose(new Matrix4x4 |
||||
|
{ |
||||
|
M11 = 0.3897F, M12 = 0.6890F, M13 = -0.0787F, |
||||
|
M21 = -0.2298F, M22 = 1.1834F, M23 = 0.0464F, |
||||
|
M31 = 0, M32 = 0, M33 = 1F, |
||||
|
M44 = 1F |
||||
|
}); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// XYZ scaling chromatic adaptation transform matrix
|
||||
|
/// </summary>
|
||||
|
public static readonly Matrix4x4 XyzScaling = Matrix4x4.Transpose(Matrix4x4.Identity); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Bradford chromatic adaptation transform matrix (used in CMCCAT97)
|
||||
|
/// </summary>
|
||||
|
public static readonly Matrix4x4 Bradford |
||||
|
= Matrix4x4.Transpose(new Matrix4x4 |
||||
|
{ |
||||
|
M11 = 0.8951F, M12 = 0.2664F, M13 = -0.1614F, |
||||
|
M21 = -0.7502F, M22 = 1.7135F, M23 = 0.0367F, |
||||
|
M31 = 0.0389F, M32 = -0.0685F, M33 = 1.0296F, |
||||
|
M44 = 1F |
||||
|
}); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Spectral sharpening and the Bradford transform
|
||||
|
/// </summary>
|
||||
|
public static readonly Matrix4x4 BradfordSharp |
||||
|
= Matrix4x4.Transpose(new Matrix4x4 |
||||
|
{ |
||||
|
M11 = 1.2694F, M12 = -0.0988F, M13 = -0.1706F, |
||||
|
M21 = -0.8364F, M22 = 1.8006F, M23 = 0.0357F, |
||||
|
M31 = 0.0297F, M32 = -0.0315F, M33 = 1.0018F, |
||||
|
M44 = 1F |
||||
|
}); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// CMCCAT2000 (fitted from all available color data sets)
|
||||
|
/// </summary>
|
||||
|
public static readonly Matrix4x4 CMCCAT2000 |
||||
|
= Matrix4x4.Transpose(new Matrix4x4 |
||||
|
{ |
||||
|
M11 = 0.7982F, M12 = 0.3389F, M13 = -0.1371F, |
||||
|
M21 = -0.5918F, M22 = 1.5512F, M23 = 0.0406F, |
||||
|
M31 = 0.0008F, M32 = 0.239F, M33 = 0.9753F, |
||||
|
M44 = 1F |
||||
|
}); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// CAT02 (optimized for minimizing CIELAB differences)
|
||||
|
/// </summary>
|
||||
|
public static readonly Matrix4x4 CAT02 |
||||
|
= Matrix4x4.Transpose(new Matrix4x4 |
||||
|
{ |
||||
|
M11 = 0.7328F, M12 = 0.4296F, M13 = -0.1624F, |
||||
|
M21 = -0.7036F, M22 = 1.6975F, M23 = 0.0061F, |
||||
|
M31 = 0.0030F, M32 = 0.0136F, M33 = 0.9834F, |
||||
|
M44 = 1F |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
// <copyright file="LinearRgbToRgbConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using Rgb = ImageSharp.ColorSpaces.Rgb; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between CieXyz and LinearRgb
|
||||
|
/// </summary>
|
||||
|
internal class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase, IColorConversion<CieXyz, LinearRgb> |
||||
|
{ |
||||
|
private readonly Matrix4x4 conversionMatrix; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzToLinearRgbConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
public CieXyzToLinearRgbConverter() |
||||
|
: this(Rgb.DefaultWorkingSpace) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="CieXyzToLinearRgbConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="workingSpace">The target working space.</param>
|
||||
|
public CieXyzToLinearRgbConverter(IRgbWorkingSpace workingSpace) |
||||
|
{ |
||||
|
this.TargetWorkingSpace = workingSpace; |
||||
|
this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the target working space
|
||||
|
/// </summary>
|
||||
|
public IRgbWorkingSpace TargetWorkingSpace { get; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public LinearRgb Convert(CieXyz input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted); |
||||
|
Vector3 vector = Vector3.Transform(input.Vector, inverted); |
||||
|
return new LinearRgb(vector, this.TargetWorkingSpace); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
// <copyright file="GammaCompanding.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Implements gamma companding
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
|
||||
|
/// </remarks>
|
||||
|
public class GammaCompanding : ICompanding |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="GammaCompanding"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="gamma">The gamma value.</param>
|
||||
|
public GammaCompanding(float gamma) |
||||
|
{ |
||||
|
this.Gamma = gamma; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the gamma value
|
||||
|
/// </summary>
|
||||
|
public float Gamma { get; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Expand(float channel) |
||||
|
{ |
||||
|
return MathF.Pow(channel, this.Gamma); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Compress(float channel) |
||||
|
{ |
||||
|
return MathF.Pow(channel, 1 / this.Gamma); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
// <copyright file="LCompanding.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Implements L* companding
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// For more info see:
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
|
||||
|
/// </remarks>
|
||||
|
public class LCompanding : ICompanding |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Expand(float channel) |
||||
|
{ |
||||
|
return channel <= 0.08 ? 100 * channel / CieConstants.Kappa : MathF.Pow((channel + 0.16F) / 1.16F, 3); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Compress(float channel) |
||||
|
{ |
||||
|
return channel <= CieConstants.Epsilon |
||||
|
? channel * CieConstants.Kappa / 100F |
||||
|
: MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,69 @@ |
|||||
|
// <copyright file="LinearRgbAndCieXyzConverterBase.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides base methods for converting between Rgb and CieXyz color spaces.
|
||||
|
/// </summary>
|
||||
|
internal abstract class LinearRgbAndCieXyzConverterBase |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Geturns the correct matrix to convert between the Rgb and CieXyz color space.
|
||||
|
/// </summary>
|
||||
|
/// <param name="workingSpace">The Rgb working space.</param>
|
||||
|
/// <returns>The <see cref="Matrix4x4"/> based on the chromaticity and working space.</returns>
|
||||
|
public static Matrix4x4 GetRgbToCieXyzMatrix(IRgbWorkingSpace workingSpace) |
||||
|
{ |
||||
|
DebugGuard.NotNull(workingSpace, nameof(workingSpace)); |
||||
|
|
||||
|
RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates; |
||||
|
|
||||
|
float xr = chromaticity.R.X; |
||||
|
float xg = chromaticity.G.X; |
||||
|
float xb = chromaticity.B.X; |
||||
|
float yr = chromaticity.R.Y; |
||||
|
float yg = chromaticity.G.Y; |
||||
|
float yb = chromaticity.B.Y; |
||||
|
|
||||
|
float mXr = xr / yr; |
||||
|
const float Yr = 1; |
||||
|
float mZr = (1 - xr - yr) / yr; |
||||
|
|
||||
|
float mXg = xg / yg; |
||||
|
const float Yg = 1; |
||||
|
float mZg = (1 - xg - yg) / yg; |
||||
|
|
||||
|
float mXb = xb / yb; |
||||
|
const float Yb = 1; |
||||
|
float mZb = (1 - xb - yb) / yb; |
||||
|
|
||||
|
Matrix4x4 xyzMatrix = new Matrix4x4 |
||||
|
{ |
||||
|
M11 = mXr, M21 = mXg, M31 = mXb, |
||||
|
M12 = Yr, M22 = Yg, M32 = Yb, |
||||
|
M13 = mZr, M23 = mZg, M33 = mZb, |
||||
|
M44 = 1F |
||||
|
}; |
||||
|
|
||||
|
Matrix4x4 inverseXyzMatrix; |
||||
|
Matrix4x4.Invert(xyzMatrix, out inverseXyzMatrix); |
||||
|
|
||||
|
Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.Vector, inverseXyzMatrix); |
||||
|
|
||||
|
// Use transposed Rows/Coloumns
|
||||
|
// TODO: Is there a built in method for this multiplication?
|
||||
|
return new Matrix4x4 |
||||
|
{ |
||||
|
M11 = vector.X * mXr, M21 = vector.Y * mXg, M31 = vector.Z * mXb, |
||||
|
M12 = vector.X * Yr, M22 = vector.Y * Yg, M32 = vector.Z * Yb, |
||||
|
M13 = vector.X * mZr, M23 = vector.Y * mZg, M33 = vector.Z * mZb, |
||||
|
M44 = 1F |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
// <copyright file="LinearRgbToCieXyzConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using Rgb = ImageSharp.ColorSpaces.Rgb; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between LinearRgb and CieXyz
|
||||
|
/// </summary>
|
||||
|
internal class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase, IColorConversion<LinearRgb, CieXyz> |
||||
|
{ |
||||
|
private readonly Matrix4x4 conversionMatrix; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="LinearRgbToCieXyzConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
public LinearRgbToCieXyzConverter() |
||||
|
: this(Rgb.DefaultWorkingSpace) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="LinearRgbToCieXyzConverter"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="workingSpace">The target working space.</param>
|
||||
|
public LinearRgbToCieXyzConverter(IRgbWorkingSpace workingSpace) |
||||
|
{ |
||||
|
this.SourceWorkingSpace = workingSpace; |
||||
|
this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the source working space
|
||||
|
/// </summary>
|
||||
|
public IRgbWorkingSpace SourceWorkingSpace { get; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public CieXyz Convert(LinearRgb input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
Guard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal."); |
||||
|
|
||||
|
Vector3 vector = Vector3.Transform(input.Vector, this.conversionMatrix); |
||||
|
return new CieXyz(vector); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
// <copyright file="LinearRgbToRgbConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using Rgb = ColorSpaces.Rgb; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between LinearRgb and Rgb
|
||||
|
/// </summary>
|
||||
|
internal class LinearRgbToRgbConverter : IColorConversion<LinearRgb, Rgb> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
public Rgb Convert(LinearRgb input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
Vector3 vector = input.Vector; |
||||
|
vector.X = input.WorkingSpace.Companding.Compress(vector.X); |
||||
|
vector.Y = input.WorkingSpace.Companding.Compress(vector.Y); |
||||
|
vector.Z = input.WorkingSpace.Companding.Compress(vector.Z); |
||||
|
|
||||
|
return new Rgb(vector, input.WorkingSpace); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
// <copyright file="RgbPrimariesChromaticityCoordinates.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents the chromaticity coordinates of RGB primaries.
|
||||
|
/// One of the specifiers of <see cref="IRgbWorkingSpace"/>.
|
||||
|
/// </summary>
|
||||
|
internal struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="r">The chomaticity coordinates of the red channel.</param>
|
||||
|
/// <param name="g">The chomaticity coordinates of the green channel.</param>
|
||||
|
/// <param name="b">The chomaticity coordinates of the blue channel.</param>
|
||||
|
public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b) |
||||
|
{ |
||||
|
this.R = r; |
||||
|
this.G = g; |
||||
|
this.B = b; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chomaticity coordinates of the red channel.
|
||||
|
/// </summary>
|
||||
|
public CieXyChromaticityCoordinates R { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chomaticity coordinates of the green channel.
|
||||
|
/// </summary>
|
||||
|
public CieXyChromaticityCoordinates G { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chomaticity coordinates of the blue channel.
|
||||
|
/// </summary>
|
||||
|
public CieXyChromaticityCoordinates B { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieLab"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieLab"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieLab"/> 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 ==(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="CieLab"/> objects for inequality
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="CieLab"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="CieLab"/> 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 !=(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is RgbPrimariesChromaticityCoordinates) |
||||
|
{ |
||||
|
return this.Equals((RgbPrimariesChromaticityCoordinates)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public bool Equals(RgbPrimariesChromaticityCoordinates other) |
||||
|
{ |
||||
|
return this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
unchecked |
||||
|
{ |
||||
|
int hashCode = this.R.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ this.G.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ this.B.GetHashCode(); |
||||
|
return hashCode; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
// <copyright file="Rec2020Companding.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Implements Rec. 2020 companding function (for 12-bits).
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// <see href="http://en.wikipedia.org/wiki/Rec._2020"/>
|
||||
|
/// For 10-bits, companding is identical to <see cref="Rec709Companding"/>
|
||||
|
/// </remarks>
|
||||
|
public class Rec2020Companding : ICompanding |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Expand(float channel) |
||||
|
{ |
||||
|
return channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Compress(float channel) |
||||
|
{ |
||||
|
return channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
// <copyright file="Rec2020Companding.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Implements the Rec. 709 companding function
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// http://en.wikipedia.org/wiki/Rec._709
|
||||
|
/// </remarks>
|
||||
|
public class Rec709Companding : ICompanding |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Expand(float channel) |
||||
|
{ |
||||
|
return channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Compress(float channel) |
||||
|
{ |
||||
|
return channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
// <copyright file="RgbToLinearRgbConverter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using Rgb = ColorSpaces.Rgb; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between Rgb and LinearRgb
|
||||
|
/// </summary>
|
||||
|
internal class RgbToLinearRgbConverter : IColorConversion<Rgb, LinearRgb> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
public LinearRgb Convert(Rgb input) |
||||
|
{ |
||||
|
Guard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
Vector3 vector = input.Vector; |
||||
|
vector.X = input.WorkingSpace.Companding.Expand(vector.X); |
||||
|
vector.Y = input.WorkingSpace.Companding.Expand(vector.Y); |
||||
|
vector.Z = input.WorkingSpace.Companding.Expand(vector.Z); |
||||
|
|
||||
|
return new LinearRgb(vector, input.WorkingSpace); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
// <copyright file="RgbWorkingSpace.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Trivial implementation of <see cref="IRgbWorkingSpace"/>
|
||||
|
/// </summary>
|
||||
|
internal struct RgbWorkingSpace : IRgbWorkingSpace |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="RgbWorkingSpace"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="referenceWhite">The reference white point.</param>
|
||||
|
/// <param name="companding">The function pair for converting to <see cref="CieXyz"/> and back.</param>
|
||||
|
/// <param name="chromaticityCoordinates">The chromaticity of the rgb primaries.</param>
|
||||
|
public RgbWorkingSpace(CieXyz referenceWhite, ICompanding companding, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) |
||||
|
{ |
||||
|
this.WhitePoint = referenceWhite; |
||||
|
this.Companding = companding; |
||||
|
this.ChromaticityCoordinates = chromaticityCoordinates; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the reference white point
|
||||
|
/// </summary>
|
||||
|
public CieXyz WhitePoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the function pair for converting to <see cref="CieXyz"/> and back.
|
||||
|
/// </summary>
|
||||
|
public ICompanding Companding { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chromaticity of the rgb primaries.
|
||||
|
/// </summary>
|
||||
|
public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="RgbWorkingSpace"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="RgbWorkingSpace"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="RgbWorkingSpace"/> 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 ==(RgbWorkingSpace left, RgbWorkingSpace right) |
||||
|
{ |
||||
|
return Equals(left, right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="RgbWorkingSpace"/> objects for inequality
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="RgbWorkingSpace"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="RgbWorkingSpace"/> 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 !=(RgbWorkingSpace left, RgbWorkingSpace right) |
||||
|
{ |
||||
|
return !Equals(left, right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is RgbWorkingSpace) |
||||
|
{ |
||||
|
return this.Equals((RgbWorkingSpace)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public bool Equals(IRgbWorkingSpace other) |
||||
|
{ |
||||
|
// TODO: Object.Equals for ICompanding will be slow.
|
||||
|
return this.WhitePoint.Equals(other.WhitePoint) |
||||
|
&& this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates) |
||||
|
&& Equals(this.Companding, other.Companding); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
unchecked |
||||
|
{ |
||||
|
int hashCode = this.WhitePoint.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ this.ChromaticityCoordinates.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ (this.Companding?.GetHashCode() ?? 0); |
||||
|
return hashCode; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
// <copyright file="SRgbCompanding.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Implements sRGB companding
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// For more info see:
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
|
||||
|
/// </remarks>
|
||||
|
public class SRgbCompanding : ICompanding |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Expand(float channel) |
||||
|
{ |
||||
|
return channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public float Compress(float channel) |
||||
|
{ |
||||
|
return channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
// <copyright file="YCbCrAndRgbConverter .cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion.Implementation.YCbCr |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color converter between YCbCr and Rgb
|
||||
|
/// See <see href="https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion"/> for formulas.
|
||||
|
/// </summary>
|
||||
|
internal class YCbCrAndRgbConverter : IColorConversion<YCbCr, Rgb>, IColorConversion<Rgb, YCbCr> |
||||
|
{ |
||||
|
private static readonly Vector3 MaxBytes = new Vector3(255F); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Rgb Convert(YCbCr input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
float y = input.Y; |
||||
|
float cb = input.Cb - 128F; |
||||
|
float cr = input.Cr - 128F; |
||||
|
|
||||
|
float r = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); |
||||
|
float g = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); |
||||
|
float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); |
||||
|
|
||||
|
return new Rgb(new Vector3(r, g, b) / MaxBytes); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public YCbCr Convert(Rgb input) |
||||
|
{ |
||||
|
DebugGuard.NotNull(input, nameof(input)); |
||||
|
|
||||
|
Vector3 rgb = input.Vector * MaxBytes; |
||||
|
float r = rgb.X; |
||||
|
float g = rgb.Y; |
||||
|
float b = rgb.Z; |
||||
|
|
||||
|
float y = (0.299F * r) + (0.587F * g) + (0.114F * b); |
||||
|
float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)); |
||||
|
float cr = 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)); |
||||
|
|
||||
|
return new YCbCr(y, cb, cr); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
// <copyright file="VonKriesChromaticAdaptation.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces.Conversion |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces; |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Lms; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Basic implementation of the von Kries chromatic adaptation model
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Transformation described here:
|
||||
|
/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
|
||||
|
/// </remarks>
|
||||
|
internal class VonKriesChromaticAdaptation : IChromaticAdaptation |
||||
|
{ |
||||
|
private readonly CieXyzAndLmsConverter converter; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
||||
|
/// </summary>
|
||||
|
public VonKriesChromaticAdaptation() |
||||
|
: this(new CieXyzAndLmsConverter()) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="transformationMatrix">
|
||||
|
/// The transformation matrix used for the conversion (definition of the cone response domain).
|
||||
|
/// <see cref="LmsAdaptationMatrix"/>
|
||||
|
/// </param>
|
||||
|
public VonKriesChromaticAdaptation(Matrix4x4 transformationMatrix) |
||||
|
: this(new CieXyzAndLmsConverter(transformationMatrix)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="VonKriesChromaticAdaptation"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="converter">The color converter</param>
|
||||
|
public VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) |
||||
|
{ |
||||
|
this.converter = converter; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public CieXyz Transform(CieXyz sourceColor, CieXyz sourceWhitePoint, CieXyz targetWhitePoint) |
||||
|
{ |
||||
|
Guard.NotNull(sourceColor, nameof(sourceColor)); |
||||
|
Guard.NotNull(sourceWhitePoint, nameof(sourceWhitePoint)); |
||||
|
Guard.NotNull(targetWhitePoint, nameof(targetWhitePoint)); |
||||
|
|
||||
|
if (sourceWhitePoint.Equals(targetWhitePoint)) |
||||
|
{ |
||||
|
return sourceColor; |
||||
|
} |
||||
|
|
||||
|
Lms sourceColorLms = this.converter.Convert(sourceColor); |
||||
|
Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint); |
||||
|
Lms targetWhitePointLms = this.converter.Convert(targetWhitePoint); |
||||
|
|
||||
|
var vector = new Vector3(targetWhitePointLms.L / sourceWhitePointLms.L, targetWhitePointLms.M / sourceWhitePointLms.M, targetWhitePointLms.S / sourceWhitePointLms.S); |
||||
|
var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.Vector)); |
||||
|
|
||||
|
return this.converter.Convert(targetColorLms); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,223 @@ |
|||||
|
// <copyright file="HunterLab.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents an Hunter LAB color.
|
||||
|
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
|
||||
|
/// </summary>
|
||||
|
internal struct HunterLab : IColorVector, IEquatable<HunterLab>, IAlmostEquatable<HunterLab, float> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// D50 standard illuminant.
|
||||
|
/// Used when reference white is not specified explicitly.
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz DefaultWhitePoint = Illuminants.C; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a <see cref="HunterLab"/> that has L, A, B values set to zero.
|
||||
|
/// </summary>
|
||||
|
public static readonly HunterLab Empty = default(HunterLab); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The backing vector for SIMD support.
|
||||
|
/// </summary>
|
||||
|
private readonly Vector3 backingVector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="HunterLab"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">The lightness dimension.</param>
|
||||
|
/// <param name="a">The a (green - magenta) component.</param>
|
||||
|
/// <param name="b">The b (blue - yellow) component.</param>
|
||||
|
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public HunterLab(float l, float a, float b) |
||||
|
: this(new Vector3(l, a, b), DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="HunterLab"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">The lightness dimension.</param>
|
||||
|
/// <param name="a">The a (green - magenta) component.</param>
|
||||
|
/// <param name="b">The b (blue - yellow) component.</param>
|
||||
|
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public HunterLab(float l, float a, float b, CieXyz whitePoint) |
||||
|
: this(new Vector3(l, a, b), whitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="HunterLab"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l, a, b components.</param>
|
||||
|
/// <remarks>Uses <see cref="DefaultWhitePoint"/> as white point.</remarks>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public HunterLab(Vector3 vector) |
||||
|
: this(vector, DefaultWhitePoint) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="HunterLab"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l a b components.</param>
|
||||
|
/// <param name="whitePoint">The reference white point. <see cref="Illuminants"/></param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public HunterLab(Vector3 vector, CieXyz whitePoint) |
||||
|
: this() |
||||
|
{ |
||||
|
this.backingVector = vector; |
||||
|
this.WhitePoint = whitePoint; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the reference white point of this color
|
||||
|
/// </summary>
|
||||
|
public CieXyz WhitePoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the lightness dimension.
|
||||
|
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
|
||||
|
/// </summary>
|
||||
|
public float L |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.X; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the a color component.
|
||||
|
/// <remarks>A value ranging from -100 to 100. Negative is green, positive magenta.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float A |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the b color component.
|
||||
|
/// <remarks>A value ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
|
||||
|
/// </summary>
|
||||
|
public float B |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Z; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this <see cref="HunterLab"/> is empty.
|
||||
|
/// </summary>
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public bool IsEmpty => this.Equals(Empty); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Vector3 Vector |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="HunterLab"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="HunterLab"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="HunterLab"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(HunterLab left, HunterLab right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="HunterLab"/> objects for inequality
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="HunterLab"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="HunterLab"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(HunterLab left, HunterLab right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
unchecked |
||||
|
{ |
||||
|
int hashCode = this.WhitePoint.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ this.backingVector.GetHashCode(); |
||||
|
return hashCode; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsEmpty) |
||||
|
{ |
||||
|
return "HunterLab [Empty]"; |
||||
|
} |
||||
|
|
||||
|
return $"HunterLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]"; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is HunterLab) |
||||
|
{ |
||||
|
return this.Equals((HunterLab)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool Equals(HunterLab other) |
||||
|
{ |
||||
|
return this.backingVector.Equals(other.backingVector) |
||||
|
&& this.WhitePoint.Equals(other.WhitePoint); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool AlmostEquals(HunterLab other, float precision) |
||||
|
{ |
||||
|
var result = Vector3.Abs(this.backingVector - other.backingVector); |
||||
|
|
||||
|
return this.WhitePoint.Equals(other.WhitePoint) |
||||
|
&& result.X <= precision |
||||
|
&& result.Y <= precision |
||||
|
&& result.Z <= precision; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// <copyright file="IColorVector.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Color represented as a vector in its color space
|
||||
|
/// </summary>
|
||||
|
public interface IColorVector |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the vector representation of the color
|
||||
|
/// </summary>
|
||||
|
Vector3 Vector { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
// <copyright file="ICompanding.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Pair of companding functions for <see cref="IRgbWorkingSpace"/>.
|
||||
|
/// Used for conversion to <see cref="CieXyz"/> and backwards.
|
||||
|
/// See also: <seealso cref="IRgbWorkingSpace.Companding"/>
|
||||
|
/// </summary>
|
||||
|
internal interface ICompanding |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Expands a companded channel to its linear equivalent with respect to the energy.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// For more info see:
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
|
||||
|
/// </remarks>
|
||||
|
/// <param name="channel">The channel value</param>
|
||||
|
/// <returns>The linear channel value</returns>
|
||||
|
float Expand(float channel); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system).
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// For more info see:
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
|
||||
|
/// </remarks>
|
||||
|
/// <param name="channel">The channel value</param>
|
||||
|
/// <returns>The nonlinear channel value</returns>
|
||||
|
float Compress(float channel); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
// <copyright file="IRgbWorkingSpace.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Encasulates the RGB working color space
|
||||
|
/// </summary>
|
||||
|
internal interface IRgbWorkingSpace : IEquatable<IRgbWorkingSpace> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the reference white of the color space
|
||||
|
/// </summary>
|
||||
|
CieXyz WhitePoint { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chromaticity coordinates of the primaries
|
||||
|
/// </summary>
|
||||
|
RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the companding function associated with the RGB color system. Used for conversion to XYZ and backwards.
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
|
||||
|
/// </summary>
|
||||
|
ICompanding Companding { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The well known standard illuminants.
|
||||
|
/// Standard illuminants provide a basis for comparing images or colors recorded under different lighting
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Coefficients taken from:
|
||||
|
/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
|
||||
|
/// <br />
|
||||
|
/// Descriptions taken from:
|
||||
|
/// http://en.wikipedia.org/wiki/Standard_illuminant
|
||||
|
/// </remarks>
|
||||
|
internal static class Illuminants |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Incandescent / Tungsten
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz A = new CieXyz(1.09850F, 1F, 0.35585F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Direct sunlight at noon (obsoleteF)
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz B = new CieXyz(0.99072F, 1F, 0.85223F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Average / North sky Daylight (obsoleteF)
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz C = new CieXyz(0.98074F, 1F, 1.18232F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Horizon Light. ICC profile PCS
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz D50 = new CieXyz(0.96422F, 1F, 0.82521F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Mid-morning / Mid-afternoon Daylight
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz D55 = new CieXyz(0.95682F, 1F, 0.92149F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Noon Daylight: TelevisionF, sRGB color space
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz D65 = new CieXyz(0.95047F, 1F, 1.08883F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// North sky Daylight
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz D75 = new CieXyz(0.94972F, 1F, 1.22638F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Equal energy
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz E = new CieXyz(1F, 1F, 1F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Cool White Fluorescent
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz F2 = new CieXyz(0.99186F, 1F, 0.67393F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// D65 simulatorF, Daylight simulator
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz F7 = new CieXyz(0.95041F, 1F, 1.08747F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Philips TL84F, Ultralume 40
|
||||
|
/// </summary>
|
||||
|
public static readonly CieXyz F11 = new CieXyz(1.00962F, 1F, 0.64350F); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,213 @@ |
|||||
|
// <copyright file="LinearRgb.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents an linear Rgb color with specified <see cref="IRgbWorkingSpace"/> working space
|
||||
|
/// </summary>
|
||||
|
internal struct LinearRgb : IColorVector, IEquatable<LinearRgb>, IAlmostEquatable<LinearRgb, float> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents a <see cref="LinearRgb"/> that has R, G, and B values set to zero.
|
||||
|
/// </summary>
|
||||
|
public static readonly LinearRgb Empty = default(LinearRgb); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The default LinearRgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The backing vector for SIMD support.
|
||||
|
/// </summary>
|
||||
|
private readonly Vector3 backingVector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="LinearRgb"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="r">The red component ranging between 0 and 1.</param>
|
||||
|
/// <param name="g">The green component ranging between 0 and 1.</param>
|
||||
|
/// <param name="b">The blue component ranging between 0 and 1.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public LinearRgb(float r, float g, float b) |
||||
|
: this(new Vector3(r, g, b)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="LinearRgb"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="r">The red component ranging between 0 and 1.</param>
|
||||
|
/// <param name="g">The green component ranging between 0 and 1.</param>
|
||||
|
/// <param name="b">The blue component ranging between 0 and 1.</param>
|
||||
|
/// <param name="workingSpace">The rgb working space.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public LinearRgb(float r, float g, float b, IRgbWorkingSpace workingSpace) |
||||
|
: this(new Vector3(r, g, b), workingSpace) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="LinearRgb"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the r, g, b components.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public LinearRgb(Vector3 vector) |
||||
|
: this(vector, DefaultWorkingSpace) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="LinearRgb"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the r, g, b components.</param>
|
||||
|
/// <param name="workingSpace">The LinearRgb working space.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public LinearRgb(Vector3 vector, IRgbWorkingSpace workingSpace) |
||||
|
: this() |
||||
|
{ |
||||
|
// Clamp to 0-1 range.
|
||||
|
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); |
||||
|
this.WorkingSpace = workingSpace; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red component.
|
||||
|
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float R |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.X; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green component.
|
||||
|
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float G |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue component.
|
||||
|
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float B |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Z; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the LinearRgb color space <seealso cref="RgbWorkingSpaces"/>
|
||||
|
/// </summary>
|
||||
|
public IRgbWorkingSpace WorkingSpace { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this <see cref="LinearRgb"/> is empty.
|
||||
|
/// </summary>
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public bool IsEmpty => this.Equals(Empty); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Vector3 Vector |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="LinearRgb"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="LinearRgb"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="LinearRgb"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(LinearRgb left, LinearRgb right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="LinearRgb"/> objects for inequality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="LinearRgb"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="LinearRgb"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(LinearRgb left, LinearRgb right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return this.backingVector.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsEmpty) |
||||
|
{ |
||||
|
return "LinearRgb [ Empty ]"; |
||||
|
} |
||||
|
|
||||
|
return $"LinearRgb [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##} ]"; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is LinearRgb) |
||||
|
{ |
||||
|
return this.Equals((LinearRgb)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool Equals(LinearRgb other) |
||||
|
{ |
||||
|
return this.backingVector.Equals(other.backingVector); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool AlmostEquals(LinearRgb other, float precision) |
||||
|
{ |
||||
|
var result = Vector3.Abs(this.backingVector - other.backingVector); |
||||
|
|
||||
|
return result.X <= precision |
||||
|
&& result.Y <= precision |
||||
|
&& result.Z <= precision; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,180 @@ |
|||||
|
// <copyright file="Lms.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// LMS is a color space represented by the response of the three types of cones of the human eye,
|
||||
|
/// named after their responsivity (sensitivity) at long, medium and short wavelengths.
|
||||
|
/// <see href="https://en.wikipedia.org/wiki/LMS_color_space"/>
|
||||
|
/// </summary>
|
||||
|
internal struct Lms : IColorVector, IEquatable<Lms>, IAlmostEquatable<Lms, float> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents a <see cref="Lms"/> that has L, M, and S values set to zero.
|
||||
|
/// </summary>
|
||||
|
public static readonly Lms Empty = default(Lms); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The backing vector for SIMD support.
|
||||
|
/// </summary>
|
||||
|
private readonly Vector3 backingVector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Lms"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="l">L represents the responsivity at long wavelengths.</param>
|
||||
|
/// <param name="m">M represents the responsivity at medium wavelengths.</param>
|
||||
|
/// <param name="s">S represents the responsivity at short wavelengths.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Lms(float l, float m, float s) |
||||
|
: this(new Vector3(l, m, s)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Lms"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the l, m, s components.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Lms(Vector3 vector) |
||||
|
: this() |
||||
|
{ |
||||
|
// Not clamping as documentation about this space seems to indicate "usual" ranges
|
||||
|
this.backingVector = vector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the L long component.
|
||||
|
/// <remarks>A value usually ranging between -1 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float L |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.X; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the M medium component.
|
||||
|
/// <remarks>A value usually ranging between -1 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float M |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the S short component.
|
||||
|
/// <remarks>A value usually ranging between -1 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float S |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Z; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this <see cref="Lms"/> is empty.
|
||||
|
/// </summary>
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public bool IsEmpty => this.Equals(Empty); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Vector3 Vector |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="Lms"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="Lms"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="Lms"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(Lms left, Lms right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="Lms"/> objects for inequality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="Lms"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="Lms"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(Lms left, Lms right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return this.backingVector.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsEmpty) |
||||
|
{ |
||||
|
return "Lms [ Empty ]"; |
||||
|
} |
||||
|
|
||||
|
return $"Lms [ L={this.L:#0.##}, M={this.M:#0.##}, S={this.S:#0.##} ]"; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is Lms) |
||||
|
{ |
||||
|
return this.Equals((Lms)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool Equals(Lms other) |
||||
|
{ |
||||
|
return this.backingVector.Equals(other.backingVector); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool AlmostEquals(Lms other, float precision) |
||||
|
{ |
||||
|
var result = Vector3.Abs(this.backingVector - other.backingVector); |
||||
|
|
||||
|
return result.X <= precision |
||||
|
&& result.Y <= precision |
||||
|
&& result.Z <= precision; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,233 @@ |
|||||
|
// <copyright file="Rgb.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents an RGB color with specified <see cref="IRgbWorkingSpace"/> working space
|
||||
|
/// </summary>
|
||||
|
internal struct Rgb : IColorVector, IEquatable<Rgb>, IAlmostEquatable<Rgb, float> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents a <see cref="Rgb"/> that has R, G, and B values set to zero.
|
||||
|
/// </summary>
|
||||
|
public static readonly Rgb Empty = default(Rgb); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The default rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The backing vector for SIMD support.
|
||||
|
/// </summary>
|
||||
|
private readonly Vector3 backingVector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Rgb"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="r">The red component ranging between 0 and 1.</param>
|
||||
|
/// <param name="g">The green component ranging between 0 and 1.</param>
|
||||
|
/// <param name="b">The blue component ranging between 0 and 1.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Rgb(float r, float g, float b) |
||||
|
: this(new Vector3(r, g, b)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Rgb"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="r">The red component ranging between 0 and 1.</param>
|
||||
|
/// <param name="g">The green component ranging between 0 and 1.</param>
|
||||
|
/// <param name="b">The blue component ranging between 0 and 1.</param>
|
||||
|
/// <param name="workingSpace">The rgb working space.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Rgb(float r, float g, float b, IRgbWorkingSpace workingSpace) |
||||
|
: this(new Vector3(r, g, b), workingSpace) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Rgb"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the r, g, b components.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Rgb(Vector3 vector) |
||||
|
: this(vector, DefaultWorkingSpace) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Rgb"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the r, g, b components.</param>
|
||||
|
/// <param name="workingSpace">The rgb working space.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public Rgb(Vector3 vector, IRgbWorkingSpace workingSpace) |
||||
|
: this() |
||||
|
{ |
||||
|
// Clamp to 0-1 range.
|
||||
|
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); |
||||
|
this.WorkingSpace = workingSpace; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the red component.
|
||||
|
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float R |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.X; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the green component.
|
||||
|
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float G |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Y; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the blue component.
|
||||
|
/// <remarks>A value usually ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float B |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector.Z; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the Rgb color space <seealso cref="RgbWorkingSpaces"/>
|
||||
|
/// </summary>
|
||||
|
public IRgbWorkingSpace WorkingSpace |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this <see cref="Rgb"/> is empty.
|
||||
|
/// </summary>
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public bool IsEmpty => this.Equals(Empty); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public Vector3 Vector |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get => this.backingVector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a
|
||||
|
/// <see cref="Rgb"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="color">
|
||||
|
/// The instance of <see cref="Rgba32"/> to convert.
|
||||
|
/// </param>
|
||||
|
/// <returns>
|
||||
|
/// An instance of <see cref="Rgb"/>.
|
||||
|
/// </returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static implicit operator Rgb(Rgba32 color) |
||||
|
{ |
||||
|
return new Rgb(color.R / 255F, color.G / 255F, color.B / 255F); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="Rgb"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="Rgb"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="Rgb"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(Rgb left, Rgb right) |
||||
|
{ |
||||
|
return left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="Rgb"/> objects for inequality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">
|
||||
|
/// The <see cref="Rgb"/> on the left side of the operand.
|
||||
|
/// </param>
|
||||
|
/// <param name="right">
|
||||
|
/// The <see cref="Rgb"/> 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>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(Rgb left, Rgb right) |
||||
|
{ |
||||
|
return !left.Equals(right); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return this.backingVector.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsEmpty) |
||||
|
{ |
||||
|
return "Rgb [ Empty ]"; |
||||
|
} |
||||
|
|
||||
|
return $"Rgb [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##} ]"; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj is Rgb) |
||||
|
{ |
||||
|
return this.Equals((Rgb)obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool Equals(Rgb other) |
||||
|
{ |
||||
|
return this.backingVector.Equals(other.backingVector); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public bool AlmostEquals(Rgb other, float precision) |
||||
|
{ |
||||
|
var result = Vector3.Abs(this.backingVector - other.backingVector); |
||||
|
|
||||
|
return result.X <= precision |
||||
|
&& result.Y <= precision |
||||
|
&& result.Z <= precision; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,117 @@ |
|||||
|
// <copyright file="RgbWorkingSpaces.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace ImageSharp.ColorSpaces |
||||
|
{ |
||||
|
using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Chromaticity coordinates taken from:
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html"/>
|
||||
|
/// </summary>
|
||||
|
internal static class RgbWorkingSpaces |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// sRgb working space.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Uses proper companding function, according to:
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_Rgb_to_XYZ.html"/>
|
||||
|
/// </remarks>
|
||||
|
public static readonly IRgbWorkingSpace SRgb = new RgbWorkingSpace(Illuminants.D65, new SRgbCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Simplified sRgb working space (uses <see cref="GammaCompanding">gamma companding</see> instead of <see cref="SRgbCompanding"/>).
|
||||
|
/// See also <see cref="SRgb"/>.
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace SRgbSimplified = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Rec. 709 (ITU-R Recommendation BT.709) working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace Rec709 = new RgbWorkingSpace(Illuminants.D65, new Rec709Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace Rec2020 = new RgbWorkingSpace(Illuminants.D65, new Rec2020Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// ECI Rgb v2 working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace ECIRgbv2 = new RgbWorkingSpace(Illuminants.D50, new LCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adobe Rgb (1998) working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace AdobeRgb1998 = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Apple sRgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace ApplesRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Best Rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace BestRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Beta Rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace BetaRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Bruce Rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace BruceRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// CIE Rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace CIERgb = new RgbWorkingSpace(Illuminants.E, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// ColorMatch Rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace ColorMatchRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Don Rgb 4 working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace DonRgb4 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Ekta Space PS5 working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace EktaSpacePS5 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// NTSC Rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace NTSCRgb = new RgbWorkingSpace(Illuminants.C, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// PAL/SECAM Rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace PALSECAMRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// ProPhoto Rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace ProPhotoRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SMPTE-C Rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace SMPTECRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Wide Gamut Rgb working space
|
||||
|
/// </summary>
|
||||
|
public static readonly IRgbWorkingSpace WideGamutRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); |
||||
|
} |
||||
|
} |
||||
@ -1,167 +0,0 @@ |
|||||
// <copyright file="Bgra32.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Colors.Spaces |
|
||||
{ |
|
||||
using System; |
|
||||
using System.ComponentModel; |
|
||||
using System.Numerics; |
|
||||
using ImageSharp.PixelFormats; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents an BGRA (blue, green, red, alpha) color.
|
|
||||
/// </summary>
|
|
||||
public struct Bgra32 : IEquatable<Bgra32> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a 32 bit <see cref="Bgra32"/> that has B, G, R, and A values set to zero.
|
|
||||
/// </summary>
|
|
||||
public static readonly Bgra32 Empty = default(Bgra32); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Min range used for clamping
|
|
||||
/// </summary>
|
|
||||
private static readonly Vector4 VectorMin = Vector4.Zero; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Max range used for clamping
|
|
||||
/// </summary>
|
|
||||
private static readonly Vector4 VectorMax = new Vector4(255); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The backing vector for SIMD support.
|
|
||||
/// </summary>
|
|
||||
private readonly Vector4 backingVector; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
|
|
||||
/// </summary>
|
|
||||
/// <param name="b">The blue component of this <see cref="Bgra32"/>.</param>
|
|
||||
/// <param name="g">The green component of this <see cref="Bgra32"/>.</param>
|
|
||||
/// <param name="r">The red component of this <see cref="Bgra32"/>.</param>
|
|
||||
/// <param name="a">The alpha component of this <see cref="Bgra32"/>.</param>
|
|
||||
public Bgra32(byte b, byte g, byte r, byte a = 255) |
|
||||
: this() |
|
||||
{ |
|
||||
this.backingVector = Vector4.Clamp(new Vector4(b, g, r, a), VectorMin, VectorMax); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the blue component of the color
|
|
||||
/// </summary>
|
|
||||
public byte B => (byte)this.backingVector.X; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the green component of the color
|
|
||||
/// </summary>
|
|
||||
public byte G => (byte)this.backingVector.Y; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the red component of the color
|
|
||||
/// </summary>
|
|
||||
public byte R => (byte)this.backingVector.Z; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the alpha component of the color
|
|
||||
/// </summary>
|
|
||||
public byte A => (byte)this.backingVector.W; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the <see cref="Bgra32"/> integer representation of the color.
|
|
||||
/// </summary>
|
|
||||
public int Bgra => (this.R << 16) | (this.G << 8) | (this.B << 0) | (this.A << 24); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether this <see cref="Bgra32"/> is empty.
|
|
||||
/// </summary>
|
|
||||
[EditorBrowsable(EditorBrowsableState.Never)] |
|
||||
public bool IsEmpty => this.Equals(Empty); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a
|
|
||||
/// <see cref="Bgra32"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="color">
|
|
||||
/// The instance of <see cref="Rgba32"/> to convert.
|
|
||||
/// </param>
|
|
||||
/// <returns>
|
|
||||
/// An instance of <see cref="Bgra32"/>.
|
|
||||
/// </returns>
|
|
||||
public static implicit operator Bgra32(Rgba32 color) |
|
||||
{ |
|
||||
return new Bgra32(color.B, color.G, color.R, color.A); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compares two <see cref="Bgra32"/> objects for equality.
|
|
||||
/// </summary>
|
|
||||
/// <param name="left">
|
|
||||
/// The <see cref="Bgra32"/> on the left side of the operand.
|
|
||||
/// </param>
|
|
||||
/// <param name="right">
|
|
||||
/// The <see cref="Bgra32"/> 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 ==(Bgra32 left, Bgra32 right) |
|
||||
{ |
|
||||
return left.Equals(right); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compares two <see cref="Bgra32"/> objects for inequality.
|
|
||||
/// </summary>
|
|
||||
/// <param name="left">
|
|
||||
/// The <see cref="Bgra32"/> on the left side of the operand.
|
|
||||
/// </param>
|
|
||||
/// <param name="right">
|
|
||||
/// The <see cref="Bgra32"/> 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 !=(Bgra32 left, Bgra32 right) |
|
||||
{ |
|
||||
return !left.Equals(right); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override bool Equals(object obj) |
|
||||
{ |
|
||||
if (obj is Bgra32) |
|
||||
{ |
|
||||
Bgra32 color = (Bgra32)obj; |
|
||||
|
|
||||
return this.backingVector == color.backingVector; |
|
||||
} |
|
||||
|
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override int GetHashCode() |
|
||||
{ |
|
||||
return this.backingVector.GetHashCode(); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override string ToString() |
|
||||
{ |
|
||||
if (this.IsEmpty) |
|
||||
{ |
|
||||
return "Bgra32 [ Empty ]"; |
|
||||
} |
|
||||
|
|
||||
return $"Bgra32 [ B={this.B}, G={this.G}, R={this.R}, A={this.A} ]"; |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public bool Equals(Bgra32 other) |
|
||||
{ |
|
||||
return this.backingVector.Equals(other.backingVector); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,45 @@ |
|||||
|
// <copyright file="IccDataWriter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A segment of a curve
|
||||
|
/// </summary>
|
||||
|
internal abstract class IccCurveSegment : IEquatable<IccCurveSegment> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccCurveSegment"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="signature">The signature of this segment</param>
|
||||
|
protected IccCurveSegment(IccCurveSegmentSignature signature) |
||||
|
{ |
||||
|
this.Signature = signature; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the signature of this segment
|
||||
|
/// </summary>
|
||||
|
public IccCurveSegmentSignature Signature { get; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public virtual bool Equals(IccCurveSegment other) |
||||
|
{ |
||||
|
if (other == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (ReferenceEquals(this, other)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return this.Signature == other.Signature; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,95 @@ |
|||||
|
// <copyright file="IccDataWriter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A formula based curve segment
|
||||
|
/// </summary>
|
||||
|
internal sealed class IccFormulaCurveElement : IccCurveSegment, IEquatable<IccFormulaCurveElement> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccFormulaCurveElement"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="type">The type of this segment</param>
|
||||
|
/// <param name="gamma">Gamma segment parameter</param>
|
||||
|
/// <param name="a">A segment parameter</param>
|
||||
|
/// <param name="b">B segment parameter</param>
|
||||
|
/// <param name="c">C segment parameter</param>
|
||||
|
/// <param name="d">D segment parameter</param>
|
||||
|
/// <param name="e">E segment parameter</param>
|
||||
|
public IccFormulaCurveElement(IccFormulaCurveType type, float gamma, float a, float b, float c, float d, float e) |
||||
|
: base(IccCurveSegmentSignature.FormulaCurve) |
||||
|
{ |
||||
|
this.Type = type; |
||||
|
this.Gamma = gamma; |
||||
|
this.A = a; |
||||
|
this.B = b; |
||||
|
this.C = c; |
||||
|
this.D = d; |
||||
|
this.E = e; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the type of this curve
|
||||
|
/// </summary>
|
||||
|
public IccFormulaCurveType Type { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the gamma curve parameter
|
||||
|
/// </summary>
|
||||
|
public float Gamma { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the A curve parameter
|
||||
|
/// </summary>
|
||||
|
public float A { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the B curve parameter
|
||||
|
/// </summary>
|
||||
|
public float B { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the C curve parameter
|
||||
|
/// </summary>
|
||||
|
public float C { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the D curve parameter
|
||||
|
/// </summary>
|
||||
|
public float D { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the E curve parameter
|
||||
|
/// </summary>
|
||||
|
public float E { get; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override bool Equals(IccCurveSegment other) |
||||
|
{ |
||||
|
if (base.Equals(other) && other is IccFormulaCurveElement segment) |
||||
|
{ |
||||
|
return this.Type == segment.Type |
||||
|
&& this.Gamma == segment.Gamma |
||||
|
&& this.A == segment.A |
||||
|
&& this.B == segment.B |
||||
|
&& this.C == segment.C |
||||
|
&& this.D == segment.D |
||||
|
&& this.E == segment.E; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public bool Equals(IccFormulaCurveElement other) |
||||
|
{ |
||||
|
return this.Equals((IccCurveSegment)other); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,60 @@ |
|||||
|
// <copyright file="IccDataWriter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A one dimensional curve
|
||||
|
/// </summary>
|
||||
|
internal sealed class IccOneDimensionalCurve : IEquatable<IccOneDimensionalCurve> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccOneDimensionalCurve"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="breakPoints">The break points of this curve</param>
|
||||
|
/// <param name="segments">The segments of this curve</param>
|
||||
|
public IccOneDimensionalCurve(float[] breakPoints, IccCurveSegment[] segments) |
||||
|
{ |
||||
|
Guard.NotNull(breakPoints, nameof(breakPoints)); |
||||
|
Guard.NotNull(segments, nameof(segments)); |
||||
|
|
||||
|
bool isSizeCorrect = breakPoints.Length == segments.Length - 1; |
||||
|
Guard.IsTrue(isSizeCorrect, $"{nameof(breakPoints)},{nameof(segments)}", "Number of BreakPoints must be one less than number of Segments"); |
||||
|
|
||||
|
this.BreakPoints = breakPoints; |
||||
|
this.Segments = segments; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the breakpoints that separate two curve segments
|
||||
|
/// </summary>
|
||||
|
public float[] BreakPoints { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets an array of curve segments
|
||||
|
/// </summary>
|
||||
|
public IccCurveSegment[] Segments { get; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public bool Equals(IccOneDimensionalCurve other) |
||||
|
{ |
||||
|
if (other == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (ReferenceEquals(this, other)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return this.BreakPoints.SequenceEqual(other.BreakPoints) |
||||
|
&& this.Segments.SequenceEqual(other.Segments); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,150 @@ |
|||||
|
// <copyright file="IccDataWriter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A parametric curve
|
||||
|
/// </summary>
|
||||
|
internal sealed class IccParametricCurve : IEquatable<IccParametricCurve> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccParametricCurve"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="g">G curve parameter</param>
|
||||
|
public IccParametricCurve(float g) |
||||
|
: this(IccParametricCurveType.Type1, g, 0, 0, 0, 0, 0, 0) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccParametricCurve"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="g">G curve parameter</param>
|
||||
|
/// <param name="a">A curve parameter</param>
|
||||
|
/// <param name="b">B curve parameter</param>
|
||||
|
public IccParametricCurve(float g, float a, float b) |
||||
|
: this(IccParametricCurveType.Cie122_1996, g, a, b, 0, 0, 0, 0) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccParametricCurve"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="g">G curve parameter</param>
|
||||
|
/// <param name="a">A curve parameter</param>
|
||||
|
/// <param name="b">B curve parameter</param>
|
||||
|
/// <param name="c">C curve parameter</param>
|
||||
|
public IccParametricCurve(float g, float a, float b, float c) |
||||
|
: this(IccParametricCurveType.Iec61966_3, g, a, b, c, 0, 0, 0) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccParametricCurve"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="g">G curve parameter</param>
|
||||
|
/// <param name="a">A curve parameter</param>
|
||||
|
/// <param name="b">B curve parameter</param>
|
||||
|
/// <param name="c">C curve parameter</param>
|
||||
|
/// <param name="d">D curve parameter</param>
|
||||
|
public IccParametricCurve(float g, float a, float b, float c, float d) |
||||
|
: this(IccParametricCurveType.SRgb, g, a, b, c, d, 0, 0) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccParametricCurve"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="g">G curve parameter</param>
|
||||
|
/// <param name="a">A curve parameter</param>
|
||||
|
/// <param name="b">B curve parameter</param>
|
||||
|
/// <param name="c">C curve parameter</param>
|
||||
|
/// <param name="d">D curve parameter</param>
|
||||
|
/// <param name="e">E curve parameter</param>
|
||||
|
/// <param name="f">F curve parameter</param>
|
||||
|
public IccParametricCurve(float g, float a, float b, float c, float d, float e, float f) |
||||
|
: this(IccParametricCurveType.Type5, g, a, b, c, d, e, f) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
private IccParametricCurve(IccParametricCurveType type, float g, float a, float b, float c, float d, float e, float f) |
||||
|
{ |
||||
|
this.Type = type; |
||||
|
this.G = g; |
||||
|
this.A = a; |
||||
|
this.B = b; |
||||
|
this.C = c; |
||||
|
this.D = d; |
||||
|
this.E = e; |
||||
|
this.F = f; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the type of this curve
|
||||
|
/// </summary>
|
||||
|
public IccParametricCurveType Type { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the G curve parameter
|
||||
|
/// </summary>
|
||||
|
public float G { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the A curve parameter
|
||||
|
/// </summary>
|
||||
|
public float A { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the B curve parameter
|
||||
|
/// </summary>
|
||||
|
public float B { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the C curve parameter
|
||||
|
/// </summary>
|
||||
|
public float C { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the D curve parameter
|
||||
|
/// </summary>
|
||||
|
public float D { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the E curve parameter
|
||||
|
/// </summary>
|
||||
|
public float E { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the F curve parameter
|
||||
|
/// </summary>
|
||||
|
public float F { get; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public bool Equals(IccParametricCurve other) |
||||
|
{ |
||||
|
if (other == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (ReferenceEquals(this, other)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return this.Type == other.Type |
||||
|
&& this.G == other.G |
||||
|
&& this.A == other.A |
||||
|
&& this.B == other.B |
||||
|
&& this.C == other.C |
||||
|
&& this.D == other.D |
||||
|
&& this.E == other.E |
||||
|
&& this.F == other.F; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,87 @@ |
|||||
|
// <copyright file="IccDataWriter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A response curve
|
||||
|
/// </summary>
|
||||
|
internal sealed class IccResponseCurve : IEquatable<IccResponseCurve> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccResponseCurve"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="curveType">The type of this curve</param>
|
||||
|
/// <param name="xyzValues">The XYZ values</param>
|
||||
|
/// <param name="responseArrays">The response arrays</param>
|
||||
|
public IccResponseCurve(IccCurveMeasurementEncodings curveType, Vector3[] xyzValues, IccResponseNumber[][] responseArrays) |
||||
|
{ |
||||
|
Guard.NotNull(xyzValues, nameof(xyzValues)); |
||||
|
Guard.NotNull(responseArrays, nameof(responseArrays)); |
||||
|
|
||||
|
Guard.IsTrue(xyzValues.Length == responseArrays.Length, $"{nameof(xyzValues)},{nameof(responseArrays)}", "Arrays must have same length"); |
||||
|
Guard.MustBeBetweenOrEqualTo(xyzValues.Length, 1, 15, nameof(xyzValues)); |
||||
|
|
||||
|
this.CurveType = curveType; |
||||
|
this.XyzValues = xyzValues; |
||||
|
this.ResponseArrays = responseArrays; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the type of this curve
|
||||
|
/// </summary>
|
||||
|
public IccCurveMeasurementEncodings CurveType { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the XYZ values
|
||||
|
/// </summary>
|
||||
|
public Vector3[] XyzValues { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the response arrays
|
||||
|
/// </summary>
|
||||
|
public IccResponseNumber[][] ResponseArrays { get; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public bool Equals(IccResponseCurve other) |
||||
|
{ |
||||
|
if (other == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (ReferenceEquals(this, other)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return this.CurveType == other.CurveType |
||||
|
&& this.XyzValues.SequenceEqual(other.XyzValues) |
||||
|
&& this.EqualsResponseArray(other); |
||||
|
} |
||||
|
|
||||
|
private bool EqualsResponseArray(IccResponseCurve other) |
||||
|
{ |
||||
|
if (this.ResponseArrays.Length != other.ResponseArrays.Length) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < this.ResponseArrays.Length; i++) |
||||
|
{ |
||||
|
if (!this.ResponseArrays[i].SequenceEqual(other.ResponseArrays[i])) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
// <copyright file="IccDataWriter.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A sampled curve segment
|
||||
|
/// </summary>
|
||||
|
internal sealed class IccSampledCurveElement : IccCurveSegment, IEquatable<IccSampledCurveElement> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccSampledCurveElement"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="curveEntries">The curve values of this segment</param>
|
||||
|
public IccSampledCurveElement(float[] curveEntries) |
||||
|
: base(IccCurveSegmentSignature.SampledCurve) |
||||
|
{ |
||||
|
Guard.NotNull(curveEntries, nameof(curveEntries)); |
||||
|
Guard.IsTrue(curveEntries.Length > 0, nameof(curveEntries), "There must be at least one value"); |
||||
|
|
||||
|
this.CurveEntries = curveEntries; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the curve values of this segment
|
||||
|
/// </summary>
|
||||
|
public float[] CurveEntries { get; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override bool Equals(IccCurveSegment other) |
||||
|
{ |
||||
|
if (base.Equals(other) && other is IccSampledCurveElement segment) |
||||
|
{ |
||||
|
return this.CurveEntries.SequenceEqual(segment.CurveEntries); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public bool Equals(IccSampledCurveElement other) |
||||
|
{ |
||||
|
return this.Equals((IccCurveSegment)other); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,221 @@ |
|||||
|
// <copyright file="IccDataReader.Curves.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods to read ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataReader |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccOneDimensionalCurve"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read curve</returns>
|
||||
|
public IccOneDimensionalCurve ReadOneDimensionalCurve() |
||||
|
{ |
||||
|
ushort segmentCount = this.ReadUInt16(); |
||||
|
this.AddIndex(2); // 2 bytes reserved
|
||||
|
float[] breakPoints = new float[segmentCount - 1]; |
||||
|
for (int i = 0; i < breakPoints.Length; i++) |
||||
|
{ |
||||
|
breakPoints[i] = this.ReadSingle(); |
||||
|
} |
||||
|
|
||||
|
IccCurveSegment[] segments = new IccCurveSegment[segmentCount]; |
||||
|
for (int i = 0; i < segmentCount; i++) |
||||
|
{ |
||||
|
segments[i] = this.ReadCurveSegment(); |
||||
|
} |
||||
|
|
||||
|
return new IccOneDimensionalCurve(breakPoints, segments); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccResponseCurve"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="channelCount">The number of channels</param>
|
||||
|
/// <returns>The read curve</returns>
|
||||
|
public IccResponseCurve ReadResponseCurve(int channelCount) |
||||
|
{ |
||||
|
var type = (IccCurveMeasurementEncodings)this.ReadUInt32(); |
||||
|
uint[] measurment = new uint[channelCount]; |
||||
|
for (int i = 0; i < channelCount; i++) |
||||
|
{ |
||||
|
measurment[i] = this.ReadUInt32(); |
||||
|
} |
||||
|
|
||||
|
Vector3[] xyzValues = new Vector3[channelCount]; |
||||
|
for (int i = 0; i < channelCount; i++) |
||||
|
{ |
||||
|
xyzValues[i] = this.ReadXyzNumber(); |
||||
|
} |
||||
|
|
||||
|
IccResponseNumber[][] response = new IccResponseNumber[channelCount][]; |
||||
|
for (int i = 0; i < channelCount; i++) |
||||
|
{ |
||||
|
response[i] = new IccResponseNumber[measurment[i]]; |
||||
|
for (uint j = 0; j < measurment[i]; j++) |
||||
|
{ |
||||
|
response[i][j] = this.ReadResponseNumber(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return new IccResponseCurve(type, xyzValues, response); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccParametricCurve"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read curve</returns>
|
||||
|
public IccParametricCurve ReadParametricCurve() |
||||
|
{ |
||||
|
ushort type = this.ReadUInt16(); |
||||
|
this.AddIndex(2); // 2 bytes reserved
|
||||
|
float gamma, a, b, c, d, e, f; |
||||
|
gamma = a = b = c = d = e = f = 0; |
||||
|
|
||||
|
if (type <= 4) |
||||
|
{ |
||||
|
gamma = this.ReadFix16(); |
||||
|
} |
||||
|
|
||||
|
if (type > 0 && type <= 4) |
||||
|
{ |
||||
|
a = this.ReadFix16(); |
||||
|
b = this.ReadFix16(); |
||||
|
} |
||||
|
|
||||
|
if (type > 1 && type <= 4) |
||||
|
{ |
||||
|
c = this.ReadFix16(); |
||||
|
} |
||||
|
|
||||
|
if (type > 2 && type <= 4) |
||||
|
{ |
||||
|
d = this.ReadFix16(); |
||||
|
} |
||||
|
|
||||
|
if (type == 4) |
||||
|
{ |
||||
|
e = this.ReadFix16(); |
||||
|
f = this.ReadFix16(); |
||||
|
} |
||||
|
|
||||
|
switch (type) |
||||
|
{ |
||||
|
case 0: return new IccParametricCurve(gamma); |
||||
|
case 1: return new IccParametricCurve(gamma, a, b); |
||||
|
case 2: return new IccParametricCurve(gamma, a, b, c); |
||||
|
case 3: return new IccParametricCurve(gamma, a, b, c, d); |
||||
|
case 4: return new IccParametricCurve(gamma, a, b, c, d, e, f); |
||||
|
default: throw new InvalidIccProfileException($"Invalid parametric curve type of {type}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccCurveSegment"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read segment</returns>
|
||||
|
public IccCurveSegment ReadCurveSegment() |
||||
|
{ |
||||
|
var signature = (IccCurveSegmentSignature)this.ReadUInt32(); |
||||
|
this.AddIndex(4); // 4 bytes reserved
|
||||
|
|
||||
|
switch (signature) |
||||
|
{ |
||||
|
case IccCurveSegmentSignature.FormulaCurve: |
||||
|
return this.ReadFormulaCurveElement(); |
||||
|
case IccCurveSegmentSignature.SampledCurve: |
||||
|
return this.ReadSampledCurveElement(); |
||||
|
default: |
||||
|
throw new InvalidIccProfileException($"Invalid curve segment type of {signature}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccFormulaCurveElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read segment</returns>
|
||||
|
public IccFormulaCurveElement ReadFormulaCurveElement() |
||||
|
{ |
||||
|
var type = (IccFormulaCurveType)this.ReadUInt16(); |
||||
|
this.AddIndex(2); // 2 bytes reserved
|
||||
|
float gamma, a, b, c, d, e; |
||||
|
gamma = d = e = 0; |
||||
|
|
||||
|
if (type == IccFormulaCurveType.Type1 || type == IccFormulaCurveType.Type2) |
||||
|
{ |
||||
|
gamma = this.ReadSingle(); |
||||
|
} |
||||
|
|
||||
|
a = this.ReadSingle(); |
||||
|
b = this.ReadSingle(); |
||||
|
c = this.ReadSingle(); |
||||
|
|
||||
|
if (type == IccFormulaCurveType.Type2 || type == IccFormulaCurveType.Type3) |
||||
|
{ |
||||
|
d = this.ReadSingle(); |
||||
|
} |
||||
|
|
||||
|
if (type == IccFormulaCurveType.Type3) |
||||
|
{ |
||||
|
e = this.ReadSingle(); |
||||
|
} |
||||
|
|
||||
|
return new IccFormulaCurveElement(type, gamma, a, b, c, d, e); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccSampledCurveElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read segment</returns>
|
||||
|
public IccSampledCurveElement ReadSampledCurveElement() |
||||
|
{ |
||||
|
uint count = this.ReadUInt32(); |
||||
|
float[] entries = new float[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
entries[i] = this.ReadSingle(); |
||||
|
} |
||||
|
|
||||
|
return new IccSampledCurveElement(entries); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads curve data
|
||||
|
/// </summary>
|
||||
|
/// <param name="count">Number of input channels</param>
|
||||
|
/// <returns>The curve data</returns>
|
||||
|
private IccTagDataEntry[] ReadCurves(int count) |
||||
|
{ |
||||
|
IccTagDataEntry[] tdata = new IccTagDataEntry[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
IccTypeSignature type = this.ReadTagDataEntryHeader(); |
||||
|
if (type != IccTypeSignature.Curve && type != IccTypeSignature.ParametricCurve) |
||||
|
{ |
||||
|
throw new InvalidIccProfileException($"Curve has to be either \"{nameof(IccTypeSignature)}.{nameof(IccTypeSignature.Curve)}\" or" + |
||||
|
$" \"{nameof(IccTypeSignature)}.{nameof(IccTypeSignature.ParametricCurve)}\" for LutAToB- and LutBToA-TagDataEntries"); |
||||
|
} |
||||
|
|
||||
|
if (type == IccTypeSignature.Curve) |
||||
|
{ |
||||
|
tdata[i] = this.ReadCurveTagDataEntry(); |
||||
|
} |
||||
|
else if (type == IccTypeSignature.ParametricCurve) |
||||
|
{ |
||||
|
tdata[i] = this.ReadParametricCurveTagDataEntry(); |
||||
|
} |
||||
|
|
||||
|
this.AddPadding(); |
||||
|
} |
||||
|
|
||||
|
return tdata; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,173 @@ |
|||||
|
// <copyright file="IccDataReader.Lut.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods to read ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataReader |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Reads an 8bit lookup table
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read LUT</returns>
|
||||
|
public IccLut ReadLut8() |
||||
|
{ |
||||
|
return new IccLut(this.ReadBytes(256)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a 16bit lookup table
|
||||
|
/// </summary>
|
||||
|
/// <param name="count">The number of entries</param>
|
||||
|
/// <returns>The read LUT</returns>
|
||||
|
public IccLut ReadLut16(int count) |
||||
|
{ |
||||
|
ushort[] values = new ushort[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
values[i] = this.ReadUInt16(); |
||||
|
} |
||||
|
|
||||
|
return new IccLut(values); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a CLUT depending on type
|
||||
|
/// </summary>
|
||||
|
/// <param name="inChannelCount">Input channel count</param>
|
||||
|
/// <param name="outChannelCount">Output channel count</param>
|
||||
|
/// <param name="isFloat">If true, it's read as CLUTf32,
|
||||
|
/// else read as either CLUT8 or CLUT16 depending on embedded information</param>
|
||||
|
/// <returns>The read CLUT</returns>
|
||||
|
public IccClut ReadClut(int inChannelCount, int outChannelCount, bool isFloat) |
||||
|
{ |
||||
|
// Grid-points are always 16 bytes long but only 0-inChCount are used
|
||||
|
byte[] gridPointCount = new byte[inChannelCount]; |
||||
|
Buffer.BlockCopy(this.data, this.AddIndex(16), gridPointCount, 0, inChannelCount); |
||||
|
|
||||
|
if (!isFloat) |
||||
|
{ |
||||
|
byte size = this.data[this.AddIndex(4)]; // First byte is info, last 3 bytes are reserved
|
||||
|
if (size == 1) |
||||
|
{ |
||||
|
return this.ReadClut8(inChannelCount, outChannelCount, gridPointCount); |
||||
|
} |
||||
|
|
||||
|
if (size == 2) |
||||
|
{ |
||||
|
return this.ReadClut16(inChannelCount, outChannelCount, gridPointCount); |
||||
|
} |
||||
|
|
||||
|
throw new InvalidIccProfileException($"Invalid CLUT size of {size}"); |
||||
|
} |
||||
|
|
||||
|
return this.ReadClutF32(inChannelCount, outChannelCount, gridPointCount); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an 8 bit CLUT
|
||||
|
/// </summary>
|
||||
|
/// <param name="inChannelCount">Input channel count</param>
|
||||
|
/// <param name="outChannelCount">Output channel count</param>
|
||||
|
/// <param name="gridPointCount">Grid point count for each CLUT channel</param>
|
||||
|
/// <returns>The read CLUT8</returns>
|
||||
|
public IccClut ReadClut8(int inChannelCount, int outChannelCount, byte[] gridPointCount) |
||||
|
{ |
||||
|
int start = this.currentIndex; |
||||
|
int length = 0; |
||||
|
for (int i = 0; i < inChannelCount; i++) |
||||
|
{ |
||||
|
length += (int)Math.Pow(gridPointCount[i], inChannelCount); |
||||
|
} |
||||
|
|
||||
|
length /= inChannelCount; |
||||
|
|
||||
|
const float Max = byte.MaxValue; |
||||
|
|
||||
|
float[][] values = new float[length][]; |
||||
|
for (int i = 0; i < length; i++) |
||||
|
{ |
||||
|
values[i] = new float[outChannelCount]; |
||||
|
for (int j = 0; j < outChannelCount; j++) |
||||
|
{ |
||||
|
values[i][j] = this.data[this.currentIndex++] / Max; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.currentIndex = start + (length * outChannelCount); |
||||
|
return new IccClut(values, gridPointCount, IccClutDataType.UInt8); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a 16 bit CLUT
|
||||
|
/// </summary>
|
||||
|
/// <param name="inChannelCount">Input channel count</param>
|
||||
|
/// <param name="outChannelCount">Output channel count</param>
|
||||
|
/// <param name="gridPointCount">Grid point count for each CLUT channel</param>
|
||||
|
/// <returns>The read CLUT16</returns>
|
||||
|
public IccClut ReadClut16(int inChannelCount, int outChannelCount, byte[] gridPointCount) |
||||
|
{ |
||||
|
int start = this.currentIndex; |
||||
|
int length = 0; |
||||
|
for (int i = 0; i < inChannelCount; i++) |
||||
|
{ |
||||
|
length += (int)Math.Pow(gridPointCount[i], inChannelCount); |
||||
|
} |
||||
|
|
||||
|
length /= inChannelCount; |
||||
|
|
||||
|
const float Max = ushort.MaxValue; |
||||
|
|
||||
|
float[][] values = new float[length][]; |
||||
|
for (int i = 0; i < length; i++) |
||||
|
{ |
||||
|
values[i] = new float[outChannelCount]; |
||||
|
for (int j = 0; j < outChannelCount; j++) |
||||
|
{ |
||||
|
values[i][j] = this.ReadUInt16() / Max; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.currentIndex = start + (length * outChannelCount * 2); |
||||
|
return new IccClut(values, gridPointCount, IccClutDataType.UInt16); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a 32bit floating point CLUT
|
||||
|
/// </summary>
|
||||
|
/// <param name="inChCount">Input channel count</param>
|
||||
|
/// <param name="outChCount">Output channel count</param>
|
||||
|
/// <param name="gridPointCount">Grid point count for each CLUT channel</param>
|
||||
|
/// <returns>The read CLUTf32</returns>
|
||||
|
public IccClut ReadClutF32(int inChCount, int outChCount, byte[] gridPointCount) |
||||
|
{ |
||||
|
int start = this.currentIndex; |
||||
|
int length = 0; |
||||
|
for (int i = 0; i < inChCount; i++) |
||||
|
{ |
||||
|
length += (int)Math.Pow(gridPointCount[i], inChCount); |
||||
|
} |
||||
|
|
||||
|
length /= inChCount; |
||||
|
|
||||
|
float[][] values = new float[length][]; |
||||
|
for (int i = 0; i < length; i++) |
||||
|
{ |
||||
|
values[i] = new float[outChCount]; |
||||
|
for (int j = 0; j < outChCount; j++) |
||||
|
{ |
||||
|
values[i][j] = this.ReadSingle(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.currentIndex = start + (length * outChCount * 4); |
||||
|
return new IccClut(values, gridPointCount, IccClutDataType.Float); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
// <copyright file="IccDataReader.Matrix.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides methods to read ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataReader |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Reads a two dimensional matrix
|
||||
|
/// </summary>
|
||||
|
/// <param name="xCount">Number of values in X</param>
|
||||
|
/// <param name="yCount">Number of values in Y</param>
|
||||
|
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
||||
|
/// <returns>The read matrix</returns>
|
||||
|
public float[,] ReadMatrix(int xCount, int yCount, bool isSingle) |
||||
|
{ |
||||
|
float[,] matrix = new float[xCount, yCount]; |
||||
|
for (int y = 0; y < yCount; y++) |
||||
|
{ |
||||
|
for (int x = 0; x < xCount; x++) |
||||
|
{ |
||||
|
if (isSingle) |
||||
|
{ |
||||
|
matrix[x, y] = this.ReadSingle(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
matrix[x, y] = this.ReadFix16(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return matrix; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a one dimensional matrix
|
||||
|
/// </summary>
|
||||
|
/// <param name="yCount">Number of values</param>
|
||||
|
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
||||
|
/// <returns>The read matrix</returns>
|
||||
|
public float[] ReadMatrix(int yCount, bool isSingle) |
||||
|
{ |
||||
|
float[] matrix = new float[yCount]; |
||||
|
for (int i = 0; i < yCount; i++) |
||||
|
{ |
||||
|
if (isSingle) |
||||
|
{ |
||||
|
matrix[i] = this.ReadSingle(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
matrix[i] = this.ReadFix16(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return matrix; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,87 @@ |
|||||
|
// <copyright file="IccDataReader.MultiProcessElement.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides methods to read ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataReader |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccMultiProcessElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read <see cref="IccMultiProcessElement"/></returns>
|
||||
|
public IccMultiProcessElement ReadMultiProcessElement() |
||||
|
{ |
||||
|
IccMultiProcessElementSignature signature = (IccMultiProcessElementSignature)this.ReadUInt32(); |
||||
|
ushort inChannelCount = this.ReadUInt16(); |
||||
|
ushort outChannelCount = this.ReadUInt16(); |
||||
|
|
||||
|
switch (signature) |
||||
|
{ |
||||
|
case IccMultiProcessElementSignature.CurveSet: |
||||
|
return this.ReadCurveSetProcessElement(inChannelCount, outChannelCount); |
||||
|
case IccMultiProcessElementSignature.Matrix: |
||||
|
return this.ReadMatrixProcessElement(inChannelCount, outChannelCount); |
||||
|
case IccMultiProcessElementSignature.Clut: |
||||
|
return this.ReadClutProcessElement(inChannelCount, outChannelCount); |
||||
|
|
||||
|
// Currently just placeholders for future ICC expansion
|
||||
|
case IccMultiProcessElementSignature.BAcs: |
||||
|
this.AddIndex(8); |
||||
|
return new IccBAcsProcessElement(inChannelCount, outChannelCount); |
||||
|
case IccMultiProcessElementSignature.EAcs: |
||||
|
this.AddIndex(8); |
||||
|
return new IccEAcsProcessElement(inChannelCount, outChannelCount); |
||||
|
|
||||
|
default: |
||||
|
throw new InvalidIccProfileException($"Invalid MultiProcessElement type of {signature}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a CurveSet <see cref="IccMultiProcessElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="inChannelCount">Number of input channels</param>
|
||||
|
/// <param name="outChannelCount">Number of output channels</param>
|
||||
|
/// <returns>The read <see cref="IccCurveSetProcessElement"/></returns>
|
||||
|
public IccCurveSetProcessElement ReadCurveSetProcessElement(int inChannelCount, int outChannelCount) |
||||
|
{ |
||||
|
IccOneDimensionalCurve[] curves = new IccOneDimensionalCurve[inChannelCount]; |
||||
|
for (int i = 0; i < inChannelCount; i++) |
||||
|
{ |
||||
|
curves[i] = this.ReadOneDimensionalCurve(); |
||||
|
this.AddPadding(); |
||||
|
} |
||||
|
|
||||
|
return new IccCurveSetProcessElement(curves); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a Matrix <see cref="IccMultiProcessElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="inChannelCount">Number of input channels</param>
|
||||
|
/// <param name="outChannelCount">Number of output channels</param>
|
||||
|
/// <returns>The read <see cref="IccMatrixProcessElement"/></returns>
|
||||
|
public IccMatrixProcessElement ReadMatrixProcessElement(int inChannelCount, int outChannelCount) |
||||
|
{ |
||||
|
return new IccMatrixProcessElement( |
||||
|
this.ReadMatrix(inChannelCount, outChannelCount, true), |
||||
|
this.ReadMatrix(outChannelCount, true)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a CLUT <see cref="IccMultiProcessElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="inChannelCount">Number of input channels</param>
|
||||
|
/// <param name="outChannelCount">Number of output channels</param>
|
||||
|
/// <returns>The read <see cref="IccClutProcessElement"/></returns>
|
||||
|
public IccClutProcessElement ReadClutProcessElement(int inChannelCount, int outChannelCount) |
||||
|
{ |
||||
|
return new IccClutProcessElement(this.ReadClut(inChannelCount, outChannelCount, true)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,183 @@ |
|||||
|
// <copyright file="IccDataReader.NonPrimitives.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods to read ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataReader |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Reads a DateTime
|
||||
|
/// </summary>
|
||||
|
/// <returns>the value</returns>
|
||||
|
public DateTime ReadDateTime() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
return new DateTime( |
||||
|
year: this.ReadUInt16(), |
||||
|
month: this.ReadUInt16(), |
||||
|
day: this.ReadUInt16(), |
||||
|
hour: this.ReadUInt16(), |
||||
|
minute: this.ReadUInt16(), |
||||
|
second: this.ReadUInt16(), |
||||
|
kind: DateTimeKind.Utc); |
||||
|
} |
||||
|
catch (ArgumentOutOfRangeException) |
||||
|
{ |
||||
|
return DateTime.MinValue; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an ICC profile version number
|
||||
|
/// </summary>
|
||||
|
/// <returns>the version number</returns>
|
||||
|
public Version ReadVersionNumber() |
||||
|
{ |
||||
|
int version = this.ReadInt32(); |
||||
|
|
||||
|
int major = (version >> 24) & 0xFF; |
||||
|
int minor = (version >> 20) & 0x0F; |
||||
|
int bugfix = (version >> 16) & 0x0F; |
||||
|
|
||||
|
return new Version(major, minor, bugfix); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an XYZ number
|
||||
|
/// </summary>
|
||||
|
/// <returns>the XYZ number</returns>
|
||||
|
public Vector3 ReadXyzNumber() |
||||
|
{ |
||||
|
return new Vector3( |
||||
|
x: this.ReadFix16(), |
||||
|
y: this.ReadFix16(), |
||||
|
z: this.ReadFix16()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a profile ID
|
||||
|
/// </summary>
|
||||
|
/// <returns>the profile ID</returns>
|
||||
|
public IccProfileId ReadProfileId() |
||||
|
{ |
||||
|
return new IccProfileId( |
||||
|
p1: this.ReadUInt32(), |
||||
|
p2: this.ReadUInt32(), |
||||
|
p3: this.ReadUInt32(), |
||||
|
p4: this.ReadUInt32()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a position number
|
||||
|
/// </summary>
|
||||
|
/// <returns>the position number</returns>
|
||||
|
public IccPositionNumber ReadPositionNumber() |
||||
|
{ |
||||
|
return new IccPositionNumber( |
||||
|
offset: this.ReadUInt32(), |
||||
|
size: this.ReadUInt32()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a response number
|
||||
|
/// </summary>
|
||||
|
/// <returns>the response number</returns>
|
||||
|
public IccResponseNumber ReadResponseNumber() |
||||
|
{ |
||||
|
return new IccResponseNumber( |
||||
|
deviceCode: this.ReadUInt16(), |
||||
|
measurementValue: this.ReadFix16()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a named color
|
||||
|
/// </summary>
|
||||
|
/// <param name="deviceCoordCount">Number of device coordinates</param>
|
||||
|
/// <returns>the named color</returns>
|
||||
|
public IccNamedColor ReadNamedColor(uint deviceCoordCount) |
||||
|
{ |
||||
|
string name = this.ReadAsciiString(32); |
||||
|
ushort[] pcsCoord = { this.ReadUInt16(), this.ReadUInt16(), this.ReadUInt16() }; |
||||
|
ushort[] deviceCoord = new ushort[deviceCoordCount]; |
||||
|
|
||||
|
for (int i = 0; i < deviceCoordCount; i++) |
||||
|
{ |
||||
|
deviceCoord[i] = this.ReadUInt16(); |
||||
|
} |
||||
|
|
||||
|
return new IccNamedColor(name, pcsCoord, deviceCoord); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a profile description
|
||||
|
/// </summary>
|
||||
|
/// <returns>the profile description</returns>
|
||||
|
public IccProfileDescription ReadProfileDescription() |
||||
|
{ |
||||
|
uint manufacturer = this.ReadUInt32(); |
||||
|
uint model = this.ReadUInt32(); |
||||
|
var attributes = (IccDeviceAttribute)this.ReadInt64(); |
||||
|
var technologyInfo = (IccProfileTag)this.ReadUInt32(); |
||||
|
|
||||
|
IccMultiLocalizedUnicodeTagDataEntry manufacturerInfo = ReadText(); |
||||
|
IccMultiLocalizedUnicodeTagDataEntry modelInfo = ReadText(); |
||||
|
|
||||
|
return new IccProfileDescription( |
||||
|
manufacturer, |
||||
|
model, |
||||
|
attributes, |
||||
|
technologyInfo, |
||||
|
manufacturerInfo.Texts, |
||||
|
modelInfo.Texts); |
||||
|
|
||||
|
IccMultiLocalizedUnicodeTagDataEntry ReadText() |
||||
|
{ |
||||
|
IccTypeSignature type = this.ReadTagDataEntryHeader(); |
||||
|
switch (type) |
||||
|
{ |
||||
|
case IccTypeSignature.MultiLocalizedUnicode: |
||||
|
return this.ReadMultiLocalizedUnicodeTagDataEntry(); |
||||
|
case IccTypeSignature.TextDescription: |
||||
|
return (IccMultiLocalizedUnicodeTagDataEntry)this.ReadTextDescriptionTagDataEntry(); |
||||
|
|
||||
|
default: |
||||
|
throw new InvalidIccProfileException("Profile description can only have multi-localized Unicode or text description entries"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a colorant table entry
|
||||
|
/// </summary>
|
||||
|
/// <returns>the profile description</returns>
|
||||
|
public IccColorantTableEntry ReadColorantTableEntry() |
||||
|
{ |
||||
|
return new IccColorantTableEntry( |
||||
|
name: this.ReadAsciiString(32), |
||||
|
pcs1: this.ReadUInt16(), |
||||
|
pcs2: this.ReadUInt16(), |
||||
|
pcs3: this.ReadUInt16()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a screening channel
|
||||
|
/// </summary>
|
||||
|
/// <returns>the screening channel</returns>
|
||||
|
public IccScreeningChannel ReadScreeningChannel() |
||||
|
{ |
||||
|
return new IccScreeningChannel( |
||||
|
frequency: this.ReadFix16(), |
||||
|
angle: this.ReadFix16(), |
||||
|
spotShape: (IccScreeningSpotType)this.ReadInt32()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,178 @@ |
|||||
|
// <copyright file="IccDataReader.Primitives.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Text; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods to read ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataReader |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Reads an ushort
|
||||
|
/// </summary>
|
||||
|
/// <returns>the value</returns>
|
||||
|
public ushort ReadUInt16() |
||||
|
{ |
||||
|
return this.converter.ToUInt16(this.data, this.AddIndex(2)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a short
|
||||
|
/// </summary>
|
||||
|
/// <returns>the value</returns>
|
||||
|
public short ReadInt16() |
||||
|
{ |
||||
|
return this.converter.ToInt16(this.data, this.AddIndex(2)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an uint
|
||||
|
/// </summary>
|
||||
|
/// <returns>the value</returns>
|
||||
|
public uint ReadUInt32() |
||||
|
{ |
||||
|
return this.converter.ToUInt32(this.data, this.AddIndex(4)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an int
|
||||
|
/// </summary>
|
||||
|
/// <returns>the value</returns>
|
||||
|
public int ReadInt32() |
||||
|
{ |
||||
|
return this.converter.ToInt32(this.data, this.AddIndex(4)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an ulong
|
||||
|
/// </summary>
|
||||
|
/// <returns>the value</returns>
|
||||
|
public ulong ReadUInt64() |
||||
|
{ |
||||
|
return this.converter.ToUInt64(this.data, this.AddIndex(8)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a long
|
||||
|
/// </summary>
|
||||
|
/// <returns>the value</returns>
|
||||
|
public long ReadInt64() |
||||
|
{ |
||||
|
return this.converter.ToInt64(this.data, this.AddIndex(8)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a float
|
||||
|
/// </summary>
|
||||
|
/// <returns>the value</returns>
|
||||
|
public float ReadSingle() |
||||
|
{ |
||||
|
return this.converter.ToSingle(this.data, this.AddIndex(4)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a double
|
||||
|
/// </summary>
|
||||
|
/// <returns>the value</returns>
|
||||
|
public double ReadDouble() |
||||
|
{ |
||||
|
return this.converter.ToDouble(this.data, this.AddIndex(8)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an ASCII encoded string
|
||||
|
/// </summary>
|
||||
|
/// <param name="length">number of bytes to read</param>
|
||||
|
/// <returns>The value as a string</returns>
|
||||
|
public string ReadAsciiString(int length) |
||||
|
{ |
||||
|
if (length == 0) |
||||
|
{ |
||||
|
return string.Empty; |
||||
|
} |
||||
|
|
||||
|
Guard.MustBeGreaterThan(length, 0, nameof(length)); |
||||
|
string value = AsciiEncoding.GetString(this.data, this.AddIndex(length), length); |
||||
|
|
||||
|
// remove data after (potential) null terminator
|
||||
|
int pos = value.IndexOf('\0'); |
||||
|
if (pos >= 0) |
||||
|
{ |
||||
|
value = value.Substring(0, pos); |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an UTF-16 big-endian encoded string
|
||||
|
/// </summary>
|
||||
|
/// <param name="length">number of bytes to read</param>
|
||||
|
/// <returns>The value as a string</returns>
|
||||
|
public string ReadUnicodeString(int length) |
||||
|
{ |
||||
|
if (length == 0) |
||||
|
{ |
||||
|
return string.Empty; |
||||
|
} |
||||
|
|
||||
|
Guard.MustBeGreaterThan(length, 0, nameof(length)); |
||||
|
|
||||
|
return Encoding.BigEndianUnicode.GetString(this.data, this.AddIndex(length), length); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a signed 32bit number with 1 sign bit, 15 value bits and 16 fractional bits
|
||||
|
/// </summary>
|
||||
|
/// <returns>The number as double</returns>
|
||||
|
public float ReadFix16() |
||||
|
{ |
||||
|
return this.ReadInt32() / 65536f; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an unsigned 32bit number with 16 value bits and 16 fractional bits
|
||||
|
/// </summary>
|
||||
|
/// <returns>The number as double</returns>
|
||||
|
public float ReadUFix16() |
||||
|
{ |
||||
|
return this.ReadUInt32() / 65536f; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an unsigned 16bit number with 1 value bit and 15 fractional bits
|
||||
|
/// </summary>
|
||||
|
/// <returns>The number as double</returns>
|
||||
|
public float ReadU1Fix15() |
||||
|
{ |
||||
|
return this.ReadUInt16() / 32768f; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an unsigned 16bit number with 8 value bits and 8 fractional bits
|
||||
|
/// </summary>
|
||||
|
/// <returns>The number as double</returns>
|
||||
|
public float ReadUFix8() |
||||
|
{ |
||||
|
return this.ReadUInt16() / 256f; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a number of bytes and advances the index
|
||||
|
/// </summary>
|
||||
|
/// <param name="count">The number of bytes to read</param>
|
||||
|
/// <returns>The read bytes</returns>
|
||||
|
public byte[] ReadBytes(int count) |
||||
|
{ |
||||
|
byte[] bytes = new byte[count]; |
||||
|
Buffer.BlockCopy(this.data, this.AddIndex(count), bytes, 0, count); |
||||
|
return bytes; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,875 @@ |
|||||
|
// <copyright file="IccDataReader.TagDataEntry.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Globalization; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods to read ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataReader |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Reads a tag data entry
|
||||
|
/// </summary>
|
||||
|
/// <param name="info">The table entry with reading information</param>
|
||||
|
/// <returns>the tag data entry</returns>
|
||||
|
public IccTagDataEntry ReadTagDataEntry(IccTagTableEntry info) |
||||
|
{ |
||||
|
this.currentIndex = (int)info.Offset; |
||||
|
IccTypeSignature type = this.ReadTagDataEntryHeader(); |
||||
|
|
||||
|
switch (type) |
||||
|
{ |
||||
|
case IccTypeSignature.Chromaticity: |
||||
|
return this.ReadChromaticityTagDataEntry(); |
||||
|
case IccTypeSignature.ColorantOrder: |
||||
|
return this.ReadColorantOrderTagDataEntry(); |
||||
|
case IccTypeSignature.ColorantTable: |
||||
|
return this.ReadColorantTableTagDataEntry(); |
||||
|
case IccTypeSignature.Curve: |
||||
|
return this.ReadCurveTagDataEntry(); |
||||
|
case IccTypeSignature.Data: |
||||
|
return this.ReadDataTagDataEntry(info.DataSize); |
||||
|
case IccTypeSignature.DateTime: |
||||
|
return this.ReadDateTimeTagDataEntry(); |
||||
|
case IccTypeSignature.Lut16: |
||||
|
return this.ReadLut16TagDataEntry(); |
||||
|
case IccTypeSignature.Lut8: |
||||
|
return this.ReadLut8TagDataEntry(); |
||||
|
case IccTypeSignature.LutAToB: |
||||
|
return this.ReadLutAtoBTagDataEntry(); |
||||
|
case IccTypeSignature.LutBToA: |
||||
|
return this.ReadLutBtoATagDataEntry(); |
||||
|
case IccTypeSignature.Measurement: |
||||
|
return this.ReadMeasurementTagDataEntry(); |
||||
|
case IccTypeSignature.MultiLocalizedUnicode: |
||||
|
return this.ReadMultiLocalizedUnicodeTagDataEntry(); |
||||
|
case IccTypeSignature.MultiProcessElements: |
||||
|
return this.ReadMultiProcessElementsTagDataEntry(); |
||||
|
case IccTypeSignature.NamedColor2: |
||||
|
return this.ReadNamedColor2TagDataEntry(); |
||||
|
case IccTypeSignature.ParametricCurve: |
||||
|
return this.ReadParametricCurveTagDataEntry(); |
||||
|
case IccTypeSignature.ProfileSequenceDesc: |
||||
|
return this.ReadProfileSequenceDescTagDataEntry(); |
||||
|
case IccTypeSignature.ProfileSequenceIdentifier: |
||||
|
return this.ReadProfileSequenceIdentifierTagDataEntry(); |
||||
|
case IccTypeSignature.ResponseCurveSet16: |
||||
|
return this.ReadResponseCurveSet16TagDataEntry(); |
||||
|
case IccTypeSignature.S15Fixed16Array: |
||||
|
return this.ReadFix16ArrayTagDataEntry(info.DataSize); |
||||
|
case IccTypeSignature.Signature: |
||||
|
return this.ReadSignatureTagDataEntry(); |
||||
|
case IccTypeSignature.Text: |
||||
|
return this.ReadTextTagDataEntry(info.DataSize); |
||||
|
case IccTypeSignature.U16Fixed16Array: |
||||
|
return this.ReadUFix16ArrayTagDataEntry(info.DataSize); |
||||
|
case IccTypeSignature.UInt16Array: |
||||
|
return this.ReadUInt16ArrayTagDataEntry(info.DataSize); |
||||
|
case IccTypeSignature.UInt32Array: |
||||
|
return this.ReadUInt32ArrayTagDataEntry(info.DataSize); |
||||
|
case IccTypeSignature.UInt64Array: |
||||
|
return this.ReadUInt64ArrayTagDataEntry(info.DataSize); |
||||
|
case IccTypeSignature.UInt8Array: |
||||
|
return this.ReadUInt8ArrayTagDataEntry(info.DataSize); |
||||
|
case IccTypeSignature.ViewingConditions: |
||||
|
return this.ReadViewingConditionsTagDataEntry(); |
||||
|
case IccTypeSignature.Xyz: |
||||
|
return this.ReadXyzTagDataEntry(info.DataSize); |
||||
|
|
||||
|
// V2 Types:
|
||||
|
case IccTypeSignature.TextDescription: |
||||
|
return this.ReadTextDescriptionTagDataEntry(); |
||||
|
case IccTypeSignature.CrdInfo: |
||||
|
return this.ReadCrdInfoTagDataEntry(); |
||||
|
case IccTypeSignature.Screening: |
||||
|
return this.ReadScreeningTagDataEntry(); |
||||
|
case IccTypeSignature.UcrBg: |
||||
|
return this.ReadUcrBgTagDataEntry(info.DataSize); |
||||
|
|
||||
|
// Unsupported or unknown
|
||||
|
case IccTypeSignature.DeviceSettings: |
||||
|
case IccTypeSignature.NamedColor: |
||||
|
case IccTypeSignature.Unknown: |
||||
|
default: |
||||
|
return this.ReadUnknownTagDataEntry(info.DataSize); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads the header of a <see cref="IccTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read signature</returns>
|
||||
|
public IccTypeSignature ReadTagDataEntryHeader() |
||||
|
{ |
||||
|
var type = (IccTypeSignature)this.ReadUInt32(); |
||||
|
this.AddIndex(4); // 4 bytes are not used
|
||||
|
return type; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads the header of a <see cref="IccTagDataEntry"/> and checks if it's the expected value
|
||||
|
/// </summary>
|
||||
|
/// <param name="expected">expected value to check against</param>
|
||||
|
public void ReadCheckTagDataEntryHeader(IccTypeSignature expected) |
||||
|
{ |
||||
|
IccTypeSignature type = this.ReadTagDataEntryHeader(); |
||||
|
if (expected != (IccTypeSignature)uint.MaxValue && type != expected) |
||||
|
{ |
||||
|
throw new InvalidIccProfileException($"Read signature {type} is not the expected {expected}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccTagDataEntry"/> with an unknown <see cref="IccTypeSignature"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccUnknownTagDataEntry ReadUnknownTagDataEntry(uint size) |
||||
|
{ |
||||
|
int count = (int)size - 8; // 8 is the tag header size
|
||||
|
return new IccUnknownTagDataEntry(this.ReadBytes(count)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccChromaticityTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccChromaticityTagDataEntry ReadChromaticityTagDataEntry() |
||||
|
{ |
||||
|
ushort channelCount = this.ReadUInt16(); |
||||
|
var colorant = (IccColorantEncoding)this.ReadUInt16(); |
||||
|
|
||||
|
if (Enum.IsDefined(typeof(IccColorantEncoding), colorant) && colorant != IccColorantEncoding.Unknown) |
||||
|
{ |
||||
|
// The type is known and so are the values (they are constant)
|
||||
|
// channelCount should always be 3 but it doesn't really matter if it's not
|
||||
|
return new IccChromaticityTagDataEntry(colorant); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// The type is not know, so the values need be read
|
||||
|
double[][] values = new double[channelCount][]; |
||||
|
for (int i = 0; i < channelCount; i++) |
||||
|
{ |
||||
|
values[i] = new double[2]; |
||||
|
values[i][0] = this.ReadUFix16(); |
||||
|
values[i][1] = this.ReadUFix16(); |
||||
|
} |
||||
|
|
||||
|
return new IccChromaticityTagDataEntry(values); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccColorantOrderTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccColorantOrderTagDataEntry ReadColorantOrderTagDataEntry() |
||||
|
{ |
||||
|
uint colorantCount = this.ReadUInt32(); |
||||
|
byte[] number = this.ReadBytes((int)colorantCount); |
||||
|
return new IccColorantOrderTagDataEntry(number); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccColorantTableTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccColorantTableTagDataEntry ReadColorantTableTagDataEntry() |
||||
|
{ |
||||
|
uint colorantCount = this.ReadUInt32(); |
||||
|
IccColorantTableEntry[] cdata = new IccColorantTableEntry[colorantCount]; |
||||
|
for (int i = 0; i < colorantCount; i++) |
||||
|
{ |
||||
|
cdata[i] = this.ReadColorantTableEntry(); |
||||
|
} |
||||
|
|
||||
|
return new IccColorantTableTagDataEntry(cdata); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccCurveTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccCurveTagDataEntry ReadCurveTagDataEntry() |
||||
|
{ |
||||
|
uint pointCount = this.ReadUInt32(); |
||||
|
|
||||
|
if (pointCount == 0) |
||||
|
{ |
||||
|
return new IccCurveTagDataEntry(); |
||||
|
} |
||||
|
|
||||
|
if (pointCount == 1) |
||||
|
{ |
||||
|
return new IccCurveTagDataEntry(this.ReadUFix8()); |
||||
|
} |
||||
|
|
||||
|
float[] cdata = new float[pointCount]; |
||||
|
for (int i = 0; i < pointCount; i++) |
||||
|
{ |
||||
|
cdata[i] = this.ReadUInt16() / 65535f; |
||||
|
} |
||||
|
|
||||
|
return new IccCurveTagDataEntry(cdata); |
||||
|
|
||||
|
// TODO: If the input is PCSXYZ, 1+(32 767/32 768) shall be mapped to the value 1,0. If the output is PCSXYZ, the value 1,0 shall be mapped to 1+(32 767/32 768).
|
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccDataTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccDataTagDataEntry ReadDataTagDataEntry(uint size) |
||||
|
{ |
||||
|
this.AddIndex(3); // first 3 bytes are zero
|
||||
|
byte b = this.data[this.AddIndex(1)]; |
||||
|
|
||||
|
// last bit of 4th byte is either 0 = ASCII or 1 = binary
|
||||
|
bool ascii = this.GetBit(b, 7); |
||||
|
int length = (int)size - 12; |
||||
|
byte[] cdata = this.ReadBytes(length); |
||||
|
|
||||
|
return new IccDataTagDataEntry(cdata, ascii); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccDateTimeTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccDateTimeTagDataEntry ReadDateTimeTagDataEntry() |
||||
|
{ |
||||
|
return new IccDateTimeTagDataEntry(this.ReadDateTime()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccLut16TagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccLut16TagDataEntry ReadLut16TagDataEntry() |
||||
|
{ |
||||
|
byte inChCount = this.data[this.AddIndex(1)]; |
||||
|
byte outChCount = this.data[this.AddIndex(1)]; |
||||
|
byte clutPointCount = this.data[this.AddIndex(1)]; |
||||
|
this.AddIndex(1); // 1 byte reserved
|
||||
|
|
||||
|
float[,] matrix = this.ReadMatrix(3, 3, false); |
||||
|
|
||||
|
ushort inTableCount = this.ReadUInt16(); |
||||
|
ushort outTableCount = this.ReadUInt16(); |
||||
|
|
||||
|
// Input LUT
|
||||
|
IccLut[] inValues = new IccLut[inChCount]; |
||||
|
byte[] gridPointCount = new byte[inChCount]; |
||||
|
for (int i = 0; i < inChCount; i++) |
||||
|
{ |
||||
|
inValues[i] = this.ReadLut16(inTableCount); |
||||
|
gridPointCount[i] = clutPointCount; |
||||
|
} |
||||
|
|
||||
|
// CLUT
|
||||
|
IccClut clut = this.ReadClut16(inChCount, outChCount, gridPointCount); |
||||
|
|
||||
|
// Output LUT
|
||||
|
IccLut[] outValues = new IccLut[outChCount]; |
||||
|
for (int i = 0; i < outChCount; i++) |
||||
|
{ |
||||
|
outValues[i] = this.ReadLut16(outTableCount); |
||||
|
} |
||||
|
|
||||
|
return new IccLut16TagDataEntry(matrix, inValues, clut, outValues); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccLut8TagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccLut8TagDataEntry ReadLut8TagDataEntry() |
||||
|
{ |
||||
|
byte inChCount = this.data[this.AddIndex(1)]; |
||||
|
byte outChCount = this.data[this.AddIndex(1)]; |
||||
|
byte clutPointCount = this.data[this.AddIndex(1)]; |
||||
|
this.AddIndex(1); // 1 byte reserved
|
||||
|
|
||||
|
float[,] matrix = this.ReadMatrix(3, 3, false); |
||||
|
|
||||
|
// Input LUT
|
||||
|
IccLut[] inValues = new IccLut[inChCount]; |
||||
|
byte[] gridPointCount = new byte[inChCount]; |
||||
|
for (int i = 0; i < inChCount; i++) |
||||
|
{ |
||||
|
inValues[i] = this.ReadLut8(); |
||||
|
gridPointCount[i] = clutPointCount; |
||||
|
} |
||||
|
|
||||
|
// CLUT
|
||||
|
IccClut clut = this.ReadClut8(inChCount, outChCount, gridPointCount); |
||||
|
|
||||
|
// Output LUT
|
||||
|
IccLut[] outValues = new IccLut[outChCount]; |
||||
|
for (int i = 0; i < outChCount; i++) |
||||
|
{ |
||||
|
outValues[i] = this.ReadLut8(); |
||||
|
} |
||||
|
|
||||
|
return new IccLut8TagDataEntry(matrix, inValues, clut, outValues); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccLutAToBTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccLutAToBTagDataEntry ReadLutAtoBTagDataEntry() |
||||
|
{ |
||||
|
int start = this.currentIndex - 8; // 8 is the tag header size
|
||||
|
|
||||
|
byte inChCount = this.data[this.AddIndex(1)]; |
||||
|
byte outChCount = this.data[this.AddIndex(1)]; |
||||
|
this.AddIndex(2); // 2 bytes reserved
|
||||
|
|
||||
|
uint bCurveOffset = this.ReadUInt32(); |
||||
|
uint matrixOffset = this.ReadUInt32(); |
||||
|
uint mCurveOffset = this.ReadUInt32(); |
||||
|
uint clutOffset = this.ReadUInt32(); |
||||
|
uint aCurveOffset = this.ReadUInt32(); |
||||
|
|
||||
|
IccTagDataEntry[] bCurve = null; |
||||
|
IccTagDataEntry[] mCurve = null; |
||||
|
IccTagDataEntry[] aCurve = null; |
||||
|
IccClut clut = null; |
||||
|
float[,] matrix3x3 = null; |
||||
|
float[] matrix3x1 = null; |
||||
|
|
||||
|
if (bCurveOffset != 0) |
||||
|
{ |
||||
|
this.currentIndex = (int)bCurveOffset + start; |
||||
|
bCurve = this.ReadCurves(outChCount); |
||||
|
} |
||||
|
|
||||
|
if (mCurveOffset != 0) |
||||
|
{ |
||||
|
this.currentIndex = (int)mCurveOffset + start; |
||||
|
mCurve = this.ReadCurves(outChCount); |
||||
|
} |
||||
|
|
||||
|
if (aCurveOffset != 0) |
||||
|
{ |
||||
|
this.currentIndex = (int)aCurveOffset + start; |
||||
|
aCurve = this.ReadCurves(inChCount); |
||||
|
} |
||||
|
|
||||
|
if (clutOffset != 0) |
||||
|
{ |
||||
|
this.currentIndex = (int)clutOffset + start; |
||||
|
clut = this.ReadClut(inChCount, outChCount, false); |
||||
|
} |
||||
|
|
||||
|
if (matrixOffset != 0) |
||||
|
{ |
||||
|
this.currentIndex = (int)matrixOffset + start; |
||||
|
matrix3x3 = this.ReadMatrix(3, 3, false); |
||||
|
matrix3x1 = this.ReadMatrix(3, false); |
||||
|
} |
||||
|
|
||||
|
return new IccLutAToBTagDataEntry(bCurve, matrix3x3, matrix3x1, mCurve, clut, aCurve); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccLutBToATagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccLutBToATagDataEntry ReadLutBtoATagDataEntry() |
||||
|
{ |
||||
|
int start = this.currentIndex - 8; // 8 is the tag header size
|
||||
|
|
||||
|
byte inChCount = this.data[this.AddIndex(1)]; |
||||
|
byte outChCount = this.data[this.AddIndex(1)]; |
||||
|
this.AddIndex(2); // 2 bytes reserved
|
||||
|
|
||||
|
uint bCurveOffset = this.ReadUInt32(); |
||||
|
uint matrixOffset = this.ReadUInt32(); |
||||
|
uint mCurveOffset = this.ReadUInt32(); |
||||
|
uint clutOffset = this.ReadUInt32(); |
||||
|
uint aCurveOffset = this.ReadUInt32(); |
||||
|
|
||||
|
IccTagDataEntry[] bCurve = null; |
||||
|
IccTagDataEntry[] mCurve = null; |
||||
|
IccTagDataEntry[] aCurve = null; |
||||
|
IccClut clut = null; |
||||
|
float[,] matrix3x3 = null; |
||||
|
float[] matrix3x1 = null; |
||||
|
|
||||
|
if (bCurveOffset != 0) |
||||
|
{ |
||||
|
this.currentIndex = (int)bCurveOffset + start; |
||||
|
bCurve = this.ReadCurves(inChCount); |
||||
|
} |
||||
|
|
||||
|
if (mCurveOffset != 0) |
||||
|
{ |
||||
|
this.currentIndex = (int)mCurveOffset + start; |
||||
|
mCurve = this.ReadCurves(inChCount); |
||||
|
} |
||||
|
|
||||
|
if (aCurveOffset != 0) |
||||
|
{ |
||||
|
this.currentIndex = (int)aCurveOffset + start; |
||||
|
aCurve = this.ReadCurves(outChCount); |
||||
|
} |
||||
|
|
||||
|
if (clutOffset != 0) |
||||
|
{ |
||||
|
this.currentIndex = (int)clutOffset + start; |
||||
|
clut = this.ReadClut(inChCount, outChCount, false); |
||||
|
} |
||||
|
|
||||
|
if (matrixOffset != 0) |
||||
|
{ |
||||
|
this.currentIndex = (int)matrixOffset + start; |
||||
|
matrix3x3 = this.ReadMatrix(3, 3, false); |
||||
|
matrix3x1 = this.ReadMatrix(3, false); |
||||
|
} |
||||
|
|
||||
|
return new IccLutBToATagDataEntry(bCurve, matrix3x3, matrix3x1, mCurve, clut, aCurve); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccMeasurementTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccMeasurementTagDataEntry ReadMeasurementTagDataEntry() |
||||
|
{ |
||||
|
return new IccMeasurementTagDataEntry( |
||||
|
observer: (IccStandardObserver)this.ReadUInt32(), |
||||
|
xyzBacking: this.ReadXyzNumber(), |
||||
|
geometry: (IccMeasurementGeometry)this.ReadUInt32(), |
||||
|
flare: this.ReadUFix16(), |
||||
|
illuminant: (IccStandardIlluminant)this.ReadUInt32()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccMultiLocalizedUnicodeTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccMultiLocalizedUnicodeTagDataEntry ReadMultiLocalizedUnicodeTagDataEntry() |
||||
|
{ |
||||
|
int start = this.currentIndex - 8; // 8 is the tag header size
|
||||
|
uint recordCount = this.ReadUInt32(); |
||||
|
|
||||
|
// TODO: Why are we storing variable
|
||||
|
uint recordSize = this.ReadUInt32(); |
||||
|
IccLocalizedString[] text = new IccLocalizedString[recordCount]; |
||||
|
|
||||
|
string[] culture = new string[recordCount]; |
||||
|
uint[] length = new uint[recordCount]; |
||||
|
uint[] offset = new uint[recordCount]; |
||||
|
|
||||
|
for (int i = 0; i < recordCount; i++) |
||||
|
{ |
||||
|
culture[i] = $"{this.ReadAsciiString(2)}-{this.ReadAsciiString(2)}"; |
||||
|
length[i] = this.ReadUInt32(); |
||||
|
offset[i] = this.ReadUInt32(); |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < recordCount; i++) |
||||
|
{ |
||||
|
this.currentIndex = (int)(start + offset[i]); |
||||
|
text[i] = new IccLocalizedString(new CultureInfo(culture[i]), this.ReadUnicodeString((int)length[i])); |
||||
|
} |
||||
|
|
||||
|
return new IccMultiLocalizedUnicodeTagDataEntry(text); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccMultiProcessElementsTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccMultiProcessElementsTagDataEntry ReadMultiProcessElementsTagDataEntry() |
||||
|
{ |
||||
|
int start = this.currentIndex - 8; |
||||
|
|
||||
|
// TODO: Why are we storing variable
|
||||
|
ushort inChannelCount = this.ReadUInt16(); |
||||
|
ushort outChannelCount = this.ReadUInt16(); |
||||
|
uint elementCount = this.ReadUInt32(); |
||||
|
|
||||
|
IccPositionNumber[] positionTable = new IccPositionNumber[elementCount]; |
||||
|
for (int i = 0; i < elementCount; i++) |
||||
|
{ |
||||
|
positionTable[i] = this.ReadPositionNumber(); |
||||
|
} |
||||
|
|
||||
|
IccMultiProcessElement[] elements = new IccMultiProcessElement[elementCount]; |
||||
|
for (int i = 0; i < elementCount; i++) |
||||
|
{ |
||||
|
this.currentIndex = (int)positionTable[i].Offset + start; |
||||
|
elements[i] = this.ReadMultiProcessElement(); |
||||
|
} |
||||
|
|
||||
|
return new IccMultiProcessElementsTagDataEntry(elements); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccNamedColor2TagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccNamedColor2TagDataEntry ReadNamedColor2TagDataEntry() |
||||
|
{ |
||||
|
int vendorFlag = this.ReadInt32(); |
||||
|
uint colorCount = this.ReadUInt32(); |
||||
|
uint coordCount = this.ReadUInt32(); |
||||
|
string prefix = this.ReadAsciiString(32); |
||||
|
string suffix = this.ReadAsciiString(32); |
||||
|
|
||||
|
IccNamedColor[] colors = new IccNamedColor[colorCount]; |
||||
|
for (int i = 0; i < colorCount; i++) |
||||
|
{ |
||||
|
colors[i] = this.ReadNamedColor(coordCount); |
||||
|
} |
||||
|
|
||||
|
return new IccNamedColor2TagDataEntry(vendorFlag, prefix, suffix, colors); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccParametricCurveTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccParametricCurveTagDataEntry ReadParametricCurveTagDataEntry() |
||||
|
{ |
||||
|
return new IccParametricCurveTagDataEntry(this.ReadParametricCurve()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccProfileSequenceDescTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccProfileSequenceDescTagDataEntry ReadProfileSequenceDescTagDataEntry() |
||||
|
{ |
||||
|
uint count = this.ReadUInt32(); |
||||
|
IccProfileDescription[] description = new IccProfileDescription[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
description[i] = this.ReadProfileDescription(); |
||||
|
} |
||||
|
|
||||
|
return new IccProfileSequenceDescTagDataEntry(description); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccProfileSequenceIdentifierTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccProfileSequenceIdentifierTagDataEntry ReadProfileSequenceIdentifierTagDataEntry() |
||||
|
{ |
||||
|
int start = this.currentIndex - 8; // 8 is the tag header size
|
||||
|
uint count = this.ReadUInt32(); |
||||
|
IccPositionNumber[] table = new IccPositionNumber[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
table[i] = this.ReadPositionNumber(); |
||||
|
} |
||||
|
|
||||
|
IccProfileSequenceIdentifier[] entries = new IccProfileSequenceIdentifier[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
this.currentIndex = (int)(start + table[i].Offset); |
||||
|
IccProfileId id = this.ReadProfileId(); |
||||
|
this.ReadCheckTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode); |
||||
|
IccMultiLocalizedUnicodeTagDataEntry description = this.ReadMultiLocalizedUnicodeTagDataEntry(); |
||||
|
entries[i] = new IccProfileSequenceIdentifier(id, description.Texts); |
||||
|
} |
||||
|
|
||||
|
return new IccProfileSequenceIdentifierTagDataEntry(entries); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccResponseCurveSet16TagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccResponseCurveSet16TagDataEntry ReadResponseCurveSet16TagDataEntry() |
||||
|
{ |
||||
|
int start = this.currentIndex - 8; // 8 is the tag header size
|
||||
|
ushort channelCount = this.ReadUInt16(); |
||||
|
ushort measurmentCount = this.ReadUInt16(); |
||||
|
|
||||
|
uint[] offset = new uint[measurmentCount]; |
||||
|
for (int i = 0; i < measurmentCount; i++) |
||||
|
{ |
||||
|
offset[i] = this.ReadUInt32(); |
||||
|
} |
||||
|
|
||||
|
IccResponseCurve[] curves = new IccResponseCurve[measurmentCount]; |
||||
|
for (int i = 0; i < measurmentCount; i++) |
||||
|
{ |
||||
|
this.currentIndex = (int)(start + offset[i]); |
||||
|
curves[i] = this.ReadResponseCurve(channelCount); |
||||
|
} |
||||
|
|
||||
|
return new IccResponseCurveSet16TagDataEntry(curves); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccFix16ArrayTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccFix16ArrayTagDataEntry ReadFix16ArrayTagDataEntry(uint size) |
||||
|
{ |
||||
|
uint count = (size - 8) / 4; |
||||
|
float[] arrayData = new float[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
arrayData[i] = this.ReadFix16() / 256f; |
||||
|
} |
||||
|
|
||||
|
return new IccFix16ArrayTagDataEntry(arrayData); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccSignatureTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccSignatureTagDataEntry ReadSignatureTagDataEntry() |
||||
|
{ |
||||
|
return new IccSignatureTagDataEntry(this.ReadAsciiString(4)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccTextTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccTextTagDataEntry ReadTextTagDataEntry(uint size) |
||||
|
{ |
||||
|
return new IccTextTagDataEntry(this.ReadAsciiString((int)size - 8)); // 8 is the tag header size
|
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccUFix16ArrayTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccUFix16ArrayTagDataEntry ReadUFix16ArrayTagDataEntry(uint size) |
||||
|
{ |
||||
|
uint count = (size - 8) / 4; |
||||
|
float[] arrayData = new float[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
arrayData[i] = this.ReadUFix16(); |
||||
|
} |
||||
|
|
||||
|
return new IccUFix16ArrayTagDataEntry(arrayData); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccUInt16ArrayTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccUInt16ArrayTagDataEntry ReadUInt16ArrayTagDataEntry(uint size) |
||||
|
{ |
||||
|
uint count = (size - 8) / 2; |
||||
|
ushort[] arrayData = new ushort[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
arrayData[i] = this.ReadUInt16(); |
||||
|
} |
||||
|
|
||||
|
return new IccUInt16ArrayTagDataEntry(arrayData); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccUInt32ArrayTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccUInt32ArrayTagDataEntry ReadUInt32ArrayTagDataEntry(uint size) |
||||
|
{ |
||||
|
uint count = (size - 8) / 4; |
||||
|
uint[] arrayData = new uint[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
arrayData[i] = this.ReadUInt32(); |
||||
|
} |
||||
|
|
||||
|
return new IccUInt32ArrayTagDataEntry(arrayData); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccUInt64ArrayTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccUInt64ArrayTagDataEntry ReadUInt64ArrayTagDataEntry(uint size) |
||||
|
{ |
||||
|
uint count = (size - 8) / 8; |
||||
|
ulong[] arrayData = new ulong[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
arrayData[i] = this.ReadUInt64(); |
||||
|
} |
||||
|
|
||||
|
return new IccUInt64ArrayTagDataEntry(arrayData); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccUInt8ArrayTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccUInt8ArrayTagDataEntry ReadUInt8ArrayTagDataEntry(uint size) |
||||
|
{ |
||||
|
int count = (int)size - 8; // 8 is the tag header size
|
||||
|
byte[] adata = this.ReadBytes(count); |
||||
|
|
||||
|
return new IccUInt8ArrayTagDataEntry(adata); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccViewingConditionsTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccViewingConditionsTagDataEntry ReadViewingConditionsTagDataEntry() |
||||
|
{ |
||||
|
return new IccViewingConditionsTagDataEntry( |
||||
|
illuminantXyz: this.ReadXyzNumber(), |
||||
|
surroundXyz: this.ReadXyzNumber(), |
||||
|
illuminant: (IccStandardIlluminant)this.ReadUInt32()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccXyzTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccXyzTagDataEntry ReadXyzTagDataEntry(uint size) |
||||
|
{ |
||||
|
uint count = (size - 8) / 12; |
||||
|
Vector3[] arrayData = new Vector3[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
arrayData[i] = this.ReadXyzNumber(); |
||||
|
} |
||||
|
|
||||
|
return new IccXyzTagDataEntry(arrayData); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccTextDescriptionTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccTextDescriptionTagDataEntry ReadTextDescriptionTagDataEntry() |
||||
|
{ |
||||
|
string unicodeValue, scriptcodeValue; |
||||
|
string asciiValue = unicodeValue = scriptcodeValue = null; |
||||
|
|
||||
|
int asciiCount = (int)this.ReadUInt32(); |
||||
|
if (asciiCount > 0) |
||||
|
{ |
||||
|
asciiValue = this.ReadAsciiString(asciiCount - 1); |
||||
|
this.AddIndex(1); // Null terminator
|
||||
|
} |
||||
|
|
||||
|
uint unicodeLangCode = this.ReadUInt32(); |
||||
|
int unicodeCount = (int)this.ReadUInt32(); |
||||
|
if (unicodeCount > 0) |
||||
|
{ |
||||
|
unicodeValue = this.ReadUnicodeString((unicodeCount * 2) - 2); |
||||
|
this.AddIndex(2); // Null terminator
|
||||
|
} |
||||
|
|
||||
|
ushort scriptcodeCode = this.ReadUInt16(); |
||||
|
int scriptcodeCount = Math.Min(this.data[this.AddIndex(1)], (byte)67); |
||||
|
if (scriptcodeCount > 0) |
||||
|
{ |
||||
|
scriptcodeValue = this.ReadAsciiString(scriptcodeCount - 1); |
||||
|
this.AddIndex(1); // Null terminator
|
||||
|
} |
||||
|
|
||||
|
return new IccTextDescriptionTagDataEntry( |
||||
|
asciiValue, |
||||
|
unicodeValue, |
||||
|
scriptcodeValue, |
||||
|
unicodeLangCode, |
||||
|
scriptcodeCode); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccTextDescriptionTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccCrdInfoTagDataEntry ReadCrdInfoTagDataEntry() |
||||
|
{ |
||||
|
uint productNameCount = this.ReadUInt32(); |
||||
|
string productName = this.ReadAsciiString((int)productNameCount); |
||||
|
|
||||
|
uint crd0Count = this.ReadUInt32(); |
||||
|
string crd0Name = this.ReadAsciiString((int)crd0Count); |
||||
|
|
||||
|
uint crd1Count = this.ReadUInt32(); |
||||
|
string crd1Name = this.ReadAsciiString((int)crd1Count); |
||||
|
|
||||
|
uint crd2Count = this.ReadUInt32(); |
||||
|
string crd2Name = this.ReadAsciiString((int)crd2Count); |
||||
|
|
||||
|
uint crd3Count = this.ReadUInt32(); |
||||
|
string crd3Name = this.ReadAsciiString((int)crd3Count); |
||||
|
|
||||
|
return new IccCrdInfoTagDataEntry(productName, crd0Name, crd1Name, crd2Name, crd3Name); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccScreeningTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccScreeningTagDataEntry ReadScreeningTagDataEntry() |
||||
|
{ |
||||
|
var flags = (IccScreeningFlag)this.ReadInt32(); |
||||
|
uint channelCount = this.ReadUInt32(); |
||||
|
IccScreeningChannel[] channels = new IccScreeningChannel[channelCount]; |
||||
|
for (int i = 0; i < channels.Length; i++) |
||||
|
{ |
||||
|
channels[i] = this.ReadScreeningChannel(); |
||||
|
} |
||||
|
|
||||
|
return new IccScreeningTagDataEntry(flags, channels); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads a <see cref="IccUcrBgTagDataEntry"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="size">The size of the entry in bytes</param>
|
||||
|
/// <returns>The read entry</returns>
|
||||
|
public IccUcrBgTagDataEntry ReadUcrBgTagDataEntry(uint size) |
||||
|
{ |
||||
|
uint ucrCount = this.ReadUInt32(); |
||||
|
ushort[] ucrCurve = new ushort[ucrCount]; |
||||
|
for (int i = 0; i < ucrCurve.Length; i++) |
||||
|
{ |
||||
|
ucrCurve[i] = this.ReadUInt16(); |
||||
|
} |
||||
|
|
||||
|
uint bgCount = this.ReadUInt32(); |
||||
|
ushort[] bgCurve = new ushort[bgCount]; |
||||
|
for (int i = 0; i < bgCurve.Length; i++) |
||||
|
{ |
||||
|
bgCurve[i] = this.ReadUInt16(); |
||||
|
} |
||||
|
|
||||
|
// ((ucr length + bg length) * UInt16 size) + (ucrCount + bgCount)
|
||||
|
uint dataSize = ((ucrCount + bgCount) * 2) + 8; |
||||
|
int descriptionLength = (int)(size - 8 - dataSize); // 8 is the tag header size
|
||||
|
string description = this.ReadAsciiString(descriptionLength); |
||||
|
|
||||
|
return new IccUcrBgTagDataEntry(ucrCurve, bgCurve, description); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,106 @@ |
|||||
|
// <copyright file="IccDataReader.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Text; |
||||
|
using ImageSharp.IO; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods to read ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataReader |
||||
|
{ |
||||
|
private static readonly bool IsLittleEndian = BitConverter.IsLittleEndian; |
||||
|
private static readonly Encoding AsciiEncoding = Encoding.GetEncoding("ASCII"); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The data that is read
|
||||
|
/// </summary>
|
||||
|
private readonly byte[] data; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The bit converter
|
||||
|
/// </summary>
|
||||
|
private readonly EndianBitConverter converter = new BigEndianBitConverter(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The current reading position
|
||||
|
/// </summary>
|
||||
|
private int currentIndex; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="IccDataReader"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="data">The data to read</param>
|
||||
|
public IccDataReader(byte[] data) |
||||
|
{ |
||||
|
Guard.NotNull(data, nameof(data)); |
||||
|
this.data = data; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the reading position to the given value
|
||||
|
/// </summary>
|
||||
|
/// <param name="index">The new index position</param>
|
||||
|
public void SetIndex(int index) |
||||
|
{ |
||||
|
this.currentIndex = index.Clamp(0, this.data.Length); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the current <see cref="currentIndex"/> without increment and adds the given increment
|
||||
|
/// </summary>
|
||||
|
/// <param name="increment">The value to increment <see cref="currentIndex"/></param>
|
||||
|
/// <returns>The current <see cref="currentIndex"/> without the increment</returns>
|
||||
|
private int AddIndex(int increment) |
||||
|
{ |
||||
|
int tmp = this.currentIndex; |
||||
|
this.currentIndex += increment; |
||||
|
return tmp; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the 4 byte padding and adds it to the <see cref="currentIndex"/> variable
|
||||
|
/// </summary>
|
||||
|
private void AddPadding() |
||||
|
{ |
||||
|
this.currentIndex += this.CalcPadding(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the 4 byte padding
|
||||
|
/// </summary>
|
||||
|
/// <returns>the number of bytes to pad</returns>
|
||||
|
private int CalcPadding() |
||||
|
{ |
||||
|
int p = 4 - (this.currentIndex % 4); |
||||
|
return p >= 4 ? 0 : p; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the bit value at a specified position
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value from where the bit will be extracted</param>
|
||||
|
/// <param name="position">Position of the bit. Zero based index from left to right.</param>
|
||||
|
/// <returns>The bit value at specified position</returns>
|
||||
|
private bool GetBit(byte value, int position) |
||||
|
{ |
||||
|
return ((value >> (7 - position)) & 1) == 1; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the bit value at a specified position
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value from where the bit will be extracted</param>
|
||||
|
/// <param name="position">Position of the bit. Zero based index from left to right.</param>
|
||||
|
/// <returns>The bit value at specified position</returns>
|
||||
|
private bool GetBit(ushort value, int position) |
||||
|
{ |
||||
|
return ((value >> (15 - position)) & 1) == 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,179 @@ |
|||||
|
// <copyright file="IccDataWriter.Curves.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Provides methods to write ICC data types
|
||||
|
/// </content>
|
||||
|
internal sealed partial class IccDataWriter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Writes a <see cref="IccOneDimensionalCurve"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The curve to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteOneDimensionalCurve(IccOneDimensionalCurve value) |
||||
|
{ |
||||
|
int count = this.WriteUInt16((ushort)value.Segments.Length); |
||||
|
count += this.WriteEmpty(2); |
||||
|
|
||||
|
foreach (float point in value.BreakPoints) |
||||
|
{ |
||||
|
count += this.WriteSingle(point); |
||||
|
} |
||||
|
|
||||
|
foreach (IccCurveSegment segment in value.Segments) |
||||
|
{ |
||||
|
count += this.WriteCurveSegment(segment); |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a <see cref="IccResponseCurve"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The curve to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteResponseCurve(IccResponseCurve value) |
||||
|
{ |
||||
|
int count = this.WriteUInt32((uint)value.CurveType); |
||||
|
int channels = value.XyzValues.Length; |
||||
|
|
||||
|
foreach (IccResponseNumber[] responseArray in value.ResponseArrays) |
||||
|
{ |
||||
|
count += this.WriteUInt32((uint)responseArray.Length); |
||||
|
} |
||||
|
|
||||
|
foreach (Vector3 xyz in value.XyzValues) |
||||
|
{ |
||||
|
count += this.WriteXyzNumber(xyz); |
||||
|
} |
||||
|
|
||||
|
foreach (IccResponseNumber[] responseArray in value.ResponseArrays) |
||||
|
{ |
||||
|
foreach (IccResponseNumber response in responseArray) |
||||
|
{ |
||||
|
count += this.WriteResponseNumber(response); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a <see cref="IccParametricCurve"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The curve to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteParametricCurve(IccParametricCurve value) |
||||
|
{ |
||||
|
ushort typeValue = (ushort)value.Type; |
||||
|
int count = this.WriteUInt16(typeValue); |
||||
|
count += this.WriteEmpty(2); |
||||
|
|
||||
|
if (typeValue <= 4) |
||||
|
{ |
||||
|
count += this.WriteFix16(value.G); |
||||
|
} |
||||
|
|
||||
|
if (typeValue > 0 && typeValue <= 4) |
||||
|
{ |
||||
|
count += this.WriteFix16(value.A); |
||||
|
count += this.WriteFix16(value.B); |
||||
|
} |
||||
|
|
||||
|
if (typeValue > 1 && typeValue <= 4) |
||||
|
{ |
||||
|
count += this.WriteFix16(value.C); |
||||
|
} |
||||
|
|
||||
|
if (typeValue > 2 && typeValue <= 4) |
||||
|
{ |
||||
|
count += this.WriteFix16(value.D); |
||||
|
} |
||||
|
|
||||
|
if (typeValue == 4) |
||||
|
{ |
||||
|
count += this.WriteFix16(value.E); |
||||
|
count += this.WriteFix16(value.F); |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a <see cref="IccCurveSegment"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The curve to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteCurveSegment(IccCurveSegment value) |
||||
|
{ |
||||
|
int count = this.WriteUInt32((uint)value.Signature); |
||||
|
count += this.WriteEmpty(4); |
||||
|
|
||||
|
switch (value.Signature) |
||||
|
{ |
||||
|
case IccCurveSegmentSignature.FormulaCurve: |
||||
|
return count + this.WriteFormulaCurveElement(value as IccFormulaCurveElement); |
||||
|
case IccCurveSegmentSignature.SampledCurve: |
||||
|
return count + this.WriteSampledCurveElement(value as IccSampledCurveElement); |
||||
|
default: |
||||
|
throw new InvalidIccProfileException($"Invalid CurveSegment type of {value.Signature}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a <see cref="IccFormulaCurveElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The curve to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteFormulaCurveElement(IccFormulaCurveElement value) |
||||
|
{ |
||||
|
int count = this.WriteUInt16((ushort)value.Type); |
||||
|
count += this.WriteEmpty(2); |
||||
|
|
||||
|
if (value.Type == IccFormulaCurveType.Type1 || value.Type == IccFormulaCurveType.Type2) |
||||
|
{ |
||||
|
count += this.WriteSingle(value.Gamma); |
||||
|
} |
||||
|
|
||||
|
count += this.WriteSingle(value.A); |
||||
|
count += this.WriteSingle(value.B); |
||||
|
count += this.WriteSingle(value.C); |
||||
|
|
||||
|
if (value.Type == IccFormulaCurveType.Type2 || value.Type == IccFormulaCurveType.Type3) |
||||
|
{ |
||||
|
count += this.WriteSingle(value.D); |
||||
|
} |
||||
|
|
||||
|
if (value.Type == IccFormulaCurveType.Type3) |
||||
|
{ |
||||
|
count += this.WriteSingle(value.E); |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a <see cref="IccSampledCurveElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The curve to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteSampledCurveElement(IccSampledCurveElement value) |
||||
|
{ |
||||
|
int count = this.WriteUInt32((uint)value.CurveEntries.Length); |
||||
|
foreach (float entry in value.CurveEntries) |
||||
|
{ |
||||
|
count += this.WriteSingle(entry); |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,128 @@ |
|||||
|
// <copyright file="IccDataWriter.Lut.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
/// <content>
|
||||
|
/// Provides methods to write ICC data types
|
||||
|
/// </content>
|
||||
|
internal sealed partial class IccDataWriter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Writes an 8bit lookup table
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The LUT to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteLut8(IccLut value) |
||||
|
{ |
||||
|
foreach (float item in value.Values) |
||||
|
{ |
||||
|
this.WriteByte((byte)((item * byte.MaxValue) + 0.5f).Clamp(0, byte.MaxValue)); |
||||
|
} |
||||
|
|
||||
|
return value.Values.Length; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an 16bit lookup table
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The LUT to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteLut16(IccLut value) |
||||
|
{ |
||||
|
foreach (float item in value.Values) |
||||
|
{ |
||||
|
this.WriteUInt16((ushort)((item * ushort.MaxValue) + 0.5f).Clamp(0, ushort.MaxValue)); |
||||
|
} |
||||
|
|
||||
|
return value.Values.Length * 2; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an color lookup table
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The CLUT to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteClut(IccClut value) |
||||
|
{ |
||||
|
int count = this.WriteArray(value.GridPointCount); |
||||
|
count += this.WriteEmpty(16 - value.GridPointCount.Length); |
||||
|
|
||||
|
switch (value.DataType) |
||||
|
{ |
||||
|
case IccClutDataType.Float: |
||||
|
return count + this.WriteClutF32(value); |
||||
|
case IccClutDataType.UInt8: |
||||
|
count += this.WriteByte(1); |
||||
|
count += this.WriteEmpty(3); |
||||
|
return count + this.WriteClut8(value); |
||||
|
case IccClutDataType.UInt16: |
||||
|
count += this.WriteByte(2); |
||||
|
count += this.WriteEmpty(3); |
||||
|
return count + this.WriteClut16(value); |
||||
|
|
||||
|
default: |
||||
|
throw new InvalidIccProfileException($"Invalid CLUT data type of {value.DataType}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a 8bit color lookup table
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The CLUT to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteClut8(IccClut value) |
||||
|
{ |
||||
|
int count = 0; |
||||
|
foreach (float[] inArray in value.Values) |
||||
|
{ |
||||
|
foreach (float item in inArray) |
||||
|
{ |
||||
|
count += this.WriteByte((byte)((item * byte.MaxValue) + 0.5f).Clamp(0, byte.MaxValue)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a 16bit color lookup table
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The CLUT to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteClut16(IccClut value) |
||||
|
{ |
||||
|
int count = 0; |
||||
|
foreach (float[] inArray in value.Values) |
||||
|
{ |
||||
|
foreach (float item in inArray) |
||||
|
{ |
||||
|
count += this.WriteUInt16((ushort)((item * ushort.MaxValue) + 0.5f).Clamp(0, ushort.MaxValue)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a 32bit float color lookup table
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The CLUT to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteClutF32(IccClut value) |
||||
|
{ |
||||
|
int count = 0; |
||||
|
foreach (float[] inArray in value.Values) |
||||
|
{ |
||||
|
foreach (float item in inArray) |
||||
|
{ |
||||
|
count += this.WriteSingle(item); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,162 @@ |
|||||
|
// <copyright file="IccDataWriter.Matrix.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using ImageSharp.Memory; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods to write ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataWriter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Writes a two dimensional matrix
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The matrix to write</param>
|
||||
|
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteMatrix(Matrix4x4 value, bool isSingle) |
||||
|
{ |
||||
|
int count = 0; |
||||
|
|
||||
|
if (isSingle) |
||||
|
{ |
||||
|
count += this.WriteSingle(value.M11); |
||||
|
count += this.WriteSingle(value.M21); |
||||
|
count += this.WriteSingle(value.M31); |
||||
|
|
||||
|
count += this.WriteSingle(value.M12); |
||||
|
count += this.WriteSingle(value.M22); |
||||
|
count += this.WriteSingle(value.M32); |
||||
|
|
||||
|
count += this.WriteSingle(value.M13); |
||||
|
count += this.WriteSingle(value.M23); |
||||
|
count += this.WriteSingle(value.M33); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
count += this.WriteFix16(value.M11); |
||||
|
count += this.WriteFix16(value.M21); |
||||
|
count += this.WriteFix16(value.M31); |
||||
|
|
||||
|
count += this.WriteFix16(value.M12); |
||||
|
count += this.WriteFix16(value.M22); |
||||
|
count += this.WriteFix16(value.M32); |
||||
|
|
||||
|
count += this.WriteFix16(value.M13); |
||||
|
count += this.WriteFix16(value.M23); |
||||
|
count += this.WriteFix16(value.M33); |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a two dimensional matrix
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The matrix to write</param>
|
||||
|
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteMatrix(Fast2DArray<float> value, bool isSingle) |
||||
|
{ |
||||
|
int count = 0; |
||||
|
for (int y = 0; y < value.Height; y++) |
||||
|
{ |
||||
|
for (int x = 0; x < value.Width; x++) |
||||
|
{ |
||||
|
if (isSingle) |
||||
|
{ |
||||
|
count += this.WriteSingle(value[x, y]); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
count += this.WriteFix16(value[x, y]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a two dimensional matrix
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The matrix to write</param>
|
||||
|
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteMatrix(float[,] value, bool isSingle) |
||||
|
{ |
||||
|
int count = 0; |
||||
|
for (int y = 0; y < value.GetLength(1); y++) |
||||
|
{ |
||||
|
for (int x = 0; x < value.GetLength(0); x++) |
||||
|
{ |
||||
|
if (isSingle) |
||||
|
{ |
||||
|
count += this.WriteSingle(value[x, y]); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
count += this.WriteFix16(value[x, y]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a one dimensional matrix
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The matrix to write</param>
|
||||
|
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteMatrix(Vector3 value, bool isSingle) |
||||
|
{ |
||||
|
int count = 0; |
||||
|
if (isSingle) |
||||
|
{ |
||||
|
count += this.WriteSingle(value.X); |
||||
|
count += this.WriteSingle(value.Y); |
||||
|
count += this.WriteSingle(value.Z); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
count += this.WriteFix16(value.X); |
||||
|
count += this.WriteFix16(value.Y); |
||||
|
count += this.WriteFix16(value.Z); |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a one dimensional matrix
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The matrix to write</param>
|
||||
|
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteMatrix(float[] value, bool isSingle) |
||||
|
{ |
||||
|
int count = 0; |
||||
|
for (int i = 0; i < value.Length; i++) |
||||
|
{ |
||||
|
if (isSingle) |
||||
|
{ |
||||
|
count += this.WriteSingle(value[i]); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
count += this.WriteFix16(value[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
// <copyright file="IccDataWriter.MultiProcessElement.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides methods to write ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataWriter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Writes a <see cref="IccMultiProcessElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The element to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteMultiProcessElement(IccMultiProcessElement value) |
||||
|
{ |
||||
|
int count = this.WriteUInt32((uint)value.Signature); |
||||
|
count += this.WriteUInt16((ushort)value.InputChannelCount); |
||||
|
count += this.WriteUInt16((ushort)value.OutputChannelCount); |
||||
|
|
||||
|
switch (value.Signature) |
||||
|
{ |
||||
|
case IccMultiProcessElementSignature.CurveSet: |
||||
|
return count + this.WriteCurveSetProcessElement(value as IccCurveSetProcessElement); |
||||
|
case IccMultiProcessElementSignature.Matrix: |
||||
|
return count + this.WriteMatrixProcessElement(value as IccMatrixProcessElement); |
||||
|
case IccMultiProcessElementSignature.Clut: |
||||
|
return count + this.WriteClutProcessElement(value as IccClutProcessElement); |
||||
|
|
||||
|
case IccMultiProcessElementSignature.BAcs: |
||||
|
case IccMultiProcessElementSignature.EAcs: |
||||
|
return count + this.WriteEmpty(8); |
||||
|
|
||||
|
default: |
||||
|
throw new InvalidIccProfileException($"Invalid MultiProcessElement type of {value.Signature}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a CurveSet <see cref="IccMultiProcessElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The element to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteCurveSetProcessElement(IccCurveSetProcessElement value) |
||||
|
{ |
||||
|
int count = 0; |
||||
|
foreach (IccOneDimensionalCurve curve in value.Curves) |
||||
|
{ |
||||
|
count += this.WriteOneDimensionalCurve(curve); |
||||
|
count += this.WritePadding(); |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a Matrix <see cref="IccMultiProcessElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The element to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteMatrixProcessElement(IccMatrixProcessElement value) |
||||
|
{ |
||||
|
return this.WriteMatrix(value.MatrixIxO, true) |
||||
|
+ this.WriteMatrix(value.MatrixOx1, true); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a CLUT <see cref="IccMultiProcessElement"/>
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The element to write</param>
|
||||
|
/// <returns>The number of bytes written</returns>
|
||||
|
public int WriteClutProcessElement(IccClutProcessElement value) |
||||
|
{ |
||||
|
return this.WriteClut(value.ClutValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,137 @@ |
|||||
|
// <copyright file="IccDataWriter.NonPrimitives.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods to write ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataWriter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Writes a DateTime
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteDateTime(DateTime value) |
||||
|
{ |
||||
|
return this.WriteUInt16((ushort)value.Year) |
||||
|
+ this.WriteUInt16((ushort)value.Month) |
||||
|
+ this.WriteUInt16((ushort)value.Day) |
||||
|
+ this.WriteUInt16((ushort)value.Hour) |
||||
|
+ this.WriteUInt16((ushort)value.Minute) |
||||
|
+ this.WriteUInt16((ushort)value.Second); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an ICC profile version number
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteVersionNumber(Version value) |
||||
|
{ |
||||
|
int major = value.Major.Clamp(0, byte.MaxValue); |
||||
|
int minor = value.Minor.Clamp(0, 15); |
||||
|
int bugfix = value.Build.Clamp(0, 15); |
||||
|
|
||||
|
// TODO: This is not used?
|
||||
|
byte mb = (byte)((minor << 4) | bugfix); |
||||
|
|
||||
|
int version = (major << 24) | (minor << 20) | (bugfix << 16); |
||||
|
return this.WriteInt32(version); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an XYZ number
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteXyzNumber(Vector3 value) |
||||
|
{ |
||||
|
return this.WriteFix16(value.X) |
||||
|
+ this.WriteFix16(value.Y) |
||||
|
+ this.WriteFix16(value.Z); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a profile ID
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteProfileId(IccProfileId value) |
||||
|
{ |
||||
|
return this.WriteUInt32(value.Part1) |
||||
|
+ this.WriteUInt32(value.Part2) |
||||
|
+ this.WriteUInt32(value.Part3) |
||||
|
+ this.WriteUInt32(value.Part4); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a position number
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WritePositionNumber(IccPositionNumber value) |
||||
|
{ |
||||
|
return this.WriteUInt32(value.Offset) |
||||
|
+ this.WriteUInt32(value.Size); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a response number
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteResponseNumber(IccResponseNumber value) |
||||
|
{ |
||||
|
return this.WriteUInt16(value.DeviceCode) |
||||
|
+ this.WriteFix16(value.MeasurementValue); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a named color
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteNamedColor(IccNamedColor value) |
||||
|
{ |
||||
|
return this.WriteAsciiString(value.Name, 32, true) |
||||
|
+ this.WriteArray(value.PcsCoordinates) |
||||
|
+ this.WriteArray(value.DeviceCoordinates); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a profile description
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteProfileDescription(IccProfileDescription value) |
||||
|
{ |
||||
|
return this.WriteUInt32(value.DeviceManufacturer) |
||||
|
+ this.WriteUInt32(value.DeviceModel) |
||||
|
+ this.WriteInt64((long)value.DeviceAttributes) |
||||
|
+ this.WriteUInt32((uint)value.TechnologyInformation) |
||||
|
+ this.WriteTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode) |
||||
|
+ this.WriteMultiLocalizedUnicodeTagDataEntry(new IccMultiLocalizedUnicodeTagDataEntry(value.DeviceManufacturerInfo)) |
||||
|
+ this.WriteTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode) |
||||
|
+ this.WriteMultiLocalizedUnicodeTagDataEntry(new IccMultiLocalizedUnicodeTagDataEntry(value.DeviceModelInfo)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a screening channel
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteScreeningChannel(IccScreeningChannel value) |
||||
|
{ |
||||
|
return this.WriteFix16(value.Frequency) |
||||
|
+ this.WriteFix16(value.Angle) |
||||
|
+ this.WriteInt32((int)value.SpotShape); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,248 @@ |
|||||
|
// <copyright file="IccDataWriter.Primitives.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Text; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods to write ICC data types
|
||||
|
/// </summary>
|
||||
|
internal sealed partial class IccDataWriter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Writes a byte
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteByte(byte value) |
||||
|
{ |
||||
|
this.dataStream.WriteByte(value); |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an ushort
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public unsafe int WriteUInt16(ushort value) |
||||
|
{ |
||||
|
return this.WriteBytes((byte*)&value, 2); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a short
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public unsafe int WriteInt16(short value) |
||||
|
{ |
||||
|
return this.WriteBytes((byte*)&value, 2); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an uint
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public unsafe int WriteUInt32(uint value) |
||||
|
{ |
||||
|
return this.WriteBytes((byte*)&value, 4); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an int
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public unsafe int WriteInt32(int value) |
||||
|
{ |
||||
|
return this.WriteBytes((byte*)&value, 4); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an ulong
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public unsafe int WriteUInt64(ulong value) |
||||
|
{ |
||||
|
return this.WriteBytes((byte*)&value, 8); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a long
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public unsafe int WriteInt64(long value) |
||||
|
{ |
||||
|
return this.WriteBytes((byte*)&value, 8); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a float
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public unsafe int WriteSingle(float value) |
||||
|
{ |
||||
|
return this.WriteBytes((byte*)&value, 4); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a double
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public unsafe int WriteDouble(double value) |
||||
|
{ |
||||
|
return this.WriteBytes((byte*)&value, 8); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes a signed 32bit number with 1 sign bit, 15 value bits and 16 fractional bits
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteFix16(double value) |
||||
|
{ |
||||
|
const double Max = short.MaxValue + (65535d / 65536d); |
||||
|
const double Min = short.MinValue; |
||||
|
|
||||
|
value = value.Clamp(Min, Max); |
||||
|
value *= 65536d; |
||||
|
|
||||
|
return this.WriteInt32((int)Math.Round(value, MidpointRounding.AwayFromZero)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an unsigned 32bit number with 16 value bits and 16 fractional bits
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteUFix16(double value) |
||||
|
{ |
||||
|
const double Max = ushort.MaxValue + (65535d / 65536d); |
||||
|
const double Min = ushort.MinValue; |
||||
|
|
||||
|
value = value.Clamp(Min, Max); |
||||
|
value *= 65536d; |
||||
|
|
||||
|
return this.WriteUInt32((uint)Math.Round(value, MidpointRounding.AwayFromZero)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an unsigned 16bit number with 1 value bit and 15 fractional bits
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteU1Fix15(double value) |
||||
|
{ |
||||
|
const double Max = 1 + (32767d / 32768d); |
||||
|
const double Min = 0; |
||||
|
|
||||
|
value = value.Clamp(Min, Max); |
||||
|
value *= 32768d; |
||||
|
|
||||
|
return this.WriteUInt16((ushort)Math.Round(value, MidpointRounding.AwayFromZero)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an unsigned 16bit number with 8 value bits and 8 fractional bits
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The value to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteUFix8(double value) |
||||
|
{ |
||||
|
const double Max = byte.MaxValue + (255d / 256d); |
||||
|
const double Min = byte.MinValue; |
||||
|
|
||||
|
value = value.Clamp(Min, Max); |
||||
|
value *= 256d; |
||||
|
|
||||
|
return this.WriteUInt16((ushort)Math.Round(value, MidpointRounding.AwayFromZero)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an ASCII encoded string
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">the string to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteAsciiString(string value) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(value)) |
||||
|
{ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
byte[] data = AsciiEncoding.GetBytes(value); |
||||
|
this.dataStream.Write(data, 0, data.Length); |
||||
|
return data.Length; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an ASCII encoded string resizes it to the given length
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The string to write</param>
|
||||
|
/// <param name="length">The desired length of the string (including potential null terminator)</param>
|
||||
|
/// <param name="ensureNullTerminator">If True, there will be a \0 added at the end</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteAsciiString(string value, int length, bool ensureNullTerminator) |
||||
|
{ |
||||
|
if (length == 0) |
||||
|
{ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
Guard.MustBeGreaterThan(length, 0, nameof(length)); |
||||
|
|
||||
|
if (value == null) |
||||
|
{ |
||||
|
value = string.Empty; |
||||
|
} |
||||
|
|
||||
|
byte paddingChar = (byte)' '; |
||||
|
int lengthAdjust = 0; |
||||
|
|
||||
|
if (ensureNullTerminator) |
||||
|
{ |
||||
|
paddingChar = 0; |
||||
|
lengthAdjust = 1; |
||||
|
} |
||||
|
|
||||
|
value = value.Substring(0, Math.Min(length - lengthAdjust, value.Length)); |
||||
|
|
||||
|
byte[] textData = AsciiEncoding.GetBytes(value); |
||||
|
int actualLength = Math.Min(length - lengthAdjust, textData.Length); |
||||
|
this.dataStream.Write(textData, 0, actualLength); |
||||
|
for (int i = 0; i < length - actualLength; i++) |
||||
|
{ |
||||
|
this.dataStream.WriteByte(paddingChar); |
||||
|
} |
||||
|
|
||||
|
return length; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes an UTF-16 big-endian encoded string
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">the string to write</param>
|
||||
|
/// <returns>the number of bytes written</returns>
|
||||
|
public int WriteUnicodeString(string value) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(value)) |
||||
|
{ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
byte[] data = Encoding.BigEndianUnicode.GetBytes(value); |
||||
|
this.dataStream.Write(data, 0, data.Length); |
||||
|
return data.Length; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue