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 (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using Drawing.Processors; |
|||
using ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Extension methods for the <see cref="Image{TPixel}"/> type.
|
|||
/// </summary>
|
|||
public static partial class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <param name="options">The options.</param>
|
|||
/// <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) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
if (size == default(Size)) |
|||
{ |
|||
size = new Size(image.Width, image.Height); |
|||
} |
|||
|
|||
if (location == default(Point)) |
|||
{ |
|||
location = Point.Empty; |
|||
} |
|||
|
|||
source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, size, location, options), source.Bounds); |
|||
return source; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image this method extends.</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>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, float percent) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
GraphicsOptions options = GraphicsOptions.Default; |
|||
options.BlendPercentage = percent; |
|||
return DrawImage(source, image, default(Size), default(Point), options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</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>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float percent) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
GraphicsOptions options = GraphicsOptions.Default; |
|||
options.BlendPercentage = percent; |
|||
options.BlenderMode = blender; |
|||
return DrawImage(source, image, default(Size), default(Point), options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image this method extends.</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>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, GraphicsOptions options) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
return DrawImage(source, image, default(Size), default(Point), options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <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="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <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) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
GraphicsOptions options = GraphicsOptions.Default; |
|||
options.BlendPercentage = percent; |
|||
return source.DrawImage(image, size, location, options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <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="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <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) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
GraphicsOptions options = GraphicsOptions.Default; |
|||
options.BlenderMode = blender; |
|||
options.BlendPercentage = percent; |
|||
return source.DrawImage(image, size, location, options); |
|||
} |
|||
} |
|||
// <copyright file="DrawImage.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using Drawing.Processors; |
|||
using ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Extension methods for the <see cref="Image{TPixel}"/> type.
|
|||
/// </summary>
|
|||
public static partial class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <param name="options">The options.</param>
|
|||
/// <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) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
if (size == default(Size)) |
|||
{ |
|||
size = new Size(image.Width, image.Height); |
|||
} |
|||
|
|||
if (location == default(Point)) |
|||
{ |
|||
location = Point.Empty; |
|||
} |
|||
|
|||
source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, size, location, options), source.Bounds); |
|||
return source; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image this method extends.</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>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, float percent) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
GraphicsOptions options = GraphicsOptions.Default; |
|||
options.BlendPercentage = percent; |
|||
return DrawImage(source, image, default(Size), default(Point), options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</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>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float percent) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
GraphicsOptions options = GraphicsOptions.Default; |
|||
options.BlendPercentage = percent; |
|||
options.BlenderMode = blender; |
|||
return DrawImage(source, image, default(Size), default(Point), options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image this method extends.</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>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, GraphicsOptions options) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
return DrawImage(source, image, default(Size), default(Point), options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <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="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <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) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
GraphicsOptions options = GraphicsOptions.Default; |
|||
options.BlendPercentage = percent; |
|||
return source.DrawImage(image, size, location, options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <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="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <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) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
GraphicsOptions options = GraphicsOptions.Default; |
|||
options.BlenderMode = blender; |
|||
options.BlendPercentage = percent; |
|||
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