168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy/*
268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * Copyright (C) 2016 The Android Open Source Project
368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * Licensed under the Apache License, Version 2.0 (the "License");
568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * you may not use this file except in compliance with the License.
668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * You may obtain a copy of the License at
768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *      http://www.apache.org/licenses/LICENSE-2.0
968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
1068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * Unless required by applicable law or agreed to in writing, software
1168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * distributed under the License is distributed on an "AS IS" BASIS,
1268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * See the License for the specific language governing permissions and
1468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * limitations under the License.
1568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy */
1668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
1768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guypackage android.graphics;
1868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
1977b161e0b14372e3eb124ed19321a9639aeb4271Romain Guyimport android.annotation.AnyThread;
2015296a2d3478f53402e2d98f49724bb791eb339dRomain Guyimport android.annotation.ColorInt;
2168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guyimport android.annotation.IntRange;
2268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guyimport android.annotation.NonNull;
23199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guyimport android.annotation.Nullable;
24efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guyimport android.annotation.Size;
25910e081216ac530432ac9d0aab10d5e5e4c73ab8Jeff Sharkeyimport android.annotation.SuppressAutoDoc;
2615296a2d3478f53402e2d98f49724bb791eb339dRomain Guyimport android.util.Pair;
2768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
2815296a2d3478f53402e2d98f49724bb791eb339dRomain Guyimport java.util.ArrayList;
2968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guyimport java.util.Arrays;
3015296a2d3478f53402e2d98f49724bb791eb339dRomain Guyimport java.util.List;
3168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guyimport java.util.function.DoubleUnaryOperator;
3268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
3368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy/**
3468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * {@usesMathJax}
3568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
3668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>A {@link ColorSpace} is used to identify a specific organization of colors.
3768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * Each color space is characterized by a {@link Model color model} that defines
3868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * how a color value is represented (for instance the {@link Model#RGB RGB} color
3968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * model defines a color value as a triplet of numbers).</p>
4068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
4168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>Each component of a color must fall within a valid range, specific to each
4268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * color space, defined by {@link #getMinValue(int)} and {@link #getMaxValue(int)}
4368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * This range is commonly \([0..1]\). While it is recommended to use values in the
4468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * valid range, a color space always clamps input and output values when performing
4568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * operations such as converting to a different color space.</p>
4668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
4768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <h3>Using color spaces</h3>
4868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
4968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>This implementation provides a pre-defined set of common color spaces
5068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * described in the {@link Named} enum. To obtain an instance of one of the
5168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * pre-defined color spaces, simply invoke {@link #get(Named)}:</p>
5268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
5368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <pre class="prettyprint">
5468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * ColorSpace sRgb = ColorSpace.get(ColorSpace.Named.SRGB);
5568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * </pre>
5668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
5768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>The {@link #get(Named)} method always returns the same instance for a given
5868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * name. Color spaces with an {@link Model#RGB RGB} color model can be safely
5968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * cast to {@link Rgb}. Doing so gives you access to more APIs to query various
6068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * properties of RGB color models: color gamut primaries, transfer functions,
6168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * conversions to and from linear space, etc. Please refer to {@link Rgb} for
6268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * more information.</p>
6368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
6468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>The documentation of {@link Named} provides a detailed description of the
6568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * various characteristics of each available color space.</p>
6668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
6768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <h3>Color space conversions</h3>
6868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
6968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>To allow conversion between color spaces, this implementation uses the CIE
7068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * XYZ profile connection space (PCS). Color values can be converted to and from
7168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * this PCS using {@link #toXyz(float[])} and {@link #fromXyz(float[])}.</p>
7268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
7368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>For color space with a non-RGB color model, the white point of the PCS
7468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <em>must be</em> the CIE standard illuminant D50. RGB color spaces use their
7568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * native white point (D65 for {@link Named#SRGB sRGB} for instance and must
7668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * undergo {@link Adaptation chromatic adaptation} as necessary.</p>
7768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
7868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>Since the white point of the PCS is not defined for RGB color space, it is
7968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * highly recommended to use the variants of the {@link #connect(ColorSpace, ColorSpace)}
8068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * method to perform conversions between color spaces. A color space can be
8168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * manually adapted to a specific white point using {@link #adapt(ColorSpace, float[])}.
8268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * Please refer to the documentation of {@link Rgb RGB color spaces} for more
8368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * information. Several common CIE standard illuminants are provided in this
8468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * class as reference (see {@link #ILLUMINANT_D65} or {@link #ILLUMINANT_D50}
8568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * for instance).</p>
8668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
8768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>Here is an example of how to convert from a color space to another:</p>
8868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
8968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <pre class="prettyprint">
9068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * // Convert from DCI-P3 to Rec.2020
9168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * ColorSpace.Connector connector = ColorSpace.connect(
9268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *         ColorSpace.get(ColorSpace.Named.DCI_P3),
9368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *         ColorSpace.get(ColorSpace.Named.BT2020));
9468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
9568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * float[] bt2020 = connector.transform(p3r, p3g, p3b);
9668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * </pre>
9768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
9868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>You can easily convert to {@link Named#SRGB sRGB} by omitting the second
9968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * parameter:</p>
10068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
10168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <pre class="prettyprint">
10268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * // Convert from DCI-P3 to sRGB
10368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * ColorSpace.Connector connector = ColorSpace.connect(ColorSpace.get(ColorSpace.Named.DCI_P3));
10468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
10568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * float[] sRGB = connector.transform(p3r, p3g, p3b);
10668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * </pre>
10768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
10868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>Conversions also work between color spaces with different color models:</p>
10968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
11068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <pre class="prettyprint">
11168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * // Convert from CIE L*a*b* (color model Lab) to Rec.709 (color model RGB)
11268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * ColorSpace.Connector connector = ColorSpace.connect(
11368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *         ColorSpace.get(ColorSpace.Named.CIE_LAB),
11468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *         ColorSpace.get(ColorSpace.Named.BT709));
11568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * </pre>
11668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
11768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <h3>Color spaces and multi-threading</h3>
11868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
11968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>Color spaces and other related classes ({@link Connector} for instance)
12068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * are immutable and stateless. They can be safely used from multiple concurrent
12168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * threads.</p>
12268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
12368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * <p>Public static methods provided by this class, such as {@link #get(Named)}
12468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * and {@link #connect(ColorSpace, ColorSpace)}, are also guaranteed to be
12568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * thread-safe.</p>
12668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy *
12768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * @see #get(Named)
12868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * @see Named
12968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * @see Model
13068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * @see Connector
13168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy * @see Adaptation
13268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy */
13377b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy@AnyThread
13468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy@SuppressWarnings("StaticInitializerReferencesSubClass")
135910e081216ac530432ac9d0aab10d5e5e4c73ab8Jeff Sharkey@SuppressAutoDoc
13668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guypublic abstract class ColorSpace {
13768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
13868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Standard CIE 1931 2° illuminant A, encoded in xyY.
13968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * This illuminant has a color temperature of 2856K.
14068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
14168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static final float[] ILLUMINANT_A   = { 0.44757f, 0.40745f };
14268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
14368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Standard CIE 1931 2° illuminant B, encoded in xyY.
14468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * This illuminant has a color temperature of 4874K.
14568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
14668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static final float[] ILLUMINANT_B   = { 0.34842f, 0.35161f };
14768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
14868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Standard CIE 1931 2° illuminant C, encoded in xyY.
14968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * This illuminant has a color temperature of 6774K.
15068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
15168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static final float[] ILLUMINANT_C   = { 0.31006f, 0.31616f };
15268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
15368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Standard CIE 1931 2° illuminant D50, encoded in xyY.
15468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * This illuminant has a color temperature of 5003K. This illuminant
15568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * is used by the profile connection space in ICC profiles.
15668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
15768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static final float[] ILLUMINANT_D50 = { 0.34567f, 0.35850f };
15868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
15968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Standard CIE 1931 2° illuminant D55, encoded in xyY.
16068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * This illuminant has a color temperature of 5503K.
16168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
16268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static final float[] ILLUMINANT_D55 = { 0.33242f, 0.34743f };
16368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
16468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Standard CIE 1931 2° illuminant D60, encoded in xyY.
16568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * This illuminant has a color temperature of 6004K.
16668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
16768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static final float[] ILLUMINANT_D60 = { 0.32168f, 0.33767f };
16868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
16968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Standard CIE 1931 2° illuminant D65, encoded in xyY.
17068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * This illuminant has a color temperature of 6504K. This illuminant
17168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * is commonly used in RGB color spaces such as sRGB, BT.209, etc.
17268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
17368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static final float[] ILLUMINANT_D65 = { 0.31271f, 0.32902f };
17468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
17568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Standard CIE 1931 2° illuminant D75, encoded in xyY.
17668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * This illuminant has a color temperature of 7504K.
17768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
17868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static final float[] ILLUMINANT_D75 = { 0.29902f, 0.31485f };
17968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
18068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Standard CIE 1931 2° illuminant E, encoded in xyY.
18168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * This illuminant has a color temperature of 5454K.
18268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
18368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static final float[] ILLUMINANT_E   = { 0.33333f, 0.33333f };
18468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
18568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
18668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * The minimum ID value a color space can have.
18768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
18868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #getId()
18968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
19068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static final int MIN_ID = -1; // Do not change
19168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
19268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * The maximum ID value a color space can have.
19368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
19468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #getId()
19568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
19677b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy    public static final int MAX_ID = 63; // Do not change, used to encode in longs
19768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
19868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static final float[] SRGB_PRIMARIES = { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f };
19968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static final float[] NTSC_1953_PRIMARIES = { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f };
20068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static final float[] ILLUMINANT_D50_XYZ = { 0.964212f, 1.0f, 0.825188f };
20168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
20268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    // See static initialization block next to #get(Named)
20368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length];
20468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
20568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull private final String mName;
20668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull private final Model mModel;
20768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @IntRange(from = MIN_ID, to = MAX_ID) private final int mId;
20868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
20968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
21068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@usesMathJax}
21168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
21268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>List of common, named color spaces. A corresponding instance of
21368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@link ColorSpace} can be obtained by calling {@link ColorSpace#get(Named)}:</p>
21468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
21568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <pre class="prettyprint">
21668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * ColorSpace cs = ColorSpace.get(ColorSpace.Named.DCI_P3);
21768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * </pre>
21868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
219199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     * <p>The properties of each color space are described below (see {@link #SRGB sRGB}
220199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     * for instance). When applicable, the color gamut of each color space is compared
221199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     * to the color gamut of sRGB using a CIE 1931 xy chromaticity diagram. This diagram
222199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     * shows the location of the color space's primaries and white point.</p>
223199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     *
22468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see ColorSpace#get(Named)
22568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
22668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public enum Named {
22768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        // NOTE: Do NOT change the order of the enum
22868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
22968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space sRGB standardized as IEC 61966-2.1:1999.</p>
23068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
23168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
23268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
23368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
23468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.640</td><td>0.300</td><td>0.150</td><td>0.3127</td></tr>
23568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.330</td><td>0.600</td><td>0.060</td><td>0.3290</td></tr>
23668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
23768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">sRGB IEC61966-2.1</td></tr>
23868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
23968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
240efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
24168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
242efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *             C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0031308 \\
243efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *             1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0031308 \end{cases}
24468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
24568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
24668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
24768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
248efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
24968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
250efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *             C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \lt 0.04045 \\
251efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *             \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.04045 \end{cases}
25268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
25368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
25468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
25568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
25668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
257199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
258c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_srgb.png" />
259199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">sRGB</figcaption>
260199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
26168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
26268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        SRGB,
26368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
26468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space sRGB standardized as IEC 61966-2.1:1999.</p>
26568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
26668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
26768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
26868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
26968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.640</td><td>0.300</td><td>0.150</td><td>0.3127</td></tr>
27068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.330</td><td>0.600</td><td>0.060</td><td>0.3290</td></tr>
27168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
27268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">sRGB IEC61966-2.1 (Linear)</td></tr>
27368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
27468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
275efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
27668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{sRGB} = C_{linear}\)</td>
27768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
27868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
279efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
28068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{linear} = C_{sRGB}\)</td>
28168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
28268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
28368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
284199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
285c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_srgb.png" />
286199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">sRGB</figcaption>
287199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
28868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
28968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        LINEAR_SRGB,
29068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
29168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space scRGB-nl standardized as IEC 61966-2-2:2003.</p>
29268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
29368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
29468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
29568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
29668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.640</td><td>0.300</td><td>0.150</td><td>0.3127</td></tr>
29768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.330</td><td>0.600</td><td>0.060</td><td>0.3290</td></tr>
29868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
29968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">scRGB-nl IEC 61966-2-2:2003</td></tr>
30068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
30168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
302efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
30368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
30468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{scRGB} = \begin{cases} sign(C_{linear}) 12.92 \times \left| C_{linear} \right| &
305efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *                      \left| C_{linear} \right| \lt 0.0031308 \\
30668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             sign(C_{linear}) 1.055 \times \left| C_{linear} \right| ^{\frac{1}{2.4}} - 0.055 &
307efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *                      \left| C_{linear} \right| \ge 0.0031308 \end{cases}
30868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
30968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
31068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
31168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
312efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
31368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
31468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{linear} = \begin{cases}sign(C_{scRGB}) \frac{\left| C_{scRGB} \right|}{12.92} &
315efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *                  \left| C_{scRGB} \right| \lt 0.04045 \\
31668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             sign(C_{scRGB}) \left( \frac{\left| C_{scRGB} \right| + 0.055}{1.055} \right) ^{2.4} &
317efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *                  \left| C_{scRGB} \right| \ge 0.04045 \end{cases}
31868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
31968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
32068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
32177b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy         *     <tr><td>Range</td><td colspan="4">\([-0.799..2.399[\)</td></tr>
32268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
323199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
324c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_scrgb.png" />
32577b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy         *     <figcaption style="text-align: center;">Extended sRGB (orange) vs sRGB (white)</figcaption>
326199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
32768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
32868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        EXTENDED_SRGB,
32968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
33068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space scRGB standardized as IEC 61966-2-2:2003.</p>
33168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
33268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
33368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
33468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
33568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.640</td><td>0.300</td><td>0.150</td><td>0.3127</td></tr>
33668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.330</td><td>0.600</td><td>0.060</td><td>0.3290</td></tr>
33768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
33868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">scRGB IEC 61966-2-2:2003</td></tr>
33968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
34068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
341efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
34268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{scRGB} = C_{linear}\)</td>
34368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
34468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
345efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
34668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{linear} = C_{scRGB}\)</td>
34768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
34877b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy         *     <tr><td>Range</td><td colspan="4">\([-0.5..7.499[\)</td></tr>
34968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
350199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
351c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_scrgb.png" />
35277b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy         *     <figcaption style="text-align: center;">Extended sRGB (orange) vs sRGB (white)</figcaption>
353199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
35468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
35568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        LINEAR_EXTENDED_SRGB,
35668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
35768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space BT.709 standardized as Rec. ITU-R BT.709-5.</p>
35868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
35968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
36068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
36168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
36268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.640</td><td>0.300</td><td>0.150</td><td>0.3127</td></tr>
36368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.330</td><td>0.600</td><td>0.060</td><td>0.3290</td></tr>
36468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
36568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">Rec. ITU-R BT.709-5</td></tr>
36668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
36768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
368efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
36968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
37068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\
37168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases}
37268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
37368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
37468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
37568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
376efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
37768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
37868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\
37968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases}
38068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
38168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
38268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
38368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
38468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
385199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
386c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_bt709.png" />
387199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">BT.709</figcaption>
388199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
38968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
39068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        BT709,
39168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
39268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space BT.2020 standardized as Rec. ITU-R BT.2020-1.</p>
39368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
39468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
39568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
39668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
39768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.708</td><td>0.170</td><td>0.131</td><td>0.3127</td></tr>
39868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.292</td><td>0.797</td><td>0.046</td><td>0.3290</td></tr>
39968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
40068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">Rec. ITU-R BT.2020-1</td></tr>
40168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
40268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
403efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
40468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
40568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{BT2020} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.0181 \\
40668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             1.0993 \times C_{linear}^{\frac{1}{2.2}} - 0.0993 & C_{linear} \ge 0.0181 \end{cases}
40768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
40868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
40968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
41068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
411efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
41268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
41368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{linear} = \begin{cases}\frac{C_{BT2020}}{4.5} & C_{BT2020} \lt 0.08145 \\
41468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \left( \frac{C_{BT2020} + 0.0993}{1.0993} \right) ^{2.2} & C_{BT2020} \ge 0.08145 \end{cases}
41568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
41668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
41768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
41868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
41968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
420199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
421c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_bt2020.png" />
422199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">BT.2020 (orange) vs sRGB (white)</figcaption>
423199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
42468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
42568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        BT2020,
42668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
42768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space DCI-P3 standardized as SMPTE RP 431-2-2007.</p>
42868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
42968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
43068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
43168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
43268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.680</td><td>0.265</td><td>0.150</td><td>0.314</td></tr>
43368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.320</td><td>0.690</td><td>0.060</td><td>0.351</td></tr>
43468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
43568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">SMPTE RP 431-2-2007 DCI (P3)</td></tr>
43668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">N/A</td></tr>
43768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
438efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
43968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{P3} = C_{linear}^{\frac{1}{2.6}}\)</td>
44068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
44168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
442efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
44368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{linear} = C_{P3}^{2.6}\)</td>
44468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
44568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
44668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
447199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
448c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_dci_p3.png" />
449199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">DCI-P3 (orange) vs sRGB (white)</figcaption>
450199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
45168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
45268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        DCI_P3,
45368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
45468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space Display P3 based on SMPTE RP 431-2-2007 and IEC 61966-2.1:1999.</p>
45568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
45668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
45768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
45868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
45968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.680</td><td>0.265</td><td>0.150</td><td>0.3127</td></tr>
46068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.320</td><td>0.690</td><td>0.060</td><td>0.3290</td></tr>
46168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
46268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">Display P3</td></tr>
46368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
46468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
465efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
46668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
467494f784e47e74a65e8bce485065f5b7b077800fbRomain Guy         *             C_{DisplayP3} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0030186 \\
468494f784e47e74a65e8bce485065f5b7b077800fbRomain Guy         *             1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0030186 \end{cases}
46968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
47068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
47168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
47268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
473efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
47468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
475494f784e47e74a65e8bce485065f5b7b077800fbRomain Guy         *             C_{linear} = \begin{cases}\frac{C_{DisplayP3}}{12.92} & C_{sRGB} \lt 0.039 \\
476494f784e47e74a65e8bce485065f5b7b077800fbRomain Guy         *             \left( \frac{C_{DisplayP3} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.039 \end{cases}
47768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
47868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
47968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
48068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
48168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
482199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
483c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_display_p3.png" />
484199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">Display P3 (orange) vs sRGB (white)</figcaption>
485199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
48668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
48768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        DISPLAY_P3,
48868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
48968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space NTSC, 1953 standard.</p>
49068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
49168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
49268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
49368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
49468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.67</td><td>0.21</td><td>0.14</td><td>0.310</td></tr>
49568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.33</td><td>0.71</td><td>0.08</td><td>0.316</td></tr>
49668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
49768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">NTSC (1953)</td></tr>
49868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">C</td></tr>
49968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
500efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
50168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
50268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\
50368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases}
50468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
50568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
50668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
50768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
508efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
50968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
51068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\
51168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases}
51268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
51368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
51468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
51568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
51668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
517199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
518c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_ntsc_1953.png" />
519199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">NTSC 1953 (orange) vs sRGB (white)</figcaption>
520199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
52168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
52268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        NTSC_1953,
52368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
52468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space SMPTE C.</p>
52568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
52668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
52768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
52868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
52968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.630</td><td>0.310</td><td>0.155</td><td>0.3127</td></tr>
53068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.340</td><td>0.595</td><td>0.070</td><td>0.3290</td></tr>
53168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
53268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">SMPTE-C RGB</td></tr>
53368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
53468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
535efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
53668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
53768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\
53868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases}
53968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
54068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
54168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
54268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
543efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
54468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
54568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\
54668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases}
54768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
54868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
54968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
55068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
55168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
552199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
553c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_smpte_c.png" />
554199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">SMPTE-C (orange) vs sRGB (white)</figcaption>
555199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
55668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
55768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        SMPTE_C,
55868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
55968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space Adobe RGB (1998).</p>
56068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
56168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
56268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
56368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
56468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.64</td><td>0.21</td><td>0.15</td><td>0.3127</td></tr>
56568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.33</td><td>0.71</td><td>0.06</td><td>0.3290</td></tr>
56668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
56768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">Adobe RGB (1998)</td></tr>
56868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
56968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
570efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
57168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{RGB} = C_{linear}^{\frac{1}{2.2}}\)</td>
57268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
57368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
574efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
57568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{linear} = C_{RGB}^{2.2}\)</td>
57668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
57768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
57868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
579199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
580c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_adobe_rgb.png" />
581199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">Adobe RGB (orange) vs sRGB (white)</figcaption>
582199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
58368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
58468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        ADOBE_RGB,
58568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
58668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space ProPhoto RGB standardized as ROMM RGB ISO 22028-2:2013.</p>
58768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
58868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
58968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
59068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
59168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.7347</td><td>0.1596</td><td>0.0366</td><td>0.3457</td></tr>
59268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.2653</td><td>0.8404</td><td>0.0001</td><td>0.3585</td></tr>
59368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
59468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">ROMM RGB ISO 22028-2:2013</td></tr>
59568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D50</td></tr>
59668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
597efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
59868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
59968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{ROMM} = \begin{cases} 16 \times C_{linear} & C_{linear} \lt 0.001953 \\
60068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{linear}^{\frac{1}{1.8}} & C_{linear} \ge 0.001953 \end{cases}
60168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
60268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
60368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
60468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
605efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
60668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(\begin{equation}
60768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{linear} = \begin{cases}\frac{C_{ROMM}}{16} & C_{ROMM} \lt 0.031248 \\
60868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             C_{ROMM}^{1.8} & C_{ROMM} \ge 0.031248 \end{cases}
60968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             \end{equation}\)
61068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         </td>
61168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
61268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
61368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
614199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
615c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_pro_photo_rgb.png" />
616199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">ProPhoto RGB (orange) vs sRGB (white)</figcaption>
617199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
61868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
61968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        PRO_PHOTO_RGB,
62068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
62168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space ACES standardized as SMPTE ST 2065-1:2012.</p>
62268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
62368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
62468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
62568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
62668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.73470</td><td>0.00000</td><td>0.00010</td><td>0.32168</td></tr>
62768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.26530</td><td>1.00000</td><td>-0.07700</td><td>0.33767</td></tr>
62868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
62968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">SMPTE ST 2065-1:2012 ACES</td></tr>
63068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D60</td></tr>
63168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
632efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
63368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{ACES} = C_{linear}\)</td>
63468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
63568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
636efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
63768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{linear} = C_{ACES}\)</td>
63868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
63968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([-65504.0, 65504.0]\)</td></tr>
64068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
641199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
642c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_aces.png" />
643199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">ACES (orange) vs sRGB (white)</figcaption>
644199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
64568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
64668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        ACES,
64768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
64868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link ColorSpace.Rgb RGB} color space ACEScg standardized as Academy S-2014-004.</p>
64968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
65068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
65168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
65268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
65368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>x</td><td>0.713</td><td>0.165</td><td>0.128</td><td>0.32168</td></tr>
65468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>y</td><td>0.293</td><td>0.830</td><td>0.044</td><td>0.33767</td></tr>
65568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
65668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">Academy S-2014-004 ACEScg</td></tr>
65768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D60</td></tr>
65868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
659efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Opto-electronic transfer function (OETF)</td>
66068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{ACEScg} = C_{linear}\)</td>
66168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
66268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr>
663efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         <td>Electro-optical transfer function (EOTF)</td>
66468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         <td colspan="4">\(C_{linear} = C_{ACEScg}\)</td>
66568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     </tr>
66668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([-65504.0, 65504.0]\)</td></tr>
66768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
668199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * <p>
669c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_acescg.png" />
670199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         *     <figcaption style="text-align: center;">ACEScg (orange) vs sRGB (white)</figcaption>
671199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * </p>
67268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
67368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        ACESCG,
67468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
67568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link Model#XYZ XYZ} color space CIE XYZ. This color space assumes standard
67668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * illuminant D50 as its white point.</p>
67768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
67868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
67968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">Generic XYZ</td></tr>
68068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D50</td></tr>
68168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\([-2.0, 2.0]\)</td></tr>
68268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
68368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
68468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        CIE_XYZ,
68568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
68668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>{@link Model#LAB Lab} color space CIE L*a*b*. This color space uses CIE XYZ D50
68768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * as a profile conversion space.</p>
68868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Color space definition">
68968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
69068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Name</td><td colspan="4">Generic L*a*b*</td></tr>
69168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>CIE standard illuminant</td><td colspan="4">D50</td></tr>
69268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>Range</td><td colspan="4">\(L: [0.0, 100.0], a: [-128, 128], b: [-128, 128]\)</td></tr>
69368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
69468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
69568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        CIE_LAB
69668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        // Update the initialization block next to #get(Named) when adding new values
69768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
69868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
69968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
70068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>A render intent determines how a {@link ColorSpace.Connector connector}
70168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * maps colors from one color space to another. The choice of mapping is
70268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * important when the source color space has a larger color gamut than the
70368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * destination color space.</p>
70468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
70568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see ColorSpace#connect(ColorSpace, ColorSpace, RenderIntent)
70668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
70768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public enum RenderIntent {
70868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
70968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Compresses the source gamut into the destination gamut.
71068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * This render intent affects all colors, inside and outside
71168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * of destination gamut. The goal of this render intent is
71268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to preserve the visual relationship between colors.</p>
71368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
71468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p class="note">This render intent is currently not
71568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * implemented and behaves like {@link #RELATIVE}.</p>
71668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
71768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        PERCEPTUAL,
71868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
71968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Similar to the {@link #ABSOLUTE} render intent, this render
72068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * intent matches the closest color in the destination gamut
72168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * but makes adjustments for the destination white point.
72268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
72368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        RELATIVE,
72468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
72568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Attempts to maintain the relative saturation of colors
72668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * from the source gamut to the destination gamut, to keep
72768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * highly saturated colors as saturated as possible.</p>
72868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
72968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p class="note">This render intent is currently not
73068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * implemented and behaves like {@link #RELATIVE}.</p>
73168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
73268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        SATURATION,
73368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
73468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Colors that are in the destination gamut are left unchanged.
73568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Colors that fall outside of the destination gamut are mapped
73668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to the closest possible color within the gamut of the destination
73768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * color space (they are clipped).
73868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
73968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        ABSOLUTE
74068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
74168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
74268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
74368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@usesMathJax}
74468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
74568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>List of adaptation matrices that can be used for chromatic adaptation
74668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * using the von Kries transform. These matrices are used to convert values
74768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * in the CIE XYZ space to values in the LMS space (Long Medium Short).</p>
74868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
74968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Given an adaptation matrix \(A\), the conversion from XYZ to
75068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * LMS is straightforward:</p>
75168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
75268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * $$\left[ \begin{array}{c} L\\ M\\ S \end{array} \right] =
75368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * A \left[ \begin{array}{c} X\\ Y\\ Z \end{array} \right]$$
75468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
75568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>The complete von Kries transform \(T\) uses a diagonal matrix
75668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * noted \(D\) to perform the adaptation in LMS space. In addition
75768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * to \(A\) and \(D\), the source white point \(W1\) and the destination
75868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * white point \(W2\) must be specified:</p>
75968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
76068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * $$\begin{align*}
76168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * \left[ \begin{array}{c} L_1\\ M_1\\ S_1 \end{array} \right] &=
76268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *      A \left[ \begin{array}{c} W1_X\\ W1_Y\\ W1_Z \end{array} \right] \\
76368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * \left[ \begin{array}{c} L_2\\ M_2\\ S_2 \end{array} \right] &=
76468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *      A \left[ \begin{array}{c} W2_X\\ W2_Y\\ W2_Z \end{array} \right] \\
76568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * D &= \left[ \begin{matrix} \frac{L_2}{L_1} & 0 & 0 \\
76668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *      0 & \frac{M_2}{M_1} & 0 \\
76768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *      0 & 0 & \frac{S_2}{S_1} \end{matrix} \right] \\
76868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * T &= A^{-1}.D.A
76968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * \end{align*}$$
77068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
77168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>As an example, the resulting matrix \(T\) can then be used to
77268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * perform the chromatic adaptation of sRGB XYZ transform from D65
77368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * to D50:</p>
77468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
77568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * $$sRGB_{D50} = T.sRGB_{D65}$$
77668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
77768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see ColorSpace.Connector
77868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see ColorSpace#connect(ColorSpace, ColorSpace)
77968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
78068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public enum Adaptation {
78168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
7824db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy         * Bradford chromatic adaptation transform, as defined in the
7834db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy         * CIECAM97s color appearance model.
78468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
78568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        BRADFORD(new float[] {
78668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                 0.8951f, -0.7502f,  0.0389f,
78768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                 0.2664f,  1.7135f, -0.0685f,
78868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                -0.1614f,  0.0367f,  1.0296f
78968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }),
79068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
7914db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy         * von Kries chromatic adaptation transform.
79268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
79368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        VON_KRIES(new float[] {
79468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                 0.40024f, -0.22630f, 0.00000f,
79568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                 0.70760f,  1.16532f, 0.00000f,
79668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                -0.08081f,  0.04570f, 0.91822f
7974db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy        }),
7984db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy        /**
7994db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy         * CIECAT02 chromatic adaption transform, as defined in the
8004db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy         * CIECAM02 color appearance model.
8014db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy         */
8024db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy        CIECAT02(new float[] {
8034db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy                 0.7328f, -0.7036f,  0.0030f,
8044db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy                 0.4296f,  1.6975f,  0.0136f,
8054db2c229be8e4d243ca19fac4080cda1eeb22710Romain Guy                -0.1624f,  0.0061f,  0.9834f
80668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        });
80768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
80868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        final float[] mTransform;
80968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
81068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        Adaptation(@NonNull @Size(9) float[] transform) {
81168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mTransform = transform;
81268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
81368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
81468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
81568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
81668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * A color model is required by a {@link ColorSpace} to describe the
81768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * way colors can be represented as tuples of numbers. A common color
81868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * model is the {@link #RGB RGB} color model which defines a color
81968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * as represented by a tuple of 3 numbers (red, green and blue).
82068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
82168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public enum Model {
82268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
82368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The RGB model is a color model with 3 components that
82468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * refer to the three additive primiaries: red, green
82568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * andd blue.
82668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
82768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        RGB(3),
82868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
82968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The XYZ model is a color model with 3 components that
83068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * are used to model human color vision on a basic sensory
83168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * level.
83268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
83368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        XYZ(3),
83468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
83568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The Lab model is a color model with 3 components used
83668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to describe a color space that is more perceptually
83768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * uniform than XYZ.
83868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
83968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        LAB(3),
84068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
84168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The CMYK model is a color model with 4 components that
84268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * refer to four inks used in color printing: cyan, magenta,
84368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * yellow and black (or key). CMYK is a subtractive color
84468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * model.
84568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
84668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        CMYK(4);
84768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
84868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private final int mComponentCount;
84968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
85068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        Model(@IntRange(from = 1, to = 4) int componentCount) {
85168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mComponentCount = componentCount;
85268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
85368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
85468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
85568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Returns the number of components for this color model.
85668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
85768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return An integer between 1 and 4
85868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
85968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @IntRange(from = 1, to = 4)
86068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public int getComponentCount() {
86168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mComponentCount;
86268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
86368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
86468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
86568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private ColorSpace(
86668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull String name,
86768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull Model model,
86868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @IntRange(from = MIN_ID, to = MAX_ID) int id) {
86968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
87068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (name == null || name.length() < 1) {
87168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            throw new IllegalArgumentException("The name of a color space cannot be null and " +
87268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    "must contain at least 1 character");
87368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
87468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
87568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (model == null) {
87668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            throw new IllegalArgumentException("A color space must have a model");
87768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
87868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
87968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (id < MIN_ID || id > MAX_ID) {
88068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            throw new IllegalArgumentException("The id must be between " +
88168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    MIN_ID + " and " + MAX_ID);
88268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
88368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
88468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        mName = name;
88568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        mModel = model;
88668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        mId = id;
88768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
88868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
88968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
890cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * <p>Returns the name of this color space. The name is never null
891cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * and contains always at least 1 character.</p>
892cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *
893cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * <p>Color space names are recommended to be unique but are not
894cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * guaranteed to be. There is no defined format but the name usually
895cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * falls in one of the following categories:</p>
896cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * <ul>
897cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *     <li>Generic names used to identify color spaces in non-RGB
898cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *     color models. For instance: {@link Named#CIE_LAB Generic L*a*b*}.</li>
899cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *     <li>Names tied to a particular specification. For instance:
900cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *     {@link Named#SRGB sRGB IEC61966-2.1} or
901cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *     {@link Named#ACES SMPTE ST 2065-1:2012 ACES}.</li>
902cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *     <li>Ad-hoc names, often generated procedurally or by the user
903cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *     during a calibration workflow. These names often contain the
904cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *     make and model of the display.</li>
905cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * </ul>
906cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *
907cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * <p>Because the format of color space names is not defined, it is
908cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * not recommended to programmatically identify a color space by its
909cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * name alone. Names can be used as a first approximation.</p>
910cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     *
911cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * <p>It is however perfectly acceptable to display color space names to
912cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * users in a UI, or in debuggers and logs. When displaying a color space
913cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * name to the user, it is recommended to add extra information to avoid
914cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * ambiguities: color model, a representation of the color space's gamut,
915cb7a24539bc5c0595ee554f982c2e6233d232b7aRomain Guy     * white point, etc.</p>
91668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
91768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A non-null String of length >= 1
91868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
91968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
92068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public String getName() {
92168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return mName;
92268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
92368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
92468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
92568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Returns the ID of this color space. Positive IDs match the color
92668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * spaces enumerated in {@link Named}. A negative ID indicates a
92768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color space created by calling one of the public constructors.
92868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
92968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return An integer between {@link #MIN_ID} and {@link #MAX_ID}
93068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
93168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @IntRange(from = MIN_ID, to = MAX_ID)
93268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public int getId() {
93368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return mId;
93468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
93568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
93668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
93768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Return the color model of this color space.
93868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
93968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A non-null {@link Model}
94068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
94168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see Model
94268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #getComponentCount()
94368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
94468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
94568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public Model getModel() {
94668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return mModel;
94768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
94868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
94968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
95068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Returns the number of components that form a color value according
95168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * to this color space's color model.
95268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
95368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return An integer between 1 and 4
95468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
95568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see Model
95668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #getModel()
95768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
95868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @IntRange(from = 1, to = 4)
95968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public int getComponentCount() {
96068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return mModel.getComponentCount();
96168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
96268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
96368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
96468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Returns whether this color space is a wide-gamut color space.
96568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * An RGB color space is wide-gamut if its gamut entirely contains
96668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * the {@link Named#SRGB sRGB} gamut and if the area of its gamut is
96768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * 90% of greater than the area of the {@link Named#NTSC_1953 NTSC}
96868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * gamut.
96968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
97068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return True if this color space is a wide-gamut color space,
97168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         false otherwise
97268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
97368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public abstract boolean isWideGamut();
97468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
97568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
97668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Indicates whether this color space is the sRGB color space or
97768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * equivalent to the sRGB color space.</p>
97868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>A color space is considered sRGB if it meets all the following
97968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * conditions:</p>
98068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <ul>
98168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>Its color model is {@link Model#RGB}.</li>
98268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>
98368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         Its primaries are within 1e-3 of the true
98468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         {@link Named#SRGB sRGB} primaries.
98568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     </li>
98668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>
98768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         Its white point is withing 1e-3 of the CIE standard
98868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         illuminant {@link #ILLUMINANT_D65 D65}.
98968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     </li>
99068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>Its opto-electronic transfer function is not linear.</li>
99168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>Its electro-optical transfer function is not linear.</li>
99268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>Its range is \([0..1]\).</li>
99368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * </ul>
99468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>This method always returns true for {@link Named#SRGB}.</p>
99568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
99668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return True if this color space is the sRGB color space (or a
99768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         close approximation), false otherwise
99868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
99968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public boolean isSrgb() {
100068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return false;
100168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
100268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
100368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
100468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Returns the minimum valid value for the specified component of this
100568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color space's color model.
100668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
100768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param component The index of the component
100868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A floating point value less than {@link #getMaxValue(int)}
100968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
101068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #getMaxValue(int)
101168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see Model#getComponentCount()
101268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
101368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public abstract float getMinValue(@IntRange(from = 0, to = 3) int component);
101468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
101568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
101668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Returns the maximum valid value for the specified component of this
101768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color space's color model.
101868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
101968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param component The index of the component
102068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A floating point value greater than {@link #getMinValue(int)}
102168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
102268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #getMinValue(int)
102368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see Model#getComponentCount()
102468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
102568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public abstract float getMaxValue(@IntRange(from = 0, to = 3) int component);
102668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
102768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
102868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Converts a color value from this color space's model to
102968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * tristimulus CIE XYZ values. If the color model of this color
103068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * space is not {@link Model#RGB RGB}, it is assumed that the
103168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * target CIE XYZ space uses a {@link #ILLUMINANT_D50 D50}
103268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * standard illuminant.</p>
103368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
103468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>This method is a convenience for color spaces with a model
103568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * of 3 components ({@link Model#RGB RGB} or {@link Model#LAB}
103668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * for instance). With color spaces using fewer or more components,
103768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * use {@link #toXyz(float[])} instead</p>.
103868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
103968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param r The first component of the value to convert from (typically R in RGB)
104068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param g The second component of the value to convert from (typically G in RGB)
104168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param b The third component of the value to convert from (typically B in RGB)
104268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A new array of 3 floats, containing tristimulus XYZ values
104368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
104468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #toXyz(float[])
104568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #fromXyz(float, float, float)
104668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
104768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
104868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Size(3)
104968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public float[] toXyz(float r, float g, float b) {
105068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return toXyz(new float[] { r, g, b });
105168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
105268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
105368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
105468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Converts a color value from this color space's model to
105568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * tristimulus CIE XYZ values. If the color model of this color
105668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * space is not {@link Model#RGB RGB}, it is assumed that the
105768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * target CIE XYZ space uses a {@link #ILLUMINANT_D50 D50}
105868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * standard illuminant.</p>
105968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
106068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p class="note">The specified array's length  must be at least
106168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * equal to to the number of color components as returned by
106268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@link Model#getComponentCount()}.</p>
106368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
106468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param v An array of color components containing the color space's
106568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *          color value to convert to XYZ, and large enough to hold
106668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *          the resulting tristimulus XYZ values
106768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return The array passed in parameter
106868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
106968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #toXyz(float, float, float)
107068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #fromXyz(float[])
107168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
107268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
107368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Size(min = 3)
107468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public abstract float[] toXyz(@NonNull @Size(min = 3) float[] v);
107568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
107668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
107768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Converts tristimulus values from the CIE XYZ space to this
107868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color space's color model.</p>
107968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
108068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param x The X component of the color value
108168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param y The Y component of the color value
108268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param z The Z component of the color value
108368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A new array whose size is equal to the number of color
108468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         components as returned by {@link Model#getComponentCount()}
108568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
108668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #fromXyz(float[])
108768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #toXyz(float, float, float)
108868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
108968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
109068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Size(min = 3)
109168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public float[] fromXyz(float x, float y, float z) {
109268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float[] xyz = new float[mModel.getComponentCount()];
109368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        xyz[0] = x;
109468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        xyz[1] = y;
109568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        xyz[2] = z;
109668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return fromXyz(xyz);
109768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
109868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
109968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
110068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Converts tristimulus values from the CIE XYZ space to this color
110168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * space's color model. The resulting value is passed back in the specified
110268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * array.</p>
110368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
110477b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy     * <p class="note">The specified array's length  must be at least equal to
110568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * to the number of color components as returned by
110668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@link Model#getComponentCount()}, and its first 3 values must
110768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * be the XYZ components to convert from.</p>
110868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
110968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param v An array of color components containing the XYZ values
111068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *          to convert from, and large enough to hold the number
111168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *          of components of this color space's model
111268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return The array passed in parameter
111368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
111468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #fromXyz(float, float, float)
111568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #toXyz(float[])
111668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
111768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
111868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Size(min = 3)
111968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public abstract float[] fromXyz(@NonNull @Size(min = 3) float[] v);
112068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
112168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
112268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Returns a string representation of the object. This method returns
112368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * a string equal to the value of:</p>
112468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
112568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <pre class="prettyprint">
112668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * getName() + "(id=" + getId() + ", model=" + getModel() + ")"
112768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * </pre>
112868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
112968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>For instance, the string representation of the {@link Named#SRGB sRGB}
113068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color space is equal to the following value:</p>
113168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
113268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <pre>
113368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * sRGB IEC61966-2.1 (id=0, model=RGB)
113468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * </pre>
113568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
113668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A string representation of the object
113768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
113868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Override
113977b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy    @NonNull
114068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public String toString() {
114168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return mName + " (id=" + mId + ", model=" + mModel + ")";
114268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
114368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
114468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Override
114568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public boolean equals(Object o) {
114668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (this == o) return true;
114768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (o == null || getClass() != o.getClass()) return false;
114868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
114968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        ColorSpace that = (ColorSpace) o;
115068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
115168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (mId != that.mId) return false;
115268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        //noinspection SimplifiableIfStatement
115368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (!mName.equals(that.mName)) return false;
115468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return mModel == that.mModel;
115568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
115668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
115768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
115868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Override
115968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public int hashCode() {
116068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        int result = mName.hashCode();
116168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        result = 31 * result + mModel.hashCode();
116268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        result = 31 * result + mId;
116368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return result;
116468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
116568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
116668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
116768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Connects two color spaces to allow conversion from the source color
116868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * space to the destination color space. If the source and destination
116968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color spaces do not have the same profile connection space (CIE XYZ
117068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * with the same white point), they are chromatically adapted to use the
117168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * CIE standard illuminant {@link #ILLUMINANT_D50 D50} as needed.</p>
117268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
117368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>If the source and destination are the same, an optimized connector
117468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * is returned to avoid unnecessary computations and loss of precision.</p>
117568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
117668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Colors are mapped from the source color space to the destination color
117768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * space using the {@link RenderIntent#PERCEPTUAL perceptual} render intent.</p>
117868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
117968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param source The color space to convert colors from
118068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param destination The color space to convert colors to
118168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A non-null connector between the two specified color spaces
118268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
118368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace)
118468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace, RenderIntent)
118568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace, ColorSpace, RenderIntent)
118668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
118768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
118868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static Connector connect(@NonNull ColorSpace source, @NonNull ColorSpace destination) {
118968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return connect(source, destination, RenderIntent.PERCEPTUAL);
119068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
119168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
119268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
119368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Connects two color spaces to allow conversion from the source color
119468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * space to the destination color space. If the source and destination
119568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color spaces do not have the same profile connection space (CIE XYZ
119668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * with the same white point), they are chromatically adapted to use the
119768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * CIE standard illuminant {@link #ILLUMINANT_D50 D50} as needed.</p>
119868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
119968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>If the source and destination are the same, an optimized connector
120068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * is returned to avoid unnecessary computations and loss of precision.</p>
120168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
120268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param source The color space to convert colors from
120368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param destination The color space to convert colors to
120468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param intent The render intent to map colors from the source to the destination
120568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A non-null connector between the two specified color spaces
120668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
120768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace)
120868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace, RenderIntent)
120968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace, ColorSpace)
121068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
121168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
121268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @SuppressWarnings("ConstantConditions")
121368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static Connector connect(@NonNull ColorSpace source, @NonNull ColorSpace destination,
121468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull RenderIntent intent) {
121568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (source.equals(destination)) return Connector.identity(source);
121668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
121768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (source.getModel() == Model.RGB && destination.getModel() == Model.RGB) {
1218199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy            return new Connector.Rgb((Rgb) source, (Rgb) destination, intent);
121968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
122068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
122168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return new Connector(source, destination, intent);
122268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
122368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
122468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
122568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Connects the specified color spaces to sRGB.
122668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * If the source color space does not use CIE XYZ D65 as its profile
122768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * connection space, the two spaces are chromatically adapted to use the
122868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * CIE standard illuminant {@link #ILLUMINANT_D50 D50} as needed.</p>
122968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
123068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>If the source is the sRGB color space, an optimized connector
123168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * is returned to avoid unnecessary computations and loss of precision.</p>
123268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
123368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Colors are mapped from the source color space to the destination color
123468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * space using the {@link RenderIntent#PERCEPTUAL perceptual} render intent.</p>
123568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
123668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param source The color space to convert colors from
123768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A non-null connector between the specified color space and sRGB
123868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
123968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace, RenderIntent)
124068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace, ColorSpace)
124168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace, ColorSpace, RenderIntent)
124268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
124368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
124468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static Connector connect(@NonNull ColorSpace source) {
124568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return connect(source, RenderIntent.PERCEPTUAL);
124668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
124768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
124868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
124968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Connects the specified color spaces to sRGB.
125068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * If the source color space does not use CIE XYZ D65 as its profile
125168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * connection space, the two spaces are chromatically adapted to use the
125268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * CIE standard illuminant {@link #ILLUMINANT_D50 D50} as needed.</p>
125368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
125468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>If the source is the sRGB color space, an optimized connector
125568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * is returned to avoid unnecessary computations and loss of precision.</p>
125668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
125768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param source The color space to convert colors from
125868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param intent The render intent to map colors from the source to the destination
125968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A non-null connector between the specified color space and sRGB
126068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
126168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace)
126268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace, ColorSpace)
126368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #connect(ColorSpace, ColorSpace, RenderIntent)
126468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
126568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
126668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static Connector connect(@NonNull ColorSpace source, @NonNull RenderIntent intent) {
126768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (source.isSrgb()) return Connector.identity(source);
126868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
126968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (source.getModel() == Model.RGB) {
1270199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy            return new Connector.Rgb((Rgb) source, (Rgb) get(Named.SRGB), intent);
127168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
127268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
127368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return new Connector(source, get(Named.SRGB), intent);
127468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
127568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
127668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
127768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Performs the chromatic adaptation of a color space from its native
127868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * white point to the specified white point.</p>
127968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
128068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>The chromatic adaptation is performed using the
128168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@link Adaptation#BRADFORD} matrix.</p>
128268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
128368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p class="note">The color space returned by this method always has
128468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * an ID of {@link #MIN_ID}.</p>
128568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
128668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param colorSpace The color space to chromatically adapt
128768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param whitePoint The new white point
128868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A {@link ColorSpace} instance with the same name, primaries,
128968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         transfer functions and range as the specified color space
129068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
129168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see Adaptation
129268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #adapt(ColorSpace, float[], Adaptation)
129368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
129468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
129568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static ColorSpace adapt(@NonNull ColorSpace colorSpace,
129668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull @Size(min = 2, max = 3) float[] whitePoint) {
129768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return adapt(colorSpace, whitePoint, Adaptation.BRADFORD);
129868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
129968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
130068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
130168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Performs the chromatic adaptation of a color space from its native
130268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * white point to the specified white point. If the specified color space
130368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * does not have an {@link Model#RGB RGB} color model, or if the color
130468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * space already has the target white point, the color space is returned
130568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * unmodified.</p>
130668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
130768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>The chromatic adaptation is performed using the von Kries method
130868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * described in the documentation of {@link Adaptation}.</p>
130968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
131068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p class="note">The color space returned by this method always has
131168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * an ID of {@link #MIN_ID}.</p>
131268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
131368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param colorSpace The color space to chromatically adapt
131468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param whitePoint The new white point
131568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param adaptation The adaptation matrix
131668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A new color space if the specified color space has an RGB
131768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         model and a white point different from the specified white
131868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         point; the specified color space otherwise
131968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
132068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see Adaptation
132168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see #adapt(ColorSpace, float[])
132268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
132368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
132468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static ColorSpace adapt(@NonNull ColorSpace colorSpace,
132568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull @Size(min = 2, max = 3) float[] whitePoint,
132668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull Adaptation adaptation) {
132768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (colorSpace.getModel() == Model.RGB) {
132868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            ColorSpace.Rgb rgb = (ColorSpace.Rgb) colorSpace;
132968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (compare(rgb.mWhitePoint, whitePoint)) return colorSpace;
133068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
133168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float[] xyz = whitePoint.length == 3 ?
133268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    Arrays.copyOf(whitePoint, 3) : xyYToXyz(whitePoint);
133368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float[] adaptationTransform = chromaticAdaptation(adaptation.mTransform,
133468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    xyYToXyz(rgb.getWhitePoint()), xyz);
133568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float[] transform = mul3x3(adaptationTransform, rgb.mTransform);
133668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
133768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return new ColorSpace.Rgb(rgb, transform, whitePoint);
133868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
133968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return colorSpace;
134068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
134168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
134268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
134326ca96ecb82cdbc058a39cebbfb16b402bf0fe03Romain Guy     * <p>Returns an instance of {@link ColorSpace} whose ID matches the
134426ca96ecb82cdbc058a39cebbfb16b402bf0fe03Romain Guy     * specified ID.</p>
134568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
134668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>This method always returns the same instance for a given ID.</p>
134768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
134868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>This method is thread-safe.</p>
134968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
135068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param index An integer ID between {@link #MIN_ID} and {@link #MAX_ID}
135168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A non-null {@link ColorSpace} instance
135226ca96ecb82cdbc058a39cebbfb16b402bf0fe03Romain Guy     * @throws IllegalArgumentException If the ID does not match the ID of one of the
135326ca96ecb82cdbc058a39cebbfb16b402bf0fe03Romain Guy     *         {@link Named named color spaces}
135468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
135568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
135668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) {
135768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (index < 0 || index > Named.values().length) {
135826ca96ecb82cdbc058a39cebbfb16b402bf0fe03Romain Guy            throw new IllegalArgumentException("Invalid ID, must be in the range [0.." +
135926ca96ecb82cdbc058a39cebbfb16b402bf0fe03Romain Guy                    Named.values().length + "]");
136068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
136168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return sNamedColorSpaces[index];
136268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
136368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
136468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
136568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Returns an instance of {@link ColorSpace} identified by the specified
136668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * name. The list of names provided in the {@link Named} enum gives access
136768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * to a variety of common RGB color spaces.</p>
136868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
136968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>This method always returns the same instance for a given name.</p>
137068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
137168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>This method is thread-safe.</p>
137268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
137368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param name The name of the color space to get an instance of
137468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A non-null {@link ColorSpace} instance
137568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
137668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
137768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static ColorSpace get(@NonNull Named name) {
137868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return sNamedColorSpaces[name.ordinal()];
137968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
138068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
138115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy    /**
1382efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * <p>Returns a {@link Named} instance of {@link ColorSpace} that matches
1383efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * the specified RGB to CIE XYZ transform and transfer functions. If no
1384efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * instance can be found, this method returns null.</p>
1385efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     *
1386efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * <p>The color transform matrix is assumed to target the CIE XYZ space
1387efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * a {@link #ILLUMINANT_D50 D50} standard illuminant.</p>
1388efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     *
1389efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * @param toXYZD50 3x3 column-major transform matrix from RGB to the profile
1390efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     *                 connection space CIE XYZ as an array of 9 floats, cannot be null
1391efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * @param function Parameters for the transfer functions
1392efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * @return A non-null {@link ColorSpace} if a match is found, null otherwise
1393efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     */
1394efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    @Nullable
1395efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    public static ColorSpace match(
1396efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            @NonNull @Size(9) float[] toXYZD50,
1397efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            @NonNull Rgb.TransferParameters function) {
1398efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
1399efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        for (ColorSpace colorSpace : sNamedColorSpaces) {
1400efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            if (colorSpace.getModel() == Model.RGB) {
1401efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                ColorSpace.Rgb rgb = (ColorSpace.Rgb) adapt(colorSpace, ILLUMINANT_D50_XYZ);
1402efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (compare(toXYZD50, rgb.mTransform) &&
1403efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                        compare(function, rgb.mTransferParameters)) {
1404efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    return colorSpace;
1405efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                }
1406efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            }
1407efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        }
1408efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
1409efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        return null;
1410efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    }
1411efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
1412efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    /**
141315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * <p>Creates a new {@link Renderer} that can be used to visualize and
141415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * debug color spaces. See the documentation of {@link Renderer} for
141515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * more information.</p>
141615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *
141715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * @return A new non-null {@link Renderer} instance
141815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *
141915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * @see Renderer
142066d7da6a41d2ae2acf8220d8b146ba6eea385a28Romain Guy     *
142166d7da6a41d2ae2acf8220d8b146ba6eea385a28Romain Guy     * @hide
142215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     */
142315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy    @NonNull
142415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy    public static Renderer createRenderer() {
142515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        return new Renderer();
142615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy    }
142715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
142868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    static {
142968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.SRGB.ordinal()] = new ColorSpace.Rgb(
143068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "sRGB IEC61966-2.1",
143168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                SRGB_PRIMARIES,
143268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D65,
1433efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
143468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.SRGB.ordinal()
143568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
143668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb(
143768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "sRGB IEC61966-2.1 (Linear)",
143868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                SRGB_PRIMARIES,
143968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D65,
1440efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                1.0,
144168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                0.0f, 1.0f,
144268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.LINEAR_SRGB.ordinal()
144368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
144468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
144568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "scRGB-nl IEC 61966-2-2:2003",
144668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                SRGB_PRIMARIES,
144768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D65,
1448efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                x -> absRcpResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
1449efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                x -> absResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
145077b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy                -0.799f, 2.399f,
145168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.EXTENDED_SRGB.ordinal()
145268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
145368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
1454efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                "scRGB IEC 61966-2-2:2003",
145568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                SRGB_PRIMARIES,
145668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D65,
1457efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                1.0,
145877b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy                -0.5f, 7.499f,
145968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.LINEAR_EXTENDED_SRGB.ordinal()
146068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
146168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.BT709.ordinal()] = new ColorSpace.Rgb(
146268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "Rec. ITU-R BT.709-5",
146368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                new float[] { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
146468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D65,
1465efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
146668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.BT709.ordinal()
146768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
146868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb(
146968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "Rec. ITU-R BT.2020-1",
147068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                new float[] { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f },
147168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D65,
1472efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45),
147368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.BT2020.ordinal()
147468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
147568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb(
147668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "SMPTE RP 431-2-2007 DCI (P3)",
147768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
147868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                new float[] { 0.314f, 0.351f },
1479efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                2.6,
148068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                0.0f, 1.0f,
148168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.DCI_P3.ordinal()
148268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
148368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.DISPLAY_P3.ordinal()] = new ColorSpace.Rgb(
148468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "Display P3",
148568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
148668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D65,
1487494f784e47e74a65e8bce485065f5b7b077800fbRomain Guy                new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.039, 2.4),
148868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.DISPLAY_P3.ordinal()
148968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
149068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
149168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "NTSC (1953)",
149268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                NTSC_1953_PRIMARIES,
149368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_C,
1494efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
149568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.NTSC_1953.ordinal()
149668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
149768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.SMPTE_C.ordinal()] = new ColorSpace.Rgb(
149868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "SMPTE-C RGB",
149968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f },
150068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D65,
1501efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
150268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.SMPTE_C.ordinal()
150368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
150468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.ADOBE_RGB.ordinal()] = new ColorSpace.Rgb(
150568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "Adobe RGB (1998)",
150668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f },
150768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D65,
1508efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                2.2,
150968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                0.0f, 1.0f,
151068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.ADOBE_RGB.ordinal()
151168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
151268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.PRO_PHOTO_RGB.ordinal()] = new ColorSpace.Rgb(
151368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "ROMM RGB ISO 22028-2:2013",
151468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
151568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D50,
1516efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8),
151768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.PRO_PHOTO_RGB.ordinal()
151868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
151968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.ACES.ordinal()] = new ColorSpace.Rgb(
152068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "SMPTE ST 2065-1:2012 ACES",
152168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f },
152268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D60,
1523efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                1.0,
152468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                -65504.0f, 65504.0f,
152568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.ACES.ordinal()
152668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
152768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.ACESCG.ordinal()] = new ColorSpace.Rgb(
152868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "Academy S-2014-004 ACEScg",
152968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f },
153068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ILLUMINANT_D60,
1531efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                1.0,
153268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                -65504.0f, 65504.0f,
153368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.ACESCG.ordinal()
153468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
153568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.CIE_XYZ.ordinal()] = new Xyz(
153668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "Generic XYZ",
153768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.CIE_XYZ.ordinal()
153868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
153968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        sNamedColorSpaces[Named.CIE_LAB.ordinal()] = new ColorSpace.Lab(
154068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                "Generic L*a*b*",
154168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                Named.CIE_LAB.ordinal()
154268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        );
154368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
154468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
154568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    // Reciprocal piecewise gamma response
1546efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    private static double rcpResponse(double x, double a, double b, double c, double d, double g) {
154768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return x >= d * c ? (Math.pow(x, 1.0 / g) - b) / a : x / c;
154868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
154968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
155068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    // Piecewise gamma response
1551efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    private static double response(double x, double a, double b, double c, double d, double g) {
155268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return x >= d ? Math.pow(a * x + b, g) : c * x;
155368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
155468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
1555efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    // Reciprocal piecewise gamma response
1556efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    private static double rcpResponse(double x, double a, double b, double c, double d,
1557efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            double e, double f, double g) {
1558efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        return x >= d * c ? (Math.pow(x - e, 1.0 / g) - b) / a : (x - f) / c;
1559efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    }
1560efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
1561efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    // Piecewise gamma response
1562efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    private static double response(double x, double a, double b, double c, double d,
1563efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            double e, double f, double g) {
1564efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        return x >= d ? Math.pow(a * x + b, g) + e : c * x + f;
1565efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    }
1566efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
156768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    // Reciprocal piecewise gamma response, encoded as sign(x).f(abs(x)) for color
156868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    // spaces that allow negative values
156968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @SuppressWarnings("SameParameterValue")
1570efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    private static double absRcpResponse(double x, double a, double b, double c, double d, double g) {
1571efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        return Math.copySign(rcpResponse(x < 0.0 ? -x : x, a, b, c, d, g), x);
157268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
157368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
157468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    // Piecewise gamma response, encoded as sign(x).f(abs(x)) for color spaces that
157568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    // allow negative values
157668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @SuppressWarnings("SameParameterValue")
1577efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    private static double absResponse(double x, double a, double b, double c, double d, double g) {
1578efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        return Math.copySign(response(x < 0.0 ? -x : x, a, b, c, d, g), x);
1579efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    }
1580efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
1581efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    /**
1582efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * Compares two sets of parametric transfer functions parameters with a precision of 1e-3.
1583efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     *
1584efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * @param a The first set of parameters to compare
1585efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * @param b The second set of parameters to compare
1586efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * @return True if the two sets are equal, false otherwise
1587efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     */
1588efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy    private static boolean compare(
1589efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            @Nullable Rgb.TransferParameters a,
1590efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            @Nullable Rgb.TransferParameters b) {
1591efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        //noinspection SimplifiableIfStatement
1592efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        if (a == null && b == null) return true;
1593efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        return a != null && b != null &&
1594efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                Math.abs(a.a - b.a) < 1e-3 &&
1595efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                Math.abs(a.b - b.b) < 1e-3 &&
1596efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                Math.abs(a.c - b.c) < 1e-3 &&
159755455181233cadcd6d2e28d28d0dfc9a653f7787Romain Guy                Math.abs(a.d - b.d) < 2e-3 && // Special case for variations in sRGB OETF/EOTF
1598efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                Math.abs(a.e - b.e) < 1e-3 &&
1599efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                Math.abs(a.f - b.f) < 1e-3 &&
1600efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                Math.abs(a.g - b.g) < 1e-3;
160168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
160268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
160368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
160468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Compares two arrays of float with a precision of 1e-3.
160568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
160668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param a The first array to compare
160768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param b The second array to compare
160868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return True if the two arrays are equal, false otherwise
160968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
161068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static boolean compare(@NonNull float[] a, @NonNull float[] b) {
161168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        if (a == b) return true;
161268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        for (int i = 0; i < a.length; i++) {
161368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (Float.compare(a[i], b[i]) != 0 && Math.abs(a[i] - b[i]) > 1e-3f) return false;
161468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
161568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return true;
161668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
161768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
161868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
161968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Inverts a 3x3 matrix. This method assumes the matrix is invertible.
162068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
162168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param m A 3x3 matrix as a non-null array of 9 floats
162268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A new array of 9 floats containing the inverse of the input matrix
162368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
162468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
162568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Size(9)
162668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static float[] inverse3x3(@NonNull @Size(9) float[] m) {
162768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float a = m[0];
162868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float b = m[3];
162968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float c = m[6];
163068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float d = m[1];
163168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float e = m[4];
163268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float f = m[7];
163368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float g = m[2];
163468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float h = m[5];
163568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float i = m[8];
163668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
163768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float A = e * i - f * h;
163868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float B = f * g - d * i;
163968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float C = d * h - e * g;
164068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
164168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float det = a * A + b * B + c * C;
164268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
164368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float inverted[] = new float[m.length];
164468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        inverted[0] = A / det;
164568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        inverted[1] = B / det;
164668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        inverted[2] = C / det;
164768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        inverted[3] = (c * h - b * i) / det;
164868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        inverted[4] = (a * i - c * g) / det;
164968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        inverted[5] = (b * g - a * h) / det;
165068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        inverted[6] = (b * f - c * e) / det;
165168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        inverted[7] = (c * d - a * f) / det;
165268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        inverted[8] = (a * e - b * d) / det;
165368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return inverted;
165468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
165568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
165668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
165768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Multiplies two 3x3 matrices, represented as non-null arrays of 9 floats.
165868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
165968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param lhs 3x3 matrix, as a non-null array of 9 floats
166068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param rhs 3x3 matrix, as a non-null array of 9 floats
166168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A new array of 9 floats containing the result of the multiplication
166268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         of rhs by lhs
166368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
166468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
166568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Size(9)
166668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) {
166768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float[] r = new float[9];
166868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        r[0] = lhs[0] * rhs[0] + lhs[3] * rhs[1] + lhs[6] * rhs[2];
166968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        r[1] = lhs[1] * rhs[0] + lhs[4] * rhs[1] + lhs[7] * rhs[2];
167068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        r[2] = lhs[2] * rhs[0] + lhs[5] * rhs[1] + lhs[8] * rhs[2];
167168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        r[3] = lhs[0] * rhs[3] + lhs[3] * rhs[4] + lhs[6] * rhs[5];
167268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        r[4] = lhs[1] * rhs[3] + lhs[4] * rhs[4] + lhs[7] * rhs[5];
167368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        r[5] = lhs[2] * rhs[3] + lhs[5] * rhs[4] + lhs[8] * rhs[5];
167468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        r[6] = lhs[0] * rhs[6] + lhs[3] * rhs[7] + lhs[6] * rhs[8];
167568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        r[7] = lhs[1] * rhs[6] + lhs[4] * rhs[7] + lhs[7] * rhs[8];
167668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        r[8] = lhs[2] * rhs[6] + lhs[5] * rhs[7] + lhs[8] * rhs[8];
167768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return r;
167868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
167968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
168068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
168168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Multiplies a vector of 3 components by a 3x3 matrix and stores the
168268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * result in the input vector.
168368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
168468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param lhs 3x3 matrix, as a non-null array of 9 floats
168568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param rhs Vector of 3 components, as a non-null array of 3 floats
168668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return The array of 3 passed as the rhs parameter
168768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
168868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
168968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Size(min = 3)
169068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static float[] mul3x3Float3(
169168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull @Size(9) float[] lhs, @NonNull @Size(min = 3) float[] rhs) {
169268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float r0 = rhs[0];
169368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float r1 = rhs[1];
169468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float r2 = rhs[2];
169568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        rhs[0] = lhs[0] * r0 + lhs[3] * r1 + lhs[6] * r2;
169668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        rhs[1] = lhs[1] * r0 + lhs[4] * r1 + lhs[7] * r2;
169768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        rhs[2] = lhs[2] * r0 + lhs[5] * r1 + lhs[8] * r2;
169868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return rhs;
169968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
170068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
170168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
170268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Multiplies a diagonal 3x3 matrix lhs, represented as an array of 3 floats,
170368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * by a 3x3 matrix represented as an array of 9 floats.
170468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
170568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param lhs Diagonal 3x3 matrix, as a non-null array of 3 floats
170668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param rhs 3x3 matrix, as a non-null array of 9 floats
170768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A new array of 9 floats containing the result of the multiplication
170868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         of rhs by lhs
170968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
171068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
171168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Size(9)
171268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static float[] mul3x3Diag(
171368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull @Size(3) float[] lhs, @NonNull @Size(9) float[] rhs) {
171468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return new float[] {
171568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                lhs[0] * rhs[0], lhs[1] * rhs[1], lhs[2] * rhs[2],
171668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                lhs[0] * rhs[3], lhs[1] * rhs[4], lhs[2] * rhs[5],
171768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                lhs[0] * rhs[6], lhs[1] * rhs[7], lhs[2] * rhs[8]
171868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        };
171968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
172068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
172168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
172268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Converts a value from CIE xyY to CIE XYZ. Y is assumed to be 1 so the
172368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * input xyY array only contains the x and y components.
172468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
172568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param xyY The xyY value to convert to XYZ, cannot be null, length must be 2
172668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A new float array of length 3 containing XYZ values
172768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
172868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
172968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Size(3)
173068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static float[] xyYToXyz(@NonNull @Size(2) float[] xyY) {
173168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return new float[] { xyY[0] / xyY[1], 1.0f, (1 - xyY[0] - xyY[1]) / xyY[1] };
173268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
173368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
173468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
17359505a6552764461c22ce48f1ac13d025d23e1579Romain Guy     * Converts values from CIE xyY to CIE L*u*v*. Y is assumed to be 1 so the
17369505a6552764461c22ce48f1ac13d025d23e1579Romain Guy     * input xyY array only contains the x and y components. After this method
17379505a6552764461c22ce48f1ac13d025d23e1579Romain Guy     * returns, the xyY array contains the converted u and v components.
17389505a6552764461c22ce48f1ac13d025d23e1579Romain Guy     *
17399505a6552764461c22ce48f1ac13d025d23e1579Romain Guy     * @param xyY The xyY value to convert to XYZ, cannot be null,
17409505a6552764461c22ce48f1ac13d025d23e1579Romain Guy     *            length must be a multiple of 2
17419505a6552764461c22ce48f1ac13d025d23e1579Romain Guy     */
17429505a6552764461c22ce48f1ac13d025d23e1579Romain Guy    private static void xyYToUv(@NonNull @Size(multiple = 2) float[] xyY) {
17439505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        for (int i = 0; i < xyY.length; i += 2) {
17449505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float x = xyY[i];
17459505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float y = xyY[i + 1];
17469505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
17479505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float d = -2.0f * x + 12.0f * y + 3;
17489505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float u = (4.0f * x) / d;
17499505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float v = (9.0f * y) / d;
17509505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
17519505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            xyY[i] = u;
17529505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            xyY[i + 1] = v;
17539505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        }
17549505a6552764461c22ce48f1ac13d025d23e1579Romain Guy    }
17559505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
17569505a6552764461c22ce48f1ac13d025d23e1579Romain Guy    /**
175768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Computes the chromatic adaptation transform from the specified
175868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * source white point to the specified destination white point.</p>
175968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
1760efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * <p>The transform is computed using the von Kries method, described
176168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * in more details in the documentation of {@link Adaptation}. The
176268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@link Adaptation} enum provides different matrices that can be
176368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * used to perform the adaptation.</p>
176468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
176568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param matrix The adaptation matrix
176668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param srcWhitePoint The white point to adapt from, *will be modified*
176768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @param dstWhitePoint The white point to adapt to, *will be modified*
176868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @return A 3x3 matrix as a non-null array of 9 floats
176968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
177068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @NonNull
177168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    @Size(9)
177268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static float[] chromaticAdaptation(@NonNull @Size(9) float[] matrix,
177368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull @Size(3) float[] srcWhitePoint, @NonNull @Size(3) float[] dstWhitePoint) {
177468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float[] srcLMS = mul3x3Float3(matrix, srcWhitePoint);
177568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float[] dstLMS = mul3x3Float3(matrix, dstWhitePoint);
177668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        // LMS is a diagonal matrix stored as a float[3]
177768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        float[] LMS = { dstLMS[0] / srcLMS[0], dstLMS[1] / srcLMS[1], dstLMS[2] / srcLMS[2] };
177868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        return mul3x3(inverse3x3(matrix), mul3x3Diag(LMS, matrix));
177968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
178068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
178168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
178268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Implementation of the CIE XYZ color space. Assumes the white point is D50.
178368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
178477b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy    @AnyThread
178568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static final class Xyz extends ColorSpace {
178668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private Xyz(@NonNull String name, @IntRange(from = MIN_ID, to = MAX_ID) int id) {
178768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            super(name, Model.XYZ, id);
178868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
178968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
179068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
179168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public boolean isWideGamut() {
179268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return true;
179368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
179468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
179568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
179668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float getMinValue(@IntRange(from = 0, to = 3) int component) {
179768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return -2.0f;
179868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
179968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
180068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
180168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float getMaxValue(@IntRange(from = 0, to = 3) int component) {
180268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return 2.0f;
180368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
180468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
180568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
180668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] toXyz(@NonNull @Size(min = 3) float[] v) {
180768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[0] = clamp(v[0]);
180868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[1] = clamp(v[1]);
180968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[2] = clamp(v[2]);
181068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return v;
181168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
181268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
181368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
181468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] fromXyz(@NonNull @Size(min = 3) float[] v) {
181568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[0] = clamp(v[0]);
181668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[1] = clamp(v[1]);
181768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[2] = clamp(v[2]);
181868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return v;
181968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
182068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
182168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static float clamp(float x) {
182268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return x < -2.0f ? -2.0f : x > 2.0f ? 2.0f : x;
182368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
182468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
182568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
182668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
182768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * Implementation of the CIE L*a*b* color space. Its PCS is CIE XYZ
182868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * with a white point of D50.
182968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
183077b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy    @AnyThread
183168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    private static final class Lab extends ColorSpace {
183268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static final float A = 216.0f / 24389.0f;
183368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static final float B = 841.0f / 108.0f;
183468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static final float C = 4.0f / 29.0f;
183568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static final float D = 6.0f / 29.0f;
183668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
183768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private Lab(@NonNull String name, @IntRange(from = MIN_ID, to = MAX_ID) int id) {
183868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            super(name, Model.LAB, id);
183968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
184068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
184168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
184268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public boolean isWideGamut() {
184368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return true;
184468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
184568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
184668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
184768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float getMinValue(@IntRange(from = 0, to = 3) int component) {
184868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return component == 0 ? 0.0f : -128.0f;
184968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
185068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
185168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
185268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float getMaxValue(@IntRange(from = 0, to = 3) int component) {
185368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return component == 0 ? 100.0f : 128.0f;
185468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
185568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
185668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
185768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] toXyz(@NonNull @Size(min = 3) float[] v) {
185868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[0] = clamp(v[0], 0.0f, 100.0f);
185968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[1] = clamp(v[1], -128.0f, 128.0f);
186068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[2] = clamp(v[2], -128.0f, 128.0f);
186168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
186268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float fy = (v[0] + 16.0f) / 116.0f;
186368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float fx = fy + (v[1] * 0.002f);
186468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float fz = fy - (v[2] * 0.005f);
186568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
186668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
186768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
186868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
186968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[0] = X * ILLUMINANT_D50_XYZ[0];
187068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[1] = Y * ILLUMINANT_D50_XYZ[1];
187168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[2] = Z * ILLUMINANT_D50_XYZ[2];
187268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
187368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return v;
187468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
187568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
187668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
187768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] fromXyz(@NonNull @Size(min = 3) float[] v) {
187868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float X = v[0] / ILLUMINANT_D50_XYZ[0];
187968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Y = v[1] / ILLUMINANT_D50_XYZ[1];
188068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Z = v[2] / ILLUMINANT_D50_XYZ[2];
188168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
188268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float fx = X > A ? (float) Math.pow(X, 1.0 / 3.0) : B * X + C;
188368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float fy = Y > A ? (float) Math.pow(Y, 1.0 / 3.0) : B * Y + C;
188468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float fz = Z > A ? (float) Math.pow(Z, 1.0 / 3.0) : B * Z + C;
188568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
188668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float L = 116.0f * fy - 16.0f;
188768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float a = 500.0f * (fx - fy);
188868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float b = 200.0f * (fy - fz);
188968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
189068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[0] = clamp(L, 0.0f, 100.0f);
189168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[1] = clamp(a, -128.0f, 128.0f);
189268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[2] = clamp(b, -128.0f, 128.0f);
189368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
189468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return v;
189568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
189668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
189768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static float clamp(float x, float min, float max) {
189868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return x < min ? min : x > max ? max : x;
189968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
190068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
190168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
190268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
190368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@usesMathJax}
190468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
190568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>An RGB color space is an additive color space using the
190668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@link Model#RGB RGB} color model (a color is therefore represented
190768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * by a tuple of 3 numbers).</p>
190868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
190968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>A specific RGB color space is defined by the following properties:</p>
191068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <ul>
191168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>Three chromaticities of the red, green and blue primaries, which
191268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     define the gamut of the color space.</li>
191368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>A white point chromaticity that defines the stimulus to which
191468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     color space values are normalized (also just called "white").</li>
191568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>An opto-electronic transfer function, also called opto-electronic
191668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     conversion function or often, and approximately, gamma function.</li>
191768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>An electro-optical transfer function, also called electo-optical
191868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     conversion function or often, and approximately, gamma function.</li>
191968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *     <li>A range of valid RGB values (most commonly \([0..1]\)).</li>
192068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * </ul>
192168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
192268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>The most commonly used RGB color space is {@link Named#SRGB sRGB}.</p>
192368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
192468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <h3>Primaries and white point chromaticities</h3>
192568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>In this implementation, the chromaticity of the primaries and the white
192668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * point of an RGB color space is defined in the CIE xyY color space. This
192768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color space separates the chromaticity of a color, the x and y components,
192868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * and its luminance, the Y component. Since the primaries and the white
192968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * point have full brightness, the Y component is assumed to be 1 and only
193068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * the x and y components are needed to encode them.</p>
193168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>For convenience, this implementation also allows to define the
193268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * primaries and white point in the CIE XYZ space. The tristimulus XYZ values
193368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * are internally converted to xyY.</p>
193468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
1935199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     * <p>
1936c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy     *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_srgb.png" />
1937199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     *     <figcaption style="text-align: center;">sRGB primaries and white point</figcaption>
1938199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     * </p>
1939199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     *
194068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <h3>Transfer functions</h3>
194168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>A transfer function is a color component conversion function, defined as
194268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * a single variable, monotonic mathematical function. It is applied to each
194368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * individual component of a color. They are used to perform the mapping
194468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * between linear tristimulus values and non-linear electronic signal value.</p>
194568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>The <em>opto-electronic transfer function</em> (OETF or OECF) encodes
194668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * tristimulus values in a scene to a non-linear electronic signal value.
194768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * An OETF is often expressed as a power function with an exponent between
194868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * 0.38 and 0.55 (the reciprocal of 1.8 to 2.6).</p>
194968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>The <em>electro-optical transfer function</em> (EOTF or EOCF) decodes
195068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * a non-linear electronic signal value to a tristimulus value at the display.
195168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * An EOTF is often expressed as a power function with an exponent between
195268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * 1.8 and 2.6.</p>
195368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Transfer functions are used as a compression scheme. For instance,
195468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * linear sRGB values would normally require 11 to 12 bits of precision to
195568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * store all values that can be perceived by the human eye. When encoding
195668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * sRGB values using the appropriate OETF (see {@link Named#SRGB sRGB} for
195768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * an exact mathematical description of that OETF), the values can be
195868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * compressed to only 8 bits precision.</p>
195968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>When manipulating RGB values, particularly sRGB values, it is safe
196068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * to assume that these values have been encoded with the appropriate
196168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * OETF (unless noted otherwise). Encoded values are often said to be in
196268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * "gamma space". They are therefore defined in a non-linear space. This
196368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * in turns means that any linear operation applied to these values is
196468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * going to yield mathematically incorrect results (any linear interpolation
196568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * such as gradient generation for instance, most image processing functions
196668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * such as blurs, etc.).</p>
196768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>To properly process encoded RGB values you must first apply the
196868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * EOTF to decode the value into linear space. After processing, the RGB
196968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * value must be encoded back to non-linear ("gamma") space. Here is a
197068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * formal description of the process, where \(f\) is the processing
197168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * function to apply:</p>
197268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
197368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * $$RGB_{out} = OETF(f(EOTF(RGB_{in})))$$
197468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
1975efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * <p>If the transfer functions of the color space can be expressed as an
1976efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * ICC parametric curve as defined in ICC.1:2004-10, the numeric parameters
1977efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * can be retrieved by calling {@link #getTransferParameters()}. This can
1978efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     * be useful to match color spaces for instance.</p>
1979efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy     *
198068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p class="note">Some RGB color spaces, such as {@link Named#ACES} and
198168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@link Named#LINEAR_EXTENDED_SRGB scRGB}, are said to be linear because
198268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * their transfer functions are the identity function: \(f(x) = x\).
198368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * If the source and/or destination are known to be linear, it is not
198468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * necessary to invoke the transfer functions.</p>
198568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
198668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <h3>Range</h3>
198768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Most RGB color spaces allow RGB values in the range \([0..1]\). There
198868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * are however a few RGB color spaces that allow much larger ranges. For
198968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * instance, {@link Named#EXTENDED_SRGB scRGB} is used to manipulate the
199068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * range \([-0.5..7.5]\) while {@link Named#ACES ACES} can be used throughout
199168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * the range \([-65504, 65504]\).</p>
199268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
1993199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     * <p>
1994c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy     *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_scrgb.png" />
1995199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     *     <figcaption style="text-align: center;">Extended sRGB and its large range</figcaption>
1996199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     * </p>
1997199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy     *
199868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <h3>Converting between RGB color spaces</h3>
199968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Conversion between two color spaces is achieved by using an intermediate
200068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color space called the profile connection space (PCS). The PCS used by
200168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * this implementation is CIE XYZ. The conversion operation is defined
200268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * as such:</p>
200368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
200468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * $$RGB_{out} = OETF(T_{dst}^{-1} \cdot T_{src} \cdot EOTF(RGB_{in}))$$
200568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
200668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Where \(T_{src}\) is the {@link #getTransform() RGB to XYZ transform}
200768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * of the source color space and \(T_{dst}^{-1}\) the {@link #getInverseTransform()
200868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * XYZ to RGB transform} of the destination color space.</p>
200968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Many RGB color spaces commonly used with electronic devices use the
201068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * standard illuminant {@link #ILLUMINANT_D65 D65}. Care must be take however
201168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * when converting between two RGB color spaces if their white points do not
201268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * match. This can be achieved by either calling
201368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@link #adapt(ColorSpace, float[])} to adapt one or both color spaces to
201468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * a single common white point. This can be achieved automatically by calling
201568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@link ColorSpace#connect(ColorSpace, ColorSpace)}, which also handles
201668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * non-RGB color spaces.</p>
201768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>To learn more about the white point adaptation process, refer to the
201868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * documentation of {@link Adaptation}.</p>
201968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
202077b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy    @AnyThread
202168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static class Rgb extends ColorSpace {
2022efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        /**
2023efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * {@usesMathJax}
2024efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2025efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>Defines the parameters for the ICC parametric curve type 4, as
2026efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * defined in ICC.1:2004-10, section 10.15.</p>
2027efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2028efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>The EOTF is of the form:</p>
2029efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2030efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * \(\begin{equation}
2031efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * Y = \begin{cases}c X + f & X \lt d \\
2032efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * \left( a X + b \right) ^{g} + e & X \ge d \end{cases}
2033efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * \end{equation}\)
2034efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2035efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>The corresponding OETF is simply the inverse function.</p>
2036efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2037efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>The parameters defined by this class form a valid transfer
2038efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * function only if all the following conditions are met:</p>
2039efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <ul>
2040efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>No parameter is a {@link Double#isNaN(double) Not-a-Number}</li>
2041efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>\(d\) is in the range \([0..1]\)</li>
2042efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The function is not constant</li>
2043efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The function is positive and increasing</li>
2044efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </ul>
2045efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         */
2046efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        public static class TransferParameters {
2047efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            /** Variable \(a\) in the equation of the EOTF described above. */
2048efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public final double a;
2049efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            /** Variable \(b\) in the equation of the EOTF described above. */
2050efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public final double b;
2051efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            /** Variable \(c\) in the equation of the EOTF described above. */
2052efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public final double c;
2053efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            /** Variable \(d\) in the equation of the EOTF described above. */
2054efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public final double d;
2055efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            /** Variable \(e\) in the equation of the EOTF described above. */
2056efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public final double e;
2057efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            /** Variable \(f\) in the equation of the EOTF described above. */
2058efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public final double f;
2059efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            /** Variable \(g\) in the equation of the EOTF described above. */
2060efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public final double g;
2061efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2062efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            /**
2063efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * <p>Defines the parameters for the ICC parametric curve type 3, as
2064efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * defined in ICC.1:2004-10, section 10.15.</p>
2065efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             *
2066efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * <p>The EOTF is of the form:</p>
2067efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             *
2068efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * \(\begin{equation}
2069efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * Y = \begin{cases}c X & X \lt d \\
2070efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * \left( a X + b \right) ^{g} & X \ge d \end{cases}
2071efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * \end{equation}\)
2072efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             *
2073efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * <p>This constructor is equivalent to setting  \(e\) and \(f\) to 0.</p>
2074efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             *
2075efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param a The value of \(a\) in the equation of the EOTF described above
2076efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param b The value of \(b\) in the equation of the EOTF described above
2077efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param c The value of \(c\) in the equation of the EOTF described above
2078efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param d The value of \(d\) in the equation of the EOTF described above
2079efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param g The value of \(g\) in the equation of the EOTF described above
2080efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             *
2081efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @throws IllegalArgumentException If the parameters form an invalid transfer function
2082efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             */
2083efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public TransferParameters(double a, double b, double c, double d, double g) {
2084efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                this(a, b, c, d, 0.0, 0.0, g);
2085efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            }
2086efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2087efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            /**
2088efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * <p>Defines the parameters for the ICC parametric curve type 4, as
2089efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * defined in ICC.1:2004-10, section 10.15.</p>
2090efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             *
2091efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param a The value of \(a\) in the equation of the EOTF described above
2092efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param b The value of \(b\) in the equation of the EOTF described above
2093efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param c The value of \(c\) in the equation of the EOTF described above
2094efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param d The value of \(d\) in the equation of the EOTF described above
2095efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param e The value of \(e\) in the equation of the EOTF described above
2096efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param f The value of \(f\) in the equation of the EOTF described above
2097efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @param g The value of \(g\) in the equation of the EOTF described above
2098efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             *
2099efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             * @throws IllegalArgumentException If the parameters form an invalid transfer function
2100efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy             */
2101efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public TransferParameters(double a, double b, double c, double d, double e,
2102efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    double f, double g) {
2103efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2104efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (Double.isNaN(a) || Double.isNaN(b) || Double.isNaN(c) ||
2105efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                        Double.isNaN(d) || Double.isNaN(e) || Double.isNaN(f) ||
2106efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                        Double.isNaN(g)) {
2107efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    throw new IllegalArgumentException("Parameters cannot be NaN");
2108efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                }
2109efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2110bfa58aab0a7723f2757f7919f37a520f469b7b04Romain Guy                // Next representable float after 1.0
2111bfa58aab0a7723f2757f7919f37a520f469b7b04Romain Guy                // We use doubles here but the representation inside our native code is often floats
2112bfa58aab0a7723f2757f7919f37a520f469b7b04Romain Guy                if (!(d >= 0.0 && d <= 1.0f + Math.ulp(1.0f))) {
2113bfa58aab0a7723f2757f7919f37a520f469b7b04Romain Guy                    throw new IllegalArgumentException("Parameter d must be in the range [0..1], " +
2114bfa58aab0a7723f2757f7919f37a520f469b7b04Romain Guy                            "was " + d);
2115efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                }
2116efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2117efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (d == 0.0 && (a == 0.0 || g == 0.0)) {
2118efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    throw new IllegalArgumentException(
2119efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                            "Parameter a or g is zero, the transfer function is constant");
2120efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                }
2121efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2122efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (d >= 1.0 && c == 0.0) {
2123efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    throw new IllegalArgumentException(
2124efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                            "Parameter c is zero, the transfer function is constant");
2125efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                }
2126efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2127efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if ((a == 0.0 || g == 0.0) && c == 0.0) {
2128efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    throw new IllegalArgumentException("Parameter a or g is zero," +
2129efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                            " and c is zero, the transfer function is constant");
2130efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                }
2131efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2132efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (c < 0.0) {
2133efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    throw new IllegalArgumentException("The transfer function must be increasing");
2134efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                }
2135efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2136efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (a < 0.0 || g < 0.0) {
2137efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    throw new IllegalArgumentException("The transfer function must be " +
2138efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                            "positive or increasing");
2139efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                }
2140efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2141efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                this.a = a;
2142efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                this.b = b;
2143efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                this.c = c;
2144efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                this.d = d;
2145efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                this.e = e;
2146efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                this.f = f;
2147efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                this.g = g;
2148efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            }
2149efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2150efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            @SuppressWarnings("SimplifiableIfStatement")
2151efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            @Override
2152efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public boolean equals(Object o) {
2153efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (this == o) return true;
2154efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (o == null || getClass() != o.getClass()) return false;
2155efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2156efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                TransferParameters that = (TransferParameters) o;
2157efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2158efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (Double.compare(that.a, a) != 0) return false;
2159efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (Double.compare(that.b, b) != 0) return false;
2160efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (Double.compare(that.c, c) != 0) return false;
2161efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (Double.compare(that.d, d) != 0) return false;
2162efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (Double.compare(that.e, e) != 0) return false;
2163efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                if (Double.compare(that.f, f) != 0) return false;
2164efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                return Double.compare(that.g, g) == 0;
2165efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            }
2166efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2167efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            @Override
2168efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            public int hashCode() {
2169efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                int result;
2170efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                long temp;
2171efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                temp = Double.doubleToLongBits(a);
2172efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                result = (int) (temp ^ (temp >>> 32));
2173efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                temp = Double.doubleToLongBits(b);
2174efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                result = 31 * result + (int) (temp ^ (temp >>> 32));
2175efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                temp = Double.doubleToLongBits(c);
2176efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                result = 31 * result + (int) (temp ^ (temp >>> 32));
2177efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                temp = Double.doubleToLongBits(d);
2178efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                result = 31 * result + (int) (temp ^ (temp >>> 32));
2179efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                temp = Double.doubleToLongBits(e);
2180efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                result = 31 * result + (int) (temp ^ (temp >>> 32));
2181efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                temp = Double.doubleToLongBits(f);
2182efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                result = 31 * result + (int) (temp ^ (temp >>> 32));
2183efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                temp = Double.doubleToLongBits(g);
2184efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                result = 31 * result + (int) (temp ^ (temp >>> 32));
2185efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                return result;
2186efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            }
2187efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        }
2188efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
218968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final float[] mWhitePoint;
219068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final float[] mPrimaries;
219168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final float[] mTransform;
219268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final float[] mInverseTransform;
219368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
219468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final DoubleUnaryOperator mOetf;
219568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final DoubleUnaryOperator mEotf;
219668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final DoubleUnaryOperator mClampedOetf;
219768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final DoubleUnaryOperator mClampedEotf;
219868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
219968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private final float mMin;
220068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private final float mMax;
220168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
2202efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        private final boolean mIsWideGamut;
2203efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        private final boolean mIsSrgb;
2204efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2205efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        @Nullable private TransferParameters mTransferParameters;
2206efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
220768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
220868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Creates a new RGB color space using a 3x3 column-major transform matrix.
220968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The transform matrix must convert from the RGB space to the profile connection
221068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * space CIE XYZ.</p>
221168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
221268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p class="note">The range of the color space is imposed to be \([0..1]\).</p>
221368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
221468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param name Name of the color space, cannot be null, its length must be >= 1
221568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param toXYZ 3x3 column-major transform matrix from RGB to the profile
221668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *              connection space CIE XYZ as an array of 9 floats, cannot be null
221768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param oetf Opto-electronic transfer function, cannot be null
221868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param eotf Electro-optical transfer function, cannot be null
221968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
222068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @throws IllegalArgumentException If any of the following conditions is met:
222168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <ul>
222268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The name is null or has a length of 0.</li>
222368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The OETF is null or the EOTF is null.</li>
222468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The minimum valid value is >= the maximum valid value.</li>
222568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </ul>
222668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
222768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #get(Named)
222868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
222968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public Rgb(
223068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(min = 1) String name,
223168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(9) float[] toXYZ,
223268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull DoubleUnaryOperator oetf,
223368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull DoubleUnaryOperator eotf) {
2234efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ),
223568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    oetf, eotf, 0.0f, 1.0f, MIN_ID);
223668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
223768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
223868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
223968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Creates a new RGB color space using a specified set of primaries
224068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * and a specified white point.</p>
224168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
224268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>The primaries and white point can be specified in the CIE xyY space
224368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
224468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
224568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Parameters length">
224668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
224768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>xyY</td><td>6</td><td>2</td></tr>
224868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>XYZ</td><td>9</td><td>3</td></tr>
224968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
225068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
225168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>When the primaries and/or white point are specified in xyY, the Y component
225268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * does not need to be specified and is assumed to be 1.0. Only the xy components
225368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * are required.</p>
225468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
225568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p class="note">The ID, areturned by {@link #getId()}, of an object created by
225668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * this constructor is always {@link #MIN_ID}.</p>
225768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
225868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param name Name of the color space, cannot be null, its length must be >= 1
225968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
226068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
226168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param oetf Opto-electronic transfer function, cannot be null
226268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param eotf Electro-optical transfer function, cannot be null
226368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param min The minimum valid value in this color space's RGB range
226468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param max The maximum valid value in this color space's RGB range
226568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
226668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @throws IllegalArgumentException <p>If any of the following conditions is met:</p>
226768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <ul>
226868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The name is null or has a length of 0.</li>
226968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The primaries array is null or has a length that is neither 6 or 9.</li>
227068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The white point array is null or has a length that is neither 2 or 3.</li>
227168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The OETF is null or the EOTF is null.</li>
227268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The minimum valid value is >= the maximum valid value.</li>
227368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </ul>
227468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
227568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #get(Named)
227668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
227768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public Rgb(
227868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(min = 1) String name,
227968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(min = 6, max = 9) float[] primaries,
228068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(min = 2, max = 3) float[] whitePoint,
228168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull DoubleUnaryOperator oetf,
228268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull DoubleUnaryOperator eotf,
228368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float min,
228468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float max) {
228568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            this(name, primaries, whitePoint, oetf, eotf, min, max, MIN_ID);
228668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
228768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
228868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
2289efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>Creates a new RGB color space using a 3x3 column-major transform matrix.
2290efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * The transform matrix must convert from the RGB space to the profile connection
2291efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * space CIE XYZ.</p>
2292efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2293efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p class="note">The range of the color space is imposed to be \([0..1]\).</p>
2294efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2295efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param name Name of the color space, cannot be null, its length must be >= 1
2296efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param toXYZ 3x3 column-major transform matrix from RGB to the profile
2297efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *              connection space CIE XYZ as an array of 9 floats, cannot be null
2298efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param function Parameters for the transfer functions
2299efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2300efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @throws IllegalArgumentException If any of the following conditions is met:
2301efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <ul>
2302efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The name is null or has a length of 0.</li>
2303efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>Gamma is negative.</li>
2304efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </ul>
2305efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2306efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @see #get(Named)
2307efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         */
2308efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        public Rgb(
2309efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 1) String name,
2310efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(9) float[] toXYZ,
2311efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull TransferParameters function) {
2312efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), function, MIN_ID);
2313efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        }
2314efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2315efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        /**
2316efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>Creates a new RGB color space using a specified set of primaries
2317efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * and a specified white point.</p>
2318efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2319efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>The primaries and white point can be specified in the CIE xyY space
2320efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
2321efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2322efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <table summary="Parameters length">
2323efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
2324efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><td>xyY</td><td>6</td><td>2</td></tr>
2325efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><td>XYZ</td><td>9</td><td>3</td></tr>
2326efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </table>
2327efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2328efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>When the primaries and/or white point are specified in xyY, the Y component
2329efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * does not need to be specified and is assumed to be 1.0. Only the xy components
2330efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * are required.</p>
2331efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2332efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param name Name of the color space, cannot be null, its length must be >= 1
2333efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
2334efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
2335efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param function Parameters for the transfer functions
2336efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2337efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @throws IllegalArgumentException If any of the following conditions is met:
2338efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <ul>
2339efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The name is null or has a length of 0.</li>
2340efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The primaries array is null or has a length that is neither 6 or 9.</li>
2341efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The white point array is null or has a length that is neither 2 or 3.</li>
2342efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The transfer parameters are invalid.</li>
2343efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </ul>
2344efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2345efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @see #get(Named)
2346efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         */
2347efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        public Rgb(
2348efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 1) String name,
2349efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 6, max = 9) float[] primaries,
2350efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 2, max = 3) float[] whitePoint,
2351efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull TransferParameters function) {
2352efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            this(name, primaries, whitePoint, function, MIN_ID);
2353efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        }
2354efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2355efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        /**
2356efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>Creates a new RGB color space using a specified set of primaries
2357efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * and a specified white point.</p>
2358efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2359efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>The primaries and white point can be specified in the CIE xyY space
2360efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
2361efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2362efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <table summary="Parameters length">
2363efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
2364efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><td>xyY</td><td>6</td><td>2</td></tr>
2365efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><td>XYZ</td><td>9</td><td>3</td></tr>
2366efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </table>
2367efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2368efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>When the primaries and/or white point are specified in xyY, the Y component
2369efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * does not need to be specified and is assumed to be 1.0. Only the xy components
2370efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * are required.</p>
2371efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2372efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param name Name of the color space, cannot be null, its length must be >= 1
2373efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
2374efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
2375efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param function Parameters for the transfer functions
2376efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param id ID of this color space as an integer between {@link #MIN_ID} and {@link #MAX_ID}
2377efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2378efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @throws IllegalArgumentException If any of the following conditions is met:
2379efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <ul>
2380efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The name is null or has a length of 0.</li>
2381efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The primaries array is null or has a length that is neither 6 or 9.</li>
2382efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The white point array is null or has a length that is neither 2 or 3.</li>
2383efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The ID is not between {@link #MIN_ID} and {@link #MAX_ID}.</li>
2384efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The transfer parameters are invalid.</li>
2385efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </ul>
2386efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2387efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @see #get(Named)
2388efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         */
2389efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        private Rgb(
2390efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 1) String name,
2391efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 6, max = 9) float[] primaries,
2392efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 2, max = 3) float[] whitePoint,
2393efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull TransferParameters function,
2394efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @IntRange(from = MIN_ID, to = MAX_ID) int id) {
2395efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            this(name, primaries, whitePoint,
2396efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    function.e == 0.0 && function.f == 0.0 ?
2397efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                            x -> rcpResponse(x, function.a, function.b,
2398efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                                    function.c, function.d, function.g) :
2399efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                            x -> rcpResponse(x, function.a, function.b, function.c,
2400efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                                    function.d, function.e, function.f, function.g),
2401efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    function.e == 0.0 && function.f == 0.0 ?
2402efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                            x -> response(x, function.a, function.b,
2403efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                                    function.c, function.d, function.g) :
2404efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                            x -> response(x, function.a, function.b, function.c,
2405efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                                    function.d, function.e, function.f, function.g),
2406efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    0.0f, 1.0f, id);
2407efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            mTransferParameters = function;
2408efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        }
2409efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2410efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        /**
2411efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>Creates a new RGB color space using a 3x3 column-major transform matrix.
2412efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * The transform matrix must convert from the RGB space to the profile connection
2413efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * space CIE XYZ.</p>
2414efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2415efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p class="note">The range of the color space is imposed to be \([0..1]\).</p>
2416efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2417efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param name Name of the color space, cannot be null, its length must be >= 1
2418efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param toXYZ 3x3 column-major transform matrix from RGB to the profile
2419efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *              connection space CIE XYZ as an array of 9 floats, cannot be null
2420efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param gamma Gamma to use as the transfer function
2421efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2422efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @throws IllegalArgumentException If any of the following conditions is met:
2423efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <ul>
2424efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The name is null or has a length of 0.</li>
2425efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>Gamma is negative.</li>
2426efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </ul>
2427efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2428efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @see #get(Named)
2429efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         */
2430efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        public Rgb(
2431efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 1) String name,
2432efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(9) float[] toXYZ,
2433efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                double gamma) {
2434efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), gamma, 0.0f, 1.0f, MIN_ID);
2435efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        }
2436efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2437efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        /**
2438efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>Creates a new RGB color space using a specified set of primaries
2439efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * and a specified white point.</p>
2440efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2441efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>The primaries and white point can be specified in the CIE xyY space
2442efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
2443efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2444efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <table summary="Parameters length">
2445efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
2446efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><td>xyY</td><td>6</td><td>2</td></tr>
2447efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><td>XYZ</td><td>9</td><td>3</td></tr>
2448efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </table>
2449efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2450efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>When the primaries and/or white point are specified in xyY, the Y component
2451efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * does not need to be specified and is assumed to be 1.0. Only the xy components
2452efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * are required.</p>
2453efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2454efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param name Name of the color space, cannot be null, its length must be >= 1
2455efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
2456efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
2457efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param gamma Gamma to use as the transfer function
2458efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2459efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @throws IllegalArgumentException If any of the following conditions is met:
2460efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <ul>
2461efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The name is null or has a length of 0.</li>
2462efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The primaries array is null or has a length that is neither 6 or 9.</li>
2463efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The white point array is null or has a length that is neither 2 or 3.</li>
2464efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>Gamma is negative.</li>
2465efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </ul>
2466efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2467efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @see #get(Named)
2468efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         */
2469efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        public Rgb(
2470efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 1) String name,
2471efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 6, max = 9) float[] primaries,
2472efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 2, max = 3) float[] whitePoint,
2473efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                double gamma) {
2474efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            this(name, primaries, whitePoint, gamma, 0.0f, 1.0f, MIN_ID);
2475efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        }
2476efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2477efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        /**
2478efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>Creates a new RGB color space using a specified set of primaries
2479efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * and a specified white point.</p>
2480efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2481efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>The primaries and white point can be specified in the CIE xyY space
2482efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
2483efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2484efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <table summary="Parameters length">
2485efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
2486efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><td>xyY</td><td>6</td><td>2</td></tr>
2487efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <tr><td>XYZ</td><td>9</td><td>3</td></tr>
2488efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </table>
2489efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2490efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>When the primaries and/or white point are specified in xyY, the Y component
2491efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * does not need to be specified and is assumed to be 1.0. Only the xy components
2492efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * are required.</p>
2493efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2494efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param name Name of the color space, cannot be null, its length must be >= 1
2495efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
2496efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
2497efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param gamma Gamma to use as the transfer function
2498efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param min The minimum valid value in this color space's RGB range
2499efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param max The maximum valid value in this color space's RGB range
2500efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @param id ID of this color space as an integer between {@link #MIN_ID} and {@link #MAX_ID}
2501efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2502efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @throws IllegalArgumentException If any of the following conditions is met:
2503efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <ul>
2504efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The name is null or has a length of 0.</li>
2505efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The primaries array is null or has a length that is neither 6 or 9.</li>
2506efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The white point array is null or has a length that is neither 2 or 3.</li>
2507efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The minimum valid value is >= the maximum valid value.</li>
2508efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>The ID is not between {@link #MIN_ID} and {@link #MAX_ID}.</li>
2509efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *     <li>Gamma is negative.</li>
2510efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * </ul>
2511efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2512efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @see #get(Named)
2513efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         */
2514efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        private Rgb(
2515efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 1) String name,
2516efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 6, max = 9) float[] primaries,
2517efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @NonNull @Size(min = 2, max = 3) float[] whitePoint,
2518efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                double gamma,
2519efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                float min,
2520efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                float max,
2521efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                @IntRange(from = MIN_ID, to = MAX_ID) int id) {
2522efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            this(name, primaries, whitePoint,
2523efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    gamma == 1.0 ? DoubleUnaryOperator.identity() :
2524efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                            x -> Math.pow(x < 0.0 ? 0.0 : x, 1 / gamma),
2525efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    gamma == 1.0 ? DoubleUnaryOperator.identity() :
2526efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                            x -> Math.pow(x < 0.0 ? 0.0 : x, gamma),
2527efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    min, max, id);
2528efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            mTransferParameters = gamma == 1.0 ?
2529bfa58aab0a7723f2757f7919f37a520f469b7b04Romain Guy                    new TransferParameters(0.0, 0.0, 1.0, 1.0 + Math.ulp(1.0f), gamma) :
2530efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma);
2531efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        }
2532efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2533efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        /**
253468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Creates a new RGB color space using a specified set of primaries
253568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * and a specified white point.</p>
253668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
253768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>The primaries and white point can be specified in the CIE xyY space
253868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
253968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
254068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <table summary="Parameters length">
254168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
254268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>xyY</td><td>6</td><td>2</td></tr>
254368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <tr><td>XYZ</td><td>9</td><td>3</td></tr>
254468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </table>
254568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
254668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>When the primaries and/or white point are specified in xyY, the Y component
254768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * does not need to be specified and is assumed to be 1.0. Only the xy components
254868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * are required.</p>
254968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
255068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param name Name of the color space, cannot be null, its length must be >= 1
255168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
255268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
255368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param oetf Opto-electronic transfer function, cannot be null
255468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param eotf Electro-optical transfer function, cannot be null
255568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param min The minimum valid value in this color space's RGB range
255668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param max The maximum valid value in this color space's RGB range
255768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param id ID of this color space as an integer between {@link #MIN_ID} and {@link #MAX_ID}
255868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
255968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @throws IllegalArgumentException If any of the following conditions is met:
256068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <ul>
256168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The name is null or has a length of 0.</li>
256268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The primaries array is null or has a length that is neither 6 or 9.</li>
256368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The white point array is null or has a length that is neither 2 or 3.</li>
256468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The OETF is null or the EOTF is null.</li>
256568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The minimum valid value is >= the maximum valid value.</li>
256668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     <li>The ID is not between {@link #MIN_ID} and {@link #MAX_ID}.</li>
256768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * </ul>
256868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
256968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #get(Named)
257068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
257168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private Rgb(
257268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(min = 1) String name,
257368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(min = 6, max = 9) float[] primaries,
257468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(min = 2, max = 3) float[] whitePoint,
257568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull DoubleUnaryOperator oetf,
257668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull DoubleUnaryOperator eotf,
257768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float min,
257868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float max,
257968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @IntRange(from = MIN_ID, to = MAX_ID) int id) {
258068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
258168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            super(name, Model.RGB, id);
258268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
258368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (primaries == null || (primaries.length != 6 && primaries.length != 9)) {
258468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                throw new IllegalArgumentException("The color space's primaries must be " +
258568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        "defined as an array of 6 floats in xyY or 9 floats in XYZ");
258668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
258768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
258868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (whitePoint == null || (whitePoint.length != 2 && whitePoint.length != 3)) {
258968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                throw new IllegalArgumentException("The color space's white point must be " +
259068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        "defined as an array of 2 floats in xyY or 3 float in XYZ");
259168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
259268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
259368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (oetf == null || eotf == null) {
259468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                throw new IllegalArgumentException("The transfer functions of a color space " +
259568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        "cannot be null");
259668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
259768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
259868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (min >= max) {
259968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                throw new IllegalArgumentException("Invalid range: min=" + min + ", max=" + max +
260068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        "; min must be strictly < max");
260168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
260268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
260368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mWhitePoint = xyWhitePoint(whitePoint);
260468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mPrimaries =  xyPrimaries(primaries);
260568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
260668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mTransform = computeXYZMatrix(mPrimaries, mWhitePoint);
260768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mInverseTransform = inverse3x3(mTransform);
260868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
260968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mOetf = oetf;
261068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mEotf = eotf;
261168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
261268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mMin = min;
261368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mMax = max;
261468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
261568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            DoubleUnaryOperator clamp = this::clamp;
261668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mClampedOetf = oetf.andThen(clamp);
261768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mClampedEotf = clamp.andThen(eotf);
261868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
261968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            // A color space is wide-gamut if its area is >90% of NTSC 1953 and
262068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            // if it entirely contains the Color space definition in xyY
2621199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy            mIsWideGamut = isWideGamut(mPrimaries, min, max);
2622199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy            mIsSrgb = isSrgb(mPrimaries, mWhitePoint, oetf, eotf, min, max, id);
262368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
262468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
262568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
262668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Creates a copy of the specified color space with a new transform.
262768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
262868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param colorSpace The color space to create a copy of
262968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
263068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private Rgb(Rgb colorSpace,
263168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(9) float[] transform,
263268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(min = 2, max = 3) float[] whitePoint) {
263368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            super(colorSpace.getName(), Model.RGB, -1);
263468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
263568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mWhitePoint = xyWhitePoint(whitePoint);
263668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mPrimaries = colorSpace.mPrimaries;
263768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
263868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mTransform = transform;
263968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mInverseTransform = inverse3x3(transform);
264068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
264168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mMin = colorSpace.mMin;
264268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mMax = colorSpace.mMax;
264368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
264468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mOetf = colorSpace.mOetf;
264568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mEotf = colorSpace.mEotf;
264668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
264768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mClampedOetf = colorSpace.mClampedOetf;
264868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mClampedEotf = colorSpace.mClampedEotf;
264968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
265068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mIsWideGamut = colorSpace.mIsWideGamut;
265168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mIsSrgb = colorSpace.mIsSrgb;
2652efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
2653efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            mTransferParameters = colorSpace.mTransferParameters;
265468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
265568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
265668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
265768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Copies the non-adapted CIE xyY white point of this color space in
265868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * specified array. The Y component is assumed to be 1 and is therefore
265968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * not copied into the destination. The x and y components are written
266068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * in the array at positions 0 and 1 respectively.
266168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
266268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param whitePoint The destination array, cannot be null, its length
266368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                   must be >= 2
266468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
266568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return The destination array passed as a parameter
266668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
266768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getWhitePoint(float[])
266868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
266968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
267068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(min = 2)
267168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] getWhitePoint(@NonNull @Size(min = 2) float[] whitePoint) {
267268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            whitePoint[0] = mWhitePoint[0];
267368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            whitePoint[1] = mWhitePoint[1];
267468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return whitePoint;
267568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
267668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
267768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
267868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Returns the non-adapted CIE xyY white point of this color space as
267968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * a new array of 2 floats. The Y component is assumed to be 1 and is
268068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * therefore not copied into the destination. The x and y components
268168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * are written in the array at positions 0 and 1 respectively.
268268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
268368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new non-null array of 2 floats
268468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
268568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getWhitePoint()
268668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
268768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
268868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(2)
268968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] getWhitePoint() {
269068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return Arrays.copyOf(mWhitePoint, mWhitePoint.length);
269168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
269268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
269368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
269468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Copies the primaries of this color space in specified array. The Y
269568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * component is assumed to be 1 and is therefore not copied into the
269668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * destination. The x and y components of the first primary are written
269768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * in the array at positions 0 and 1 respectively.
269868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
269968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param primaries The destination array, cannot be null, its length
270068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                  must be >= 6
270168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
270268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return The destination array passed as a parameter
270368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
270468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getPrimaries(float[])
270568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
270668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
270768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(min = 6)
270868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] getPrimaries(@NonNull @Size(min = 6) float[] primaries) {
270968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            System.arraycopy(mPrimaries, 0, primaries, 0, mPrimaries.length);
271068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return primaries;
271168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
271268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
271368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
271468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Returns the primaries of this color space as a new array of 6 floats.
271568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The Y component is assumed to be 1 and is therefore not copied into
271668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * the destination. The x and y components of the first primary are
271768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * written in the array at positions 0 and 1 respectively.
271868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
271968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new non-null array of 2 floats
272068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
272168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getWhitePoint()
272268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
272368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
272468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(6)
272568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] getPrimaries() {
272668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return Arrays.copyOf(mPrimaries, mPrimaries.length);
272768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
272868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
272968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
273068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Copies the transform of this color space in specified array. The
273168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * transform is used to convert from RGB to XYZ (with the same white
273268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * point as this color space). To connect color spaces, you must first
273368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * {@link ColorSpace#adapt(ColorSpace, float[]) adapt} them to the
273468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * same white point.</p>
273568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>It is recommended to use {@link ColorSpace#connect(ColorSpace, ColorSpace)}
273668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to convert between color spaces.</p>
273768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
273868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param transform The destination array, cannot be null, its length
273968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                  must be >= 9
274068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
274168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return The destination array passed as a parameter
274268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
274368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getInverseTransform()
274468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
274568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
274668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(min = 9)
274768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] getTransform(@NonNull @Size(min = 9) float[] transform) {
274868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            System.arraycopy(mTransform, 0, transform, 0, mTransform.length);
274968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return transform;
275068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
275168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
275268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
275368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Returns the transform of this color space as a new array. The
275468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * transform is used to convert from RGB to XYZ (with the same white
275568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * point as this color space). To connect color spaces, you must first
275668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * {@link ColorSpace#adapt(ColorSpace, float[]) adapt} them to the
275768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * same white point.</p>
275868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>It is recommended to use {@link ColorSpace#connect(ColorSpace, ColorSpace)}
275968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to convert between color spaces.</p>
276068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
276168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new array of 9 floats
276268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
276368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getInverseTransform(float[])
276468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
276568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
276668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(9)
276768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] getTransform() {
276868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return Arrays.copyOf(mTransform, mTransform.length);
276968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
277068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
277168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
277268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Copies the inverse transform of this color space in specified array.
277368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The inverse transform is used to convert from XYZ to RGB (with the
277468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * same white point as this color space). To connect color spaces, you
277568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * must first {@link ColorSpace#adapt(ColorSpace, float[]) adapt} them
277668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to the same white point.</p>
277768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>It is recommended to use {@link ColorSpace#connect(ColorSpace, ColorSpace)}
277868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to convert between color spaces.</p>
277968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
278068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param inverseTransform The destination array, cannot be null, its length
278168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                  must be >= 9
278268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
278368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return The destination array passed as a parameter
278468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
278568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getTransform()
278668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
278768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
278868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(min = 9)
278968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] getInverseTransform(@NonNull @Size(min = 9) float[] inverseTransform) {
279068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            System.arraycopy(mInverseTransform, 0, inverseTransform, 0, mInverseTransform.length);
279168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return inverseTransform;
279268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
279368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
279468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
279568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Returns the inverse transform of this color space as a new array.
279668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The inverse transform is used to convert from XYZ to RGB (with the
279768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * same white point as this color space). To connect color spaces, you
279868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * must first {@link ColorSpace#adapt(ColorSpace, float[]) adapt} them
279968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to the same white point.</p>
280068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>It is recommended to use {@link ColorSpace#connect(ColorSpace, ColorSpace)}
280168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to convert between color spaces.</p>
280268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
280368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new array of 9 floats
280468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
280568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getTransform(float[])
280668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
280768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
280868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(9)
280968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] getInverseTransform() {
281068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return Arrays.copyOf(mInverseTransform, mInverseTransform.length);
281168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
281268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
281368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
281468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Returns the opto-electronic transfer function (OETF) of this color space.
281568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The inverse function is the electro-optical transfer function (EOTF) returned
281668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * by {@link #getEotf()}. These functions are defined to satisfy the following
281768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * equality for \(x \in [0..1]\):</p>
281868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
281968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * $$OETF(EOTF(x)) = EOTF(OETF(x)) = x$$
282068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
282168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>For RGB colors, this function can be used to convert from linear space
282268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to "gamma space" (gamma encoded). The terms gamma space and gamma encoded
282368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * are frequently used because many OETFs can be closely approximated using
282468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * a simple power function of the form \(x^{\frac{1}{\gamma}}\) (the
282577b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy         * approximation of the {@link Named#SRGB sRGB} OETF uses \(\gamma=2.2\)
282668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * for instance).</p>
282768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
282868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A transfer function that converts from linear space to "gamma space"
282968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
283068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getEotf()
2831efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @see #getTransferParameters()
283268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
283368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
283468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public DoubleUnaryOperator getOetf() {
283577b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy            return mClampedOetf;
283668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
283768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
283868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
283968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Returns the electro-optical transfer function (EOTF) of this color space.
284068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The inverse function is the opto-electronic transfer function (OETF)
284168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * returned by {@link #getOetf()}. These functions are defined to satisfy the
284268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * following equality for \(x \in [0..1]\):</p>
284368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
284468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * $$OETF(EOTF(x)) = EOTF(OETF(x)) = x$$
284568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
284668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>For RGB colors, this function can be used to convert from "gamma space"
284768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * (gamma encoded) to linear space. The terms gamma space and gamma encoded
284868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * are frequently used because many EOTFs can be closely approximated using
284968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * a simple power function of the form \(x^\gamma\) (the approximation of the
285068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * {@link Named#SRGB sRGB} EOTF uses \(\gamma=2.2\) for instance).</p>
285168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
285268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A transfer function that converts from "gamma space" to linear space
285368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
285468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getOetf()
2855efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @see #getTransferParameters()
285668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
285768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
285868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public DoubleUnaryOperator getEotf() {
285977b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy            return mClampedEotf;
286068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
286168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
2862efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        /**
2863efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>Returns the parameters used by the {@link #getEotf() electro-optical}
2864efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * and {@link #getOetf() opto-electronic} transfer functions. If the transfer
2865efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * functions do not match the ICC parametric curves defined in ICC.1:2004-10
2866efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * (section 10.15), this method returns null.</p>
2867efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2868efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * <p>See {@link TransferParameters} for a full description of the transfer
2869efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * functions.</p>
2870efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *
2871efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         * @return An instance of {@link TransferParameters} or null if this color
2872efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         space's transfer functions do not match the equation defined in
2873efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         *         {@link TransferParameters}
2874efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy         */
2875efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        @Nullable
2876efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        public TransferParameters getTransferParameters() {
2877efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            return mTransferParameters;
2878efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        }
2879efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy
288068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
288168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public boolean isSrgb() {
288268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mIsSrgb;
288368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
288468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
288568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
288668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public boolean isWideGamut() {
288768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mIsWideGamut;
288868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
288968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
289068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
289168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float getMinValue(int component) {
289268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mMin;
289368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
289468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
289568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
289668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float getMaxValue(int component) {
289768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mMax;
289868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
289968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
290068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
290168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Decodes an RGB value to linear space. This is achieved by
290268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * applying this color space's electro-optical transfer function
290368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to the supplied values.</p>
290468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
290568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Refer to the documentation of {@link ColorSpace.Rgb} for
290668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * more information about transfer functions and their use for
290768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * encoding and decoding RGB values.</p>
290868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
290968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param r The red component to decode to linear space
291068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param g The green component to decode to linear space
291168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param b The blue component to decode to linear space
291268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new array of 3 floats containing linear RGB values
291368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
291468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #toLinear(float[])
291568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #fromLinear(float, float, float)
291668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
291768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
291868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(3)
291968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] toLinear(float r, float g, float b) {
292068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return toLinear(new float[] { r, g, b });
292168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
292268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
292368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
292468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Decodes an RGB value to linear space. This is achieved by
292568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * applying this color space's electro-optical transfer function
292668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to the first 3 values of the supplied array. The result is
292768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * stored back in the input array.</p>
292868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
292968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Refer to the documentation of {@link ColorSpace.Rgb} for
293068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * more information about transfer functions and their use for
293168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * encoding and decoding RGB values.</p>
293268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
293368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param v A non-null array of non-linear RGB values, its length
293468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *          must be at least 3
293568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return The specified array
293668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
293768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #toLinear(float, float, float)
293868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #fromLinear(float[])
293968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
294068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
294168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(min = 3)
294268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] toLinear(@NonNull @Size(min = 3) float[] v) {
294368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[0] = (float) mClampedEotf.applyAsDouble(v[0]);
294468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[1] = (float) mClampedEotf.applyAsDouble(v[1]);
294568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[2] = (float) mClampedEotf.applyAsDouble(v[2]);
294668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return v;
294768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
294868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
294968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
295068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Encodes an RGB value from linear space to this color space's
295168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * "gamma space". This is achieved by applying this color space's
295268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * opto-electronic transfer function to the supplied values.</p>
295368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
295468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Refer to the documentation of {@link ColorSpace.Rgb} for
295568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * more information about transfer functions and their use for
295668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * encoding and decoding RGB values.</p>
295768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
295868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param r The red component to encode from linear space
295968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param g The green component to encode from linear space
296068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param b The blue component to encode from linear space
296168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new array of 3 floats containing non-linear RGB values
296268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
296368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #fromLinear(float[])
296468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #toLinear(float, float, float)
296568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
296668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
296768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(3)
296868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] fromLinear(float r, float g, float b) {
296968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return fromLinear(new float[] { r, g, b });
297068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
297168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
297268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
297368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Encodes an RGB value from linear space to this color space's
297468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * "gamma space". This is achieved by applying this color space's
297568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * opto-electronic transfer function to the first 3 values of the
297668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * supplied array. The result is stored back in the input array.</p>
297768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
297868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Refer to the documentation of {@link ColorSpace.Rgb} for
297968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * more information about transfer functions and their use for
298068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * encoding and decoding RGB values.</p>
298168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
298268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param v A non-null array of linear RGB values, its length
298368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *          must be at least 3
298468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new array of 3 floats containing non-linear RGB values
298568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
298668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #fromLinear(float[])
298768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #toLinear(float, float, float)
298868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
298968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
299068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(min = 3)
299168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] fromLinear(@NonNull @Size(min = 3) float[] v) {
299268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[0] = (float) mClampedOetf.applyAsDouble(v[0]);
299368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[1] = (float) mClampedOetf.applyAsDouble(v[1]);
299468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[2] = (float) mClampedOetf.applyAsDouble(v[2]);
299568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return v;
299668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
299768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
299868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
299968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
300068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(min = 3)
300168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] toXyz(@NonNull @Size(min = 3) float[] v) {
300268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[0] = (float) mClampedEotf.applyAsDouble(v[0]);
300368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[1] = (float) mClampedEotf.applyAsDouble(v[1]);
300468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[2] = (float) mClampedEotf.applyAsDouble(v[2]);
300568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mul3x3Float3(mTransform, v);
300668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
300768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
300868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
300968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
301068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(min = 3)
301168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] fromXyz(@NonNull @Size(min = 3) float[] v) {
301268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mul3x3Float3(mInverseTransform, v);
301368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[0] = (float) mClampedOetf.applyAsDouble(v[0]);
301468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[1] = (float) mClampedOetf.applyAsDouble(v[1]);
301568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            v[2] = (float) mClampedOetf.applyAsDouble(v[2]);
301668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return v;
301768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
301868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
301968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private double clamp(double x) {
302068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return x < mMin ? mMin : x > mMax ? mMax : x;
302168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
302268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
302368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
302468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public boolean equals(Object o) {
302568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (this == o) return true;
302668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (o == null || getClass() != o.getClass()) return false;
302768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (!super.equals(o)) return false;
302868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
302968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            Rgb rgb = (Rgb) o;
303068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
303168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (Float.compare(rgb.mMin, mMin) != 0) return false;
303268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (Float.compare(rgb.mMax, mMax) != 0) return false;
303368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (!Arrays.equals(mWhitePoint, rgb.mWhitePoint)) return false;
303468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (!Arrays.equals(mPrimaries, rgb.mPrimaries)) return false;
3035efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            if (mTransferParameters != null) {
3036efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                return mTransferParameters.equals(rgb.mTransferParameters);
3037efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            } else if (rgb.mTransferParameters == null) {
3038efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                return true;
3039efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            }
304068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            //noinspection SimplifiableIfStatement
304168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (!mOetf.equals(rgb.mOetf)) return false;
304268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mEotf.equals(rgb.mEotf);
304368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
304468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
304568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Override
304668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public int hashCode() {
304768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            int result = super.hashCode();
304868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            result = 31 * result + Arrays.hashCode(mWhitePoint);
304968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            result = 31 * result + Arrays.hashCode(mPrimaries);
305068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            result = 31 * result + (mMin != +0.0f ? Float.floatToIntBits(mMin) : 0);
305168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            result = 31 * result + (mMax != +0.0f ? Float.floatToIntBits(mMax) : 0);
3052efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            result = 31 * result +
3053efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                    (mTransferParameters != null ? mTransferParameters.hashCode() : 0);
3054efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            if (mTransferParameters == null) {
3055efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                result = 31 * result + mOetf.hashCode();
3056efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy                result = 31 * result + mEotf.hashCode();
3057efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            }
305868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return result;
305968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
306068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
306168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
306268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Computes whether a color space is the sRGB color space or at least
306368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * a close approximation.
306468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
306568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param primaries The set of RGB primaries in xyY as an array of 6 floats
306668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param whitePoint The white point in xyY as an array of 2 floats
306768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param OETF The opto-electronic transfer function
306868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param EOTF The electro-optical transfer function
306968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param min The minimum value of the color space's range
307068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param max The minimum value of the color space's range
307168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param id The ID of the color space
307268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return True if the color space can be considered as the sRGB color space
307368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
307468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #isSrgb()
307568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
307668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @SuppressWarnings("RedundantIfStatement")
307768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static boolean isSrgb(
307868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(6) float[] primaries,
307968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(2) float[] whitePoint,
308068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull DoubleUnaryOperator OETF,
308168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull DoubleUnaryOperator EOTF,
308268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float min,
308368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float max,
308468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @IntRange(from = MIN_ID, to = MAX_ID) int id) {
308568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (id == 0) return true;
308668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (!compare(primaries, SRGB_PRIMARIES)) {
308768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                return false;
308868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
308968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (!compare(whitePoint, ILLUMINANT_D65)) {
309068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                return false;
309168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
309268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (OETF.applyAsDouble(0.5) < 0.5001) return false;
309368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (EOTF.applyAsDouble(0.5) > 0.5001) return false;
309468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (min != 0.0f) return false;
309568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (max != 1.0f) return false;
309668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return true;
309768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
309868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
309968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
310068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Computes whether the specified CIE xyY or XYZ primaries (with Y set to 1) form
310168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * a wide color gamut. A color gamut is considered wide if its area is &gt; 90%
310268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * of the area of NTSC 1953 and if it contains the sRGB color gamut entirely.
310368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * If the conditions above are not met, the color space is considered as having
310468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * a wide color gamut if its range is larger than [0..1].
310568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
3106199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy         * @param primaries RGB primaries in CIE xyY as an array of 6 floats
310768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param min The minimum value of the color space's range
310868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param max The minimum value of the color space's range
310968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return True if the color space has a wide gamut, false otherwise
311068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
311168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #isWideGamut()
311268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #area(float[])
311368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
3114199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy        private static boolean isWideGamut(@NonNull @Size(6) float[] primaries,
311568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float min, float max) {
311668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return (area(primaries) / area(NTSC_1953_PRIMARIES) > 0.9f &&
311768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                            contains(primaries, SRGB_PRIMARIES)) || (min < 0.0f && max > 1.0f);
311868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
311968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
312068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
312168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Computes the area of the triangle represented by a set of RGB primaries
312268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * in the CIE xyY space.
312368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
312468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param primaries The triangle's vertices, as RGB primaries in an array of 6 floats
312568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return The area of the triangle
312668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
312768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #isWideGamut(float[], float, float)
312868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
312968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static float area(@NonNull @Size(6) float[] primaries) {
313068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Rx = primaries[0];
313168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Ry = primaries[1];
313268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Gx = primaries[2];
313368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Gy = primaries[3];
313468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Bx = primaries[4];
313568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float By = primaries[5];
313668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float det = Rx * Gy + Ry * Bx + Gx * By - Gy * Bx - Ry * Gx - Rx * By;
313768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float r = 0.5f * det;
313868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return r < 0.0f ? -r : r;
313968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
314068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
314168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
314268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Computes the cross product of two 2D vectors.
314368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
314468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param ax The x coordinate of the first vector
314568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param ay The y coordinate of the first vector
314668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param bx The x coordinate of the second vector
314768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param by The y coordinate of the second vector
314868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return The result of a x b
314968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
315068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static float cross(float ax, float ay, float bx, float by) {
315168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return ax * by - ay * bx;
315268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
315368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
315468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
315568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Decides whether a 2D triangle, identified by the 6 coordinates of its
315668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * 3 vertices, is contained within another 2D triangle, also identified
315768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * by the 6 coordinates of its 3 vertices.
315868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
315968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * In the illustration below, we want to test whether the RGB triangle
316068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * is contained within the triangle XYZ formed by the 3 vertices at
316168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * the "+" locations.
316268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
316368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                     Y     .
316468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                 .   +    .
316568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                  .     ..
316668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                   .   .
316768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                    . .
316868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                     .  G
316968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                     *
317068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                    * *
317168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                  **   *
317268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                 *      **
317368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                                *         *
317468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                              **           *
317568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                             *              *
317668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                            *                *
317768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                          **                  *
317868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                         *                     *
317968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                        *                       **
318068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                      **                          *   R    ...
318168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                     *                             *  .....
318268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                    *                         ***** ..
318368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *                  **              ************       .   +
318468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *              B  *    ************                    .   X
318568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *           ......*****                                 .
318668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *     ......    .                                        .
318768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *             ..
318868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *        +   .
318968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *      Z    .
319068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
319168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * RGB is contained within XYZ if all the following conditions are true
319268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * (with "x" the cross product operator):
319368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
319468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   -->  -->
319568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   GR x RX >= 0
319668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   -->  -->
319768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   RX x BR >= 0
319868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   -->  -->
319968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   RG x GY >= 0
320068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   -->  -->
320168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   GY x RG >= 0
320268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   -->  -->
320368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   RB x BZ >= 0
320468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   -->  -->
320568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *   BZ x GB >= 0
320668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
320768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param p1 The enclosing triangle
320868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param p2 The enclosed triangle
320968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return True if the triangle p1 contains the triangle p2
321068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
321168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #isWideGamut(float[], float, float)
321268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
321368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @SuppressWarnings("RedundantIfStatement")
321468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static boolean contains(@NonNull @Size(6) float[] p1, @NonNull @Size(6) float[] p2) {
321568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            // Translate the vertices p1 in the coordinates system
321668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            // with the vertices p2 as the origin
321768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float[] p0 = new float[] {
321868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    p1[0] - p2[0], p1[1] - p2[1],
321968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    p1[2] - p2[2], p1[3] - p2[3],
322068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    p1[4] - p2[4], p1[5] - p2[5],
322168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            };
322268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            // Check the first vertex of p1
322368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (cross(p0[0], p0[1], p2[0] - p2[4], p2[1] - p2[5]) < 0 ||
322468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    cross(p2[0] - p2[2], p2[1] - p2[3], p0[0], p0[1]) < 0) {
322568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                return false;
322668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
322768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            // Check the second vertex of p1
322868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (cross(p0[2], p0[3], p2[2] - p2[0], p2[3] - p2[1]) < 0 ||
322968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    cross(p2[2] - p2[4], p2[3] - p2[5], p0[2], p0[3]) < 0) {
323068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                return false;
323168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
323268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            // Check the third vertex of p1
323368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (cross(p0[4], p0[5], p2[4] - p2[2], p2[5] - p2[3]) < 0 ||
323468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    cross(p2[4] - p2[0], p2[5] - p2[1], p0[4], p0[5]) < 0) {
323568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                return false;
323668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
323768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return true;
323868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
323968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
324068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
324168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Computes the primaries  of a color space identified only by
324268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * its RGB->XYZ transform matrix. This method assumes that the
324368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * range of the color space is [0..1].
324468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
324568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param toXYZ The color space's 3x3 transform matrix to XYZ
324668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new array of 6 floats containing the color space's
324768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         primaries in CIE xyY
324868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
324968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
325068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(6)
3251efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        private static float[] computePrimaries(@NonNull @Size(9) float[] toXYZ) {
3252efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            float[] r = mul3x3Float3(toXYZ, new float[] { 1.0f, 0.0f, 0.0f });
3253efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            float[] g = mul3x3Float3(toXYZ, new float[] { 0.0f, 1.0f, 0.0f });
3254efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            float[] b = mul3x3Float3(toXYZ, new float[] { 0.0f, 0.0f, 1.0f });
325568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
325668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float rSum = r[0] + r[1] + r[2];
325768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float gSum = g[0] + g[1] + g[2];
325868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float bSum = b[0] + b[1] + b[2];
325968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
326068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return new float[] {
326168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    r[0] / rSum, r[1] / rSum,
326268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    g[0] / gSum, g[1] / gSum,
326368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    b[0] / bSum, b[1] / bSum,
326468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            };
326568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
326668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
326768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
326868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Computes the white point of a color space identified only by
326968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * its RGB->XYZ transform matrix. This method assumes that the
327068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * range of the color space is [0..1].
327168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
327268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param toXYZ The color space's 3x3 transform matrix to XYZ
327368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new array of 2 floats containing the color space's
327468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         white point in CIE xyY
327568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
327668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
327768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(2)
3278efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        private static float[] computeWhitePoint(@NonNull @Size(9) float[] toXYZ) {
3279efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy            float[] w = mul3x3Float3(toXYZ, new float[] { 1.0f, 1.0f, 1.0f });
328068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float sum = w[0] + w[1] + w[2];
328168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return new float[] { w[0] / sum, w[1] / sum };
328268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
328368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
328468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
328568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Converts the specified RGB primaries point to xyY if needed. The primaries
328668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * can be specified as an array of 6 floats (in CIE xyY) or 9 floats
328768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * (in CIE XYZ). If no conversion is needed, the input array is copied.
328868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
328968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param primaries The primaries in xyY or XYZ
329068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new array of 6 floats containing the primaries in xyY
329168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
329268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
3293199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy        @Size(6)
329468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static float[] xyPrimaries(@NonNull @Size(min = 6, max = 9) float[] primaries) {
329568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float[] xyPrimaries = new float[6];
329668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
329768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            // XYZ to xyY
329868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (primaries.length == 9) {
329968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float sum;
330068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
330168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                sum = primaries[0] + primaries[1] + primaries[2];
330268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyPrimaries[0] = primaries[0] / sum;
330368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyPrimaries[1] = primaries[1] / sum;
330468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
330568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                sum = primaries[3] + primaries[4] + primaries[5];
330668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyPrimaries[2] = primaries[3] / sum;
330768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyPrimaries[3] = primaries[4] / sum;
330868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
330968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                sum = primaries[6] + primaries[7] + primaries[8];
331068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyPrimaries[4] = primaries[6] / sum;
331168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyPrimaries[5] = primaries[7] / sum;
331268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            } else {
331368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                System.arraycopy(primaries, 0, xyPrimaries, 0, 6);
331468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
331568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
331668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return xyPrimaries;
331768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
331868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
331968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
332068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Converts the specified white point to xyY if needed. The white point
332168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * can be specified as an array of 2 floats (in CIE xyY) or 3 floats
332268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * (in CIE XYZ). If no conversion is needed, the input array is copied.
332368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
332468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param whitePoint The white point in xyY or XYZ
332568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new array of 2 floats containing the white point in xyY
332668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
332768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
332868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(2)
332968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static float[] xyWhitePoint(@Size(min = 2, max = 3) float[] whitePoint) {
333068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float[] xyWhitePoint = new float[2];
333168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
333268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            // XYZ to xyY
333368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (whitePoint.length == 3) {
333468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float sum = whitePoint[0] + whitePoint[1] + whitePoint[2];
333568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyWhitePoint[0] = whitePoint[0] / sum;
333668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyWhitePoint[1] = whitePoint[1] / sum;
333768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            } else {
333868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                System.arraycopy(whitePoint, 0, xyWhitePoint, 0, 2);
333968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
334068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
334168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return xyWhitePoint;
334268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
334368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
334468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
334568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Computes the matrix that converts from RGB to XYZ based on RGB
334668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * primaries and a white point, both specified in the CIE xyY space.
334768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * The Y component of the primaries and white point is implied to be 1.
334868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
334968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param primaries The RGB primaries in xyY, as an array of 6 floats
335068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param whitePoint The white point in xyY, as an array of 2 floats
335168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A 3x3 matrix as a new array of 9 floats
335268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
335368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
335468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(9)
335568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static float[] computeXYZMatrix(
335668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(6) float[] primaries,
335768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull @Size(2) float[] whitePoint) {
335868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Rx = primaries[0];
335968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Ry = primaries[1];
336068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Gx = primaries[2];
336168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Gy = primaries[3];
336268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Bx = primaries[4];
336368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float By = primaries[5];
336468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Wx = whitePoint[0];
336568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float Wy = whitePoint[1];
336668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
336768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float oneRxRy = (1 - Rx) / Ry;
336868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float oneGxGy = (1 - Gx) / Gy;
336968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float oneBxBy = (1 - Bx) / By;
337068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float oneWxWy = (1 - Wx) / Wy;
337168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
337268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float RxRy = Rx / Ry;
337368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float GxGy = Gx / Gy;
337468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float BxBy = Bx / By;
337568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float WxWy = Wx / Wy;
337668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
337768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float BY =
337868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) /
337968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy));
338068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy);
338168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float RY = 1 - GY - BY;
338268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
338368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float RYRy = RY / Ry;
338468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float GYGy = GY / Gy;
338568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float BYBy = BY / By;
338668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
338768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return new float[] {
338868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    RYRy * Rx, RY, RYRy * (1 - Rx - Ry),
338968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    GYGy * Gx, GY, GYGy * (1 - Gx - Gy),
339068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    BYBy * Bx, BY, BYBy * (1 - Bx - By)
339168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            };
339268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
339368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
339468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
339568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    /**
339668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@usesMathJax}
339768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
339868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>A connector transforms colors from a source color space to a destination
339968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color space.</p>
340068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
340168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>A source color space is connected to a destination color space using the
340268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * color transform \(C\) computed from their respective transforms noted
340368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * \(T_{src}\) and \(T_{dst}\) in the following equation:</p>
340468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
340568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * $$C = T^{-1}_{dst} . T_{src}$$
340668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
340768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>The transform \(C\) shown above is only valid when the source and
340868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * destination color spaces have the same profile connection space (PCS).
340968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * We know that instances of {@link ColorSpace} always use CIE XYZ as their
341068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * PCS but their white points might differ. When they do, we must perform
341168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * a chromatic adaptation of the color spaces' transforms. To do so, we
341268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * use the von Kries method described in the documentation of {@link Adaptation},
341368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * using the CIE standard illuminant {@link ColorSpace#ILLUMINANT_D50 D50}
341468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * as the target white point.</p>
341568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
341668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <p>Example of conversion from {@link Named#SRGB sRGB} to
341768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * {@link Named#DCI_P3 DCI-P3}:</p>
341868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
341968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * <pre class="prettyprint">
342068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * ColorSpace.Connector connector = ColorSpace.connect(
342168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         ColorSpace.get(ColorSpace.Named.SRGB),
342268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *         ColorSpace.get(ColorSpace.Named.DCI_P3));
342368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * float[] p3 = connector.transform(1.0f, 0.0f, 0.0f);
342468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * // p3 contains { 0.9473, 0.2740, 0.2076 }
342568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * </pre>
342668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     *
342768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see Adaptation
342868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see ColorSpace#adapt(ColorSpace, float[], Adaptation)
342968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see ColorSpace#adapt(ColorSpace, float[])
343068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see ColorSpace#connect(ColorSpace, ColorSpace, RenderIntent)
343168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see ColorSpace#connect(ColorSpace, ColorSpace)
343268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see ColorSpace#connect(ColorSpace, RenderIntent)
343368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     * @see ColorSpace#connect(ColorSpace)
343468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy     */
343577b161e0b14372e3eb124ed19321a9639aeb4271Romain Guy    @AnyThread
343668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    public static class Connector {
343768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final ColorSpace mSource;
343868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final ColorSpace mDestination;
343968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final ColorSpace mTransformSource;
344068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final ColorSpace mTransformDestination;
344168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull private final RenderIntent mIntent;
344268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull @Size(3) private final float[] mTransform;
344368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
344468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
344568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Creates a new connector between a source and a destination color space.
344668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
344768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param source The source color space, cannot be null
344868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param destination The destination color space, cannot be null
344968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param intent The render intent to use when compressing gamuts
345068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
345168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        Connector(@NonNull ColorSpace source, @NonNull ColorSpace destination,
345268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull RenderIntent intent) {
345368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            this(source, destination,
345468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    source.getModel() == Model.RGB ? adapt(source, ILLUMINANT_D50_XYZ) : source,
345568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    destination.getModel() == Model.RGB ?
345668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                            adapt(destination, ILLUMINANT_D50_XYZ) : destination,
345768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    intent, computeTransform(source, destination, intent));
345868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
345968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
346068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
346168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * To connect between color spaces, we might need to use adapted transforms.
346268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * This should be transparent to the user so this constructor takes the
346368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * original source and destinations (returned by the getters), as well as
346468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * possibly adapted color spaces used by transform().
346568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
346668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private Connector(
346768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull ColorSpace source, @NonNull ColorSpace destination,
346868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull ColorSpace transformSource, @NonNull ColorSpace transformDestination,
3469199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy                @NonNull RenderIntent intent, @Nullable @Size(3) float[] transform) {
347068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mSource = source;
347168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mDestination = destination;
347268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mTransformSource = transformSource;
347368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mTransformDestination = transformDestination;
347468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mIntent = intent;
347568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            mTransform = transform;
347668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
347768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
347868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
347968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Computes an extra transform to apply in XYZ space depending on the
348068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * selected rendering intent.
348168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
3482efb4b06493fe7b1604c762a448b13c7af2845a8dRomain Guy        @Nullable
348368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        private static float[] computeTransform(@NonNull ColorSpace source,
348468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @NonNull ColorSpace destination, @NonNull RenderIntent intent) {
348568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (intent != RenderIntent.ABSOLUTE) return null;
348668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
348768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            boolean srcRGB = source.getModel() == Model.RGB;
348868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            boolean dstRGB = destination.getModel() == Model.RGB;
348968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
349068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (srcRGB && dstRGB) return null;
349168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
349268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (srcRGB || dstRGB) {
349368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                ColorSpace.Rgb rgb = (ColorSpace.Rgb) (srcRGB ? source : destination);
349468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float[] srcXYZ = srcRGB ? xyYToXyz(rgb.mWhitePoint) : ILLUMINANT_D50_XYZ;
349568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                float[] dstXYZ = dstRGB ? xyYToXyz(rgb.mWhitePoint) : ILLUMINANT_D50_XYZ;
349668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                return new float[] {
349768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        srcXYZ[0] / dstXYZ[0],
349868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        srcXYZ[1] / dstXYZ[1],
349968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        srcXYZ[2] / dstXYZ[2],
350068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                };
350168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
350268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
350368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return null;
350468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
350568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
350668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
350768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Returns the source color space this connector will convert from.
350868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
350968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A non-null instance of {@link ColorSpace}
351068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
351168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getDestination()
351268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
351368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
351468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public ColorSpace getSource() {
351568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mSource;
351668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
351768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
351868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
351968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Returns the destination color space this connector will convert to.
352068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
352168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A non-null instance of {@link ColorSpace}
352268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
352368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #getSource()
352468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
352568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
352668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public ColorSpace getDestination() {
352768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mDestination;
352868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
352968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
353068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
353168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Returns the render intent this connector will use when mapping the
353268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * source color space to the destination color space.
353368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
353468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A non-null {@link RenderIntent}
353568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
353668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see RenderIntent
353768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
35381cf7b4fcb3b2c239e3f0a68ec78cfedff510b779Romain Guy        public RenderIntent getRenderIntent() {
353968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mIntent;
354068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
354168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
354268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
354368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Transforms the specified color from the source color space
354468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to a color in the destination color space. This convenience
354568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * method assumes a source color model with 3 components
354668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * (typically RGB). To transform from color models with more than
354768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * 3 components, such as {@link Model#CMYK CMYK}, use
354868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * {@link #transform(float[])} instead.</p>
354968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
355068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param r The red component of the color to transform
355168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param g The green component of the color to transform
355268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param b The blue component of the color to transform
355368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A new array of 3 floats containing the specified color
355468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         transformed from the source space to the destination space
355568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
355668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #transform(float[])
355768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
355868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
355968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(3)
356068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] transform(float r, float g, float b) {
356168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return transform(new float[] { r, g, b });
356268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
356368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
356468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
356568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * <p>Transforms the specified color from the source color space
356668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * to a color in the destination color space.</p>
356768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
356868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param v A non-null array of 3 floats containing the value to transform
356968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *            and that will hold the result of the transform
357068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return The v array passed as a parameter, containing the specified color
357168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *         transformed from the source space to the destination space
357268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
357368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see #transform(float, float, float)
357468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
357568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @NonNull
357668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        @Size(min = 3)
357768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        public float[] transform(@NonNull @Size(min = 3) float[] v) {
357868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            float[] xyz = mTransformSource.toXyz(v);
357968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            if (mTransform != null) {
358068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyz[0] *= mTransform[0];
358168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyz[1] *= mTransform[1];
358268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                xyz[2] *= mTransform[2];
358368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
358468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return mTransformDestination.fromXyz(xyz);
358568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
358668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
358768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
358868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Optimized connector for RGB->RGB conversions.
358968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
3590199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy        private static class Rgb extends Connector {
359168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull private final ColorSpace.Rgb mSource;
359268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull private final ColorSpace.Rgb mDestination;
359368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull private final float[] mTransform;
359468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
3595199e5a98ddc4402ba4b4cdafaa3d8deb58ef3c7dRomain Guy            Rgb(@NonNull ColorSpace.Rgb source, @NonNull ColorSpace.Rgb destination,
359668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    @NonNull RenderIntent intent) {
359768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                super(source, destination, source, destination, intent, null);
359868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                mSource = source;
359968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                mDestination = destination;
360068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                mTransform = computeTransform(source, destination, intent);
360168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
360268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
360368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @Override
360468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            public float[] transform(@NonNull @Size(min = 3) float[] rgb) {
360568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                rgb[0] = (float) mSource.mClampedEotf.applyAsDouble(rgb[0]);
360668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                rgb[1] = (float) mSource.mClampedEotf.applyAsDouble(rgb[1]);
360768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                rgb[2] = (float) mSource.mClampedEotf.applyAsDouble(rgb[2]);
360868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                mul3x3Float3(mTransform, rgb);
360968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                rgb[0] = (float) mDestination.mClampedOetf.applyAsDouble(rgb[0]);
361068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                rgb[1] = (float) mDestination.mClampedOetf.applyAsDouble(rgb[1]);
361168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                rgb[2] = (float) mDestination.mClampedOetf.applyAsDouble(rgb[2]);
361268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                return rgb;
361368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
361468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
361568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            /**
361668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * <p>Computes the color transform that connects two RGB color spaces.</p>
361768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             *
361868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * <p>We can only connect color spaces if they use the same profile
361968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * connection space. We assume the connection space is always
362068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * CIE XYZ but we maye need to perform a chromatic adaptation to
362168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * match the white points. If an adaptation is needed, we use the
362268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * CIE standard illuminant D50. The unmatched color space is adapted
362368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * using the von Kries transform and the {@link Adaptation#BRADFORD}
362468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * matrix.</p>
362568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             *
362668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * @param source The source color space, cannot be null
362768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * @param destination The destination color space, cannot be null
362868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * @param intent The render intent to use when compressing gamuts
362968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             * @return An array of 9 floats containing the 3x3 matrix transform
363068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy             */
363168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @NonNull
363268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            @Size(9)
363368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            private static float[] computeTransform(
363468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    @NonNull ColorSpace.Rgb source,
363568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    @NonNull ColorSpace.Rgb destination,
363668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    @NonNull RenderIntent intent) {
363768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                if (compare(source.mWhitePoint, destination.mWhitePoint)) {
363868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    // RGB->RGB using the PCS of both color spaces since they have the same
363968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    return mul3x3(destination.mInverseTransform, source.mTransform);
364068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                } else {
364168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    // RGB->RGB using CIE XYZ D50 as the PCS
364268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    float[] transform = source.mTransform;
364368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    float[] inverseTransform = destination.mInverseTransform;
364468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
364568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    float[] srcXYZ = xyYToXyz(source.mWhitePoint);
364668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    float[] dstXYZ = xyYToXyz(destination.mWhitePoint);
364768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
364868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    if (!compare(source.mWhitePoint, ILLUMINANT_D50)) {
364968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        float[] srcAdaptation = chromaticAdaptation(
365068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                                Adaptation.BRADFORD.mTransform, srcXYZ,
365168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                                Arrays.copyOf(ILLUMINANT_D50_XYZ, 3));
365268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        transform = mul3x3(srcAdaptation, source.mTransform);
365368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    }
365468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
365568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    if (!compare(destination.mWhitePoint, ILLUMINANT_D50)) {
365668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        float[] dstAdaptation = chromaticAdaptation(
365768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                                Adaptation.BRADFORD.mTransform, dstXYZ,
365868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                                Arrays.copyOf(ILLUMINANT_D50_XYZ, 3));
365968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        inverseTransform = inverse3x3(mul3x3(dstAdaptation, destination.mTransform));
366068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    }
366168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
366268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    if (intent == RenderIntent.ABSOLUTE) {
366368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                        transform = mul3x3Diag(
366468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                                new float[] {
366568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                                        srcXYZ[0] / dstXYZ[0],
366668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                                        srcXYZ[1] / dstXYZ[1],
366768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                                        srcXYZ[2] / dstXYZ[2],
366868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                                }, transform);
366968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    }
367068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
367168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    return mul3x3(inverseTransform, transform);
367268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                }
367368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            }
367468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
367568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy
367668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        /**
367768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * Returns the identity connector for a given color space.
367868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
367968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @param source The source and destination color space
368068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @return A non-null connector that does not perform any transform
368168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         *
368268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         * @see ColorSpace#connect(ColorSpace, ColorSpace)
368368bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy         */
368468bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        static Connector identity(ColorSpace source) {
368568bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            return new Connector(source, source, RenderIntent.RELATIVE) {
368668bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                @Override
368768bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                public float[] transform(@NonNull @Size(min = 3) float[] v) {
368868bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                    return v;
368968bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy                }
369068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy            };
369168bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy        }
369268bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy    }
369315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
369415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy    /**
369515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * <p>A color space renderer can be used to visualize and compare the gamut and
369615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * white point of one or more color spaces. The output is an sRGB {@link Bitmap}
36979505a6552764461c22ce48f1ac13d025d23e1579Romain Guy     * showing a CIE 1931 xyY or a CIE 1976 UCS chromaticity diagram.</p>
369815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *
369915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * <p>The following code snippet shows how to compare the {@link Named#SRGB}
37009505a6552764461c22ce48f1ac13d025d23e1579Romain Guy     * and {@link Named#DCI_P3} color spaces in a CIE 1931 diagram:</p>
370115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *
370215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * <pre class="prettyprint">
370315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * Bitmap bitmap = ColorSpace.createRenderer()
370415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *     .size(768)
370515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *     .clip(true)
370615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
370715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
370815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *     .render();
370915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * </pre>
371015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * <p>
3711c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy     *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_clipped.png" />
371215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *     <figcaption style="text-align: center;">sRGB vs DCI-P3</figcaption>
371315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * </p>
371415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *
371515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * <p>A renderer can also be used to show the location of specific colors,
371615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * associated with a color space, in the CIE 1931 xyY chromaticity diagram.
371715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * See {@link #add(ColorSpace, float, float, float, int)} for more information.</p>
371815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     *
371915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     * @see ColorSpace#createRenderer()
372066d7da6a41d2ae2acf8220d8b146ba6eea385a28Romain Guy     *
372166d7da6a41d2ae2acf8220d8b146ba6eea385a28Romain Guy     * @hide
372215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy     */
372315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy    public static class Renderer {
372415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private static final int NATIVE_SIZE = 1440;
37259505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        private static final float UCS_SCALE = 9.0f / 6.0f;
37269505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
37279505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        // Number of subdivision of the inside of the spectral locus
37289505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        private static final int CHROMATICITY_RESOLUTION = 32;
37299505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        private static final double ONE_THIRD = 1.0 / 3.0;
373015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
373115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        @IntRange(from = 128, to = Integer.MAX_VALUE)
373215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private int mSize = 1024;
373315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
373415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private boolean mShowWhitePoint = true;
373515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private boolean mClip = false;
37369505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        private boolean mUcs = false;
373715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
373815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private final List<Pair<ColorSpace, Integer>> mColorSpaces = new ArrayList<>(2);
373915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private final List<Point> mPoints = new ArrayList<>(0);
374015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
374115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private Renderer() {
374215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
374315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
374415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
374515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>Defines whether the chromaticity diagram should be clipped by the first
374615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * registered color space. The default value is false.</p>
374715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
374815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>The following code snippet and image show the default behavior:</p>
374915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <pre class="prettyprint">
375015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Bitmap bitmap = ColorSpace.createRenderer()
375115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
375215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
375315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .render();
375415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * </pre>
375515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>
3756c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_comparison.png" />
375715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     <figcaption style="text-align: center;">Clipping disabled</figcaption>
375815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * </p>
375915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
376015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>Here is the same example with clipping enabled:</p>
376115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <pre class="prettyprint">
376215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Bitmap bitmap = ColorSpace.createRenderer()
376315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .clip(true)
376415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
376515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
376615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .render();
376715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * </pre>
376815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>
3769c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_clipped.png" />
377015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     <figcaption style="text-align: center;">Clipping enabled</figcaption>
377115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * </p>
377215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
377315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param clip True to clip the chromaticity diagram to the first registered color space,
377415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *             false otherwise
377515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @return This instance of {@link Renderer}
377615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
377715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        @NonNull
377815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        public Renderer clip(boolean clip) {
377915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            mClip = clip;
378015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            return this;
378115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
378215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
378315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
37849505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * <p>Defines whether the chromaticity diagram should use the uniform
37859505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * chromaticity scale (CIE 1976 UCS). When the uniform chromaticity scale
37869505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * is used, the distance between two points on the diagram is approximately
37879505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * proportional to the perceived color difference.</p>
37889505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         *
37899505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * <p>The following code snippet shows how to enable the uniform chromaticity
37909505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * scale. The image below shows the result:</p>
37919505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * <pre class="prettyprint">
37929505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * Bitmap bitmap = ColorSpace.createRenderer()
37939505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         *     .uniformChromaticityScale(true)
37949505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
37959505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
37969505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         *     .render();
37979505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * </pre>
37989505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * <p>
3799c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_ucs.png" />
38009505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         *     <figcaption style="text-align: center;">CIE 1976 UCS diagram</figcaption>
38019505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * </p>
38029505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         *
38039505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * @param ucs True to render the chromaticity diagram as the CIE 1976 UCS diagram
38049505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * @return This instance of {@link Renderer}
38059505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         */
38069505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        @NonNull
38079505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        public Renderer uniformChromaticityScale(boolean ucs) {
38089505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            mUcs = ucs;
38099505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            return this;
38109505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        }
38119505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
38129505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        /**
381315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Sets the dimensions (width and height) in pixels of the output bitmap.
381415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * The size must be at least 128px and defaults to 1024px.
381515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
381615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param size The size in pixels of the output bitmap
381715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @return This instance of {@link Renderer}
381815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
381915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        @NonNull
382015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        public Renderer size(@IntRange(from = 128, to = Integer.MAX_VALUE) int size) {
382115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            mSize = Math.max(128, size);
382215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            return this;
382315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
382415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
382515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
382615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Shows or hides the white point of each color space in the output bitmap.
382715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * The default is true.
382815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
382915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param show True to show the white point of each color space, false
383015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *             otherwise
383115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @return This instance of {@link Renderer}
383215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
383315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        @NonNull
383415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        public Renderer showWhitePoint(boolean show) {
383515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            mShowWhitePoint = show;
383615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            return this;
383715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
383815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
383915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
384015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>Adds a color space to represent on the output CIE 1931 chromaticity
384115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * diagram. The color space is represented as a triangle showing the
384215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * footprint of its color gamut and, optionally, the location of its
384315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * white point.</p>
384415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
384515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p class="note">Color spaces with a color model that is not RGB are
384615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * accepted but ignored.</p>
384715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
384815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>The following code snippet and image show an example of calling this
384915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * method to compare {@link Named#SRGB sRGB} and {@link Named#DCI_P3 DCI-P3}:</p>
385015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <pre class="prettyprint">
385115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Bitmap bitmap = ColorSpace.createRenderer()
385215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
385315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
385415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .render();
385515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * </pre>
385615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>
3857c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_comparison.png" />
385815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     <figcaption style="text-align: center;">sRGB vs DCI-P3</figcaption>
385915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * </p>
386015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
386115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>Adding a color space extending beyond the boundaries of the
386215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * spectral locus will alter the size of the diagram within the output
386315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * bitmap as shown in this example:</p>
386415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <pre class="prettyprint">
386515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Bitmap bitmap = ColorSpace.createRenderer()
386615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
386715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
386815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.ACES), 0xff097ae9)
386915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), 0xff000000)
387015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .render();
387115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * </pre>
387215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>
3873c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_comparison2.png" />
38749505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         *     <figcaption style="text-align: center;">sRGB, DCI-P3, ACES and scRGB</figcaption>
387515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * </p>
387615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
387715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param colorSpace The color space whose gamut to render on the diagram
387815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param color The sRGB color to use to render the color space's gamut and white point
387915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @return This instance of {@link Renderer}
388015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
388115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @see #clip(boolean)
388215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @see #showWhitePoint(boolean)
388315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
388415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        @NonNull
388515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        public Renderer add(@NonNull ColorSpace colorSpace, @ColorInt int color) {
388615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            mColorSpaces.add(new Pair<>(colorSpace, color));
388715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            return this;
388815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
388915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
389015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
389115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>Adds a color to represent as a point on the chromaticity diagram.
389215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * The color is associated with a color space which will be used to
389315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * perform the conversion to CIE XYZ and compute the location of the point
389415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * on the diagram. The point is rendered as a colored circle.</p>
389515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
389615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>The following code snippet and image show an example of calling this
389715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * method to render the location of several sRGB colors as white circles:</p>
389815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <pre class="prettyprint">
389915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Bitmap bitmap = ColorSpace.createRenderer()
390015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .clip(true)
390115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
390215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.0f, 0.1f, 0xffffffff)
390315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.1f, 0.1f, 0xffffffff)
390415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.2f, 0.1f, 0xffffffff)
390515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.3f, 0.1f, 0xffffffff)
390615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.4f, 0.1f, 0xffffffff)
390715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0.1f, 0.5f, 0.1f, 0xffffffff)
390815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     .render();
390915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * </pre>
391015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>
3911c7dacca00828e586ce4496d83a25a4d60a6fb60fRomain Guy         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_points.png" />
391215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     <figcaption style="text-align: center;">
391315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *         Locating colors on the chromaticity diagram
391415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *     </figcaption>
391515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * </p>
391615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
391715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param colorSpace The color space of the color to locate on the diagram
391815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param r The first component of the color to locate on the diagram
391915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param g The second component of the color to locate on the diagram
392015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param b The third component of the color to locate on the diagram
392115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param pointColor The sRGB color to use to render the point on the diagram
392215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @return This instance of {@link Renderer}
392315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
392415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        @NonNull
392515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        public Renderer add(@NonNull ColorSpace colorSpace, float r, float g, float b,
392615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                @ColorInt int pointColor) {
392715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            mPoints.add(new Point(colorSpace, new float[] { r, g, b }, pointColor));
392815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            return this;
392915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
393015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
393115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
393215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * <p>Renders the {@link #add(ColorSpace, int) color spaces} and
393315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * {@link #add(ColorSpace, float, float, float, int) points} registered
393415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * with this renderer. The output bitmap is an sRGB image with the
393515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * dimensions specified by calling {@link #size(int)} (1204x1024px by
393615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * default).</p>
393715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
393815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @return A new non-null {@link Bitmap} with the dimensions specified
393915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *        by {@link #size(int)} (1024x1024 by default)
394015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
394115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        @NonNull
394215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        public Bitmap render() {
394315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
394415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            Bitmap bitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
394515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            Canvas canvas = new Canvas(bitmap);
394615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
394715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            float[] primaries = new float[6];
394815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            float[] whitePoint = new float[2];
394915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
395015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            int width = NATIVE_SIZE;
395115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            int height = NATIVE_SIZE;
395215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
395315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            Path path = new Path();
395415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
395515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            setTransform(canvas, width, height, primaries);
395615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            drawBox(canvas, width, height, paint, path);
39579505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            setUcsTransform(canvas, height);
395815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            drawLocus(canvas, width, height, paint, path, primaries);
395915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            drawGamuts(canvas, width, height, paint, path, primaries, whitePoint);
396015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            drawPoints(canvas, width, height, paint);
396115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
396215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            return bitmap;
396315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
396415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
396515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
396615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Draws registered points at their correct position in the xyY coordinates.
396715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Each point is positioned according to its associated color space.
396815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
396915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param canvas The canvas to transform
397015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param width Width in pixel of the final image
397115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param height Height in pixel of the final image
397215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param paint A pre-allocated paint used to avoid temporary allocations
397315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
397415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private void drawPoints(@NonNull Canvas canvas, int width, int height,
397515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                @NonNull Paint paint) {
397615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
397715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setStyle(Paint.Style.FILL);
397815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
39799505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float radius = 4.0f / (mUcs ? UCS_SCALE : 1.0f);
39809505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
398115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            float[] v = new float[3];
39829505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float[] xy = new float[2];
39839505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
398415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            for (final Point point : mPoints) {
398515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                v[0] = point.mRgb[0];
398615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                v[1] = point.mRgb[1];
398715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                v[2] = point.mRgb[2];
398815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                point.mColorSpace.toXyz(v);
398915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
399015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                paint.setColor(point.mColor);
399115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
39929505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                // XYZ to xyY, assuming Y=1.0, then to L*u*v* if needed
399315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                float sum = v[0] + v[1] + v[2];
39949505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                xy[0] = v[0] / sum;
39959505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                xy[1] = v[1] / sum;
39969505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                if (mUcs) xyYToUv(xy);
39979505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
39989505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                canvas.drawCircle(width * xy[0], height - height * xy[1], radius, paint);
399915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
400015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
400115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
400215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
400315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Draws the color gamuts and white points of all the registered color
400415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * spaces. Only color spaces with an RGB color model are rendered, the
400515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * others are ignored.
400615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
400715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param canvas The canvas to transform
400815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param width Width in pixel of the final image
400915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param height Height in pixel of the final image
401015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param paint A pre-allocated paint used to avoid temporary allocations
401115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param path A pre-allocated path used to avoid temporary allocations
401215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param primaries A pre-allocated array of 6 floats to avoid temporary allocations
401315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param whitePoint A pre-allocated array of 2 floats to avoid temporary allocations
401415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
401515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private void drawGamuts(
401615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                @NonNull Canvas canvas, int width, int height,
401715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                @NonNull Paint paint, @NonNull Path path,
401815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                @NonNull @Size(6) float[] primaries, @NonNull @Size(2) float[] whitePoint) {
401915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
40209505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float radius = 4.0f / (mUcs ? UCS_SCALE : 1.0f);
40219505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
402215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            for (final Pair<ColorSpace, Integer> item : mColorSpaces) {
402315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                ColorSpace colorSpace = item.first;
402415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                int color = item.second;
402515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
402615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                if (colorSpace.getModel() != Model.RGB) continue;
402715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
402815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                Rgb rgb = (Rgb) colorSpace;
40299505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                getPrimaries(rgb, primaries, mUcs);
403015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
403115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.rewind();
403215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.moveTo(width * primaries[0], height - height * primaries[1]);
403315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.lineTo(width * primaries[2], height - height * primaries[3]);
403415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.lineTo(width * primaries[4], height - height * primaries[5]);
403515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.close();
403615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
403715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                paint.setStyle(Paint.Style.STROKE);
403815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                paint.setColor(color);
403915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                canvas.drawPath(path, paint);
404015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
404115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                // Draw the white point
404215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                if (mShowWhitePoint) {
404315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    rgb.getWhitePoint(whitePoint);
40449505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    if (mUcs) xyYToUv(whitePoint);
404515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
404615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    paint.setStyle(Paint.Style.FILL);
404715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    paint.setColor(color);
40489505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    canvas.drawCircle(
40499505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                            width * whitePoint[0], height - height * whitePoint[1], radius, paint);
405015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                }
405115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
405215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
405315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
405415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
405515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Returns the primaries of the specified RGB color space. This method handles
405615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * the special case of the {@link Named#EXTENDED_SRGB} family of color spaces.
405715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
405815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param rgb The color space whose primaries to extract
405915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param primaries A pre-allocated array of 6 floats that will hold the result
40609505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * @param asUcs True if the primaries should be returned in Luv, false for xyY
406115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
406215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        @NonNull
406315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        @Size(6)
406466d7da6a41d2ae2acf8220d8b146ba6eea385a28Romain Guy        private static void getPrimaries(@NonNull Rgb rgb,
40659505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                @NonNull @Size(6) float[] primaries, boolean asUcs) {
406615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            // TODO: We should find a better way to handle these cases
406715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            if (rgb.equals(ColorSpace.get(Named.EXTENDED_SRGB)) ||
406815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    rgb.equals(ColorSpace.get(Named.LINEAR_EXTENDED_SRGB))) {
406915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                primaries[0] = 1.41f;
407015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                primaries[1] = 0.33f;
407115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                primaries[2] = 0.27f;
407215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                primaries[3] = 1.24f;
407315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                primaries[4] = -0.23f;
407415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                primaries[5] = -0.57f;
40759505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            } else {
40769505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                rgb.getPrimaries(primaries);
407715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
40789505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            if (asUcs) xyYToUv(primaries);
407915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
408015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
408115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
408215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Draws the CIE 1931 chromaticity diagram: the spectral locus and its inside.
408315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * This method respect the clip parameter.
408415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
408515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param canvas The canvas to transform
408615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param width Width in pixel of the final image
408715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param height Height in pixel of the final image
408815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param paint A pre-allocated paint used to avoid temporary allocations
408915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param path A pre-allocated path used to avoid temporary allocations
409015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param primaries A pre-allocated array of 6 floats to avoid temporary allocations
409115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
409215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private void drawLocus(
409315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                @NonNull Canvas canvas, int width, int height, @NonNull Paint paint,
409415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                @NonNull Path path, @NonNull @Size(6) float[] primaries) {
409515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
409615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            int vertexCount = SPECTRUM_LOCUS_X.length * CHROMATICITY_RESOLUTION * 6;
409715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            float[] vertices = new float[vertexCount * 2];
409815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            int[] colors = new int[vertices.length];
40999505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            computeChromaticityMesh(vertices, colors);
41009505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
41019505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            if (mUcs) xyYToUv(vertices);
41029505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            for (int i = 0; i < vertices.length; i += 2) {
41039505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                vertices[i] *= width;
41049505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                vertices[i + 1] = height - vertices[i + 1] * height;
41059505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            }
410615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
410715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            // Draw the spectral locus
410815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            if (mClip && mColorSpaces.size() > 0) {
410915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                for (final Pair<ColorSpace, Integer> item : mColorSpaces) {
411015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    ColorSpace colorSpace = item.first;
411115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    if (colorSpace.getModel() != Model.RGB) continue;
411215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
411315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    Rgb rgb = (Rgb) colorSpace;
41149505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    getPrimaries(rgb, primaries, mUcs);
41159505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
411615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    break;
411715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                }
411815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
411915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.rewind();
412015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.moveTo(width * primaries[0], height - height * primaries[1]);
412115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.lineTo(width * primaries[2], height - height * primaries[3]);
412215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.lineTo(width * primaries[4], height - height * primaries[5]);
412315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.close();
412415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
412515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                int[] solid = new int[colors.length];
412615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                Arrays.fill(solid, 0xff6c6c6c);
412715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertices.length, vertices, 0,
412815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        null, 0, solid, 0, null, 0, 0, paint);
412915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
413015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                canvas.save();
413115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                canvas.clipPath(path);
413215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
413315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertices.length, vertices, 0,
413415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        null, 0, colors, 0, null, 0, 0, paint);
413515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
413615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                canvas.restore();
413715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            } else {
413815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertices.length, vertices, 0,
413915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        null, 0, colors, 0, null, 0, 0, paint);
414015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
414115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
414215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            // Draw the non-spectral locus
414315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            int index = (CHROMATICITY_RESOLUTION - 1) * 12;
414415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            path.reset();
414515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            path.moveTo(vertices[index], vertices[index + 1]);
414615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            for (int x = 2; x < SPECTRUM_LOCUS_X.length; x++) {
414715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                index += CHROMATICITY_RESOLUTION * 12;
414815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                path.lineTo(vertices[index], vertices[index + 1]);
414915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
415015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            path.close();
415115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
41529505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            paint.setStrokeWidth(4.0f / (mUcs ? UCS_SCALE : 1.0f));
415315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setStyle(Paint.Style.STROKE);
415415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setColor(0xff000000);
415515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            canvas.drawPath(path, paint);
415615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
415715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
415815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
415915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Draws the diagram box, including borders, tick marks, grid lines
416015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * and axis labels.
416115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
416215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param canvas The canvas to transform
416315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param width Width in pixel of the final image
416415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param height Height in pixel of the final image
416515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param paint A pre-allocated paint used to avoid temporary allocations
416615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param path A pre-allocated path used to avoid temporary allocations
416715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
416815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private void drawBox(@NonNull Canvas canvas, int width, int height, @NonNull Paint paint,
416915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                @NonNull Path path) {
41709505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
41719505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            int lineCount = 10;
41729505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float scale = 1.0f;
41739505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            if (mUcs) {
41749505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                lineCount = 7;
41759505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                scale = UCS_SCALE;
41769505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            }
41779505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
417815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            // Draw the unit grid
417915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setStyle(Paint.Style.STROKE);
418015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setStrokeWidth(2.0f);
418115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setColor(0xffc0c0c0);
41829505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
41839505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            for (int i = 1; i < lineCount - 1; i++) {
41849505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                float v = i / 10.0f;
41859505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                float x = (width * v) * scale;
41869505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                float y = height - (height * v) * scale;
41879505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
41889505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                canvas.drawLine(0.0f, y, 0.9f * width, y, paint);
41899505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                canvas.drawLine(x, height, x, 0.1f * height, paint);
419015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
419115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
419215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            // Draw tick marks
419315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setStrokeWidth(4.0f);
419415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setColor(0xff000000);
41959505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            for (int i = 1; i < lineCount - 1; i++) {
41969505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                float v = i / 10.0f;
41979505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                float x = (width * v) * scale;
41989505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                float y = height - (height * v) * scale;
41999505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
42009505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                canvas.drawLine(0.0f, y, width / 100.0f, y, paint);
42019505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                canvas.drawLine(x, height, x, height - (height / 100.0f), paint);
420215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
420315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
420415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            // Draw the axis labels
420515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setStyle(Paint.Style.FILL);
420615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setTextSize(36.0f);
420715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
420815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
420915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            Rect bounds = new Rect();
42109505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            for (int i = 1; i < lineCount - 1; i++) {
421115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                String text = "0." + i;
421215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                paint.getTextBounds(text, 0, text.length(), bounds);
421315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
42149505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                float v = i / 10.0f;
42159505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                float x = (width * v) * scale;
42169505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                float y = height - (height * v) * scale;
421715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
42189505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                canvas.drawText(text, -0.05f * width + 10, y + bounds.height() / 2.0f, paint);
421915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                canvas.drawText(text, x - bounds.width() / 2.0f,
422015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        height + bounds.height() + 16, paint);
422115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
422215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            paint.setStyle(Paint.Style.STROKE);
422315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
422415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            // Draw the diagram box
422515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            path.moveTo(0.0f, height);
422615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            path.lineTo(0.9f * width, height);
422715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            path.lineTo(0.9f * width, 0.1f * height);
422815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            path.lineTo(0.0f, 0.1f * height);
422915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            path.close();
423015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            canvas.drawPath(path, paint);
423115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
423215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
423315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
423415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Computes and applies the Canvas transforms required to make the color
423515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * gamut of each color space visible in the final image.
423615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
423715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param canvas The canvas to transform
423815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param width Width in pixel of the final image
423915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param height Height in pixel of the final image
424015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param primaries Array of 6 floats used to avoid temporary allocations
424115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
424215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private void setTransform(@NonNull Canvas canvas, int width, int height,
424315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                @NonNull @Size(6) float[] primaries) {
424415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
424515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            RectF primariesBounds = new RectF();
424615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            for (final Pair<ColorSpace, Integer> item : mColorSpaces) {
424715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                ColorSpace colorSpace = item.first;
424815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                if (colorSpace.getModel() != Model.RGB) continue;
424915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
425015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                Rgb rgb = (Rgb) colorSpace;
42519505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                getPrimaries(rgb, primaries, mUcs);
425215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
425315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                primariesBounds.left = Math.min(primariesBounds.left, primaries[4]);
425415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                primariesBounds.top = Math.min(primariesBounds.top, primaries[5]);
425515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                primariesBounds.right = Math.max(primariesBounds.right, primaries[0]);
425615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                primariesBounds.bottom = Math.max(primariesBounds.bottom, primaries[3]);
425715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
425815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
42599505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float max = mUcs ? 0.6f : 0.9f;
42609505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
426115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            primariesBounds.left = Math.min(0.0f, primariesBounds.left);
426215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            primariesBounds.top = Math.min(0.0f, primariesBounds.top);
42639505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            primariesBounds.right = Math.max(max, primariesBounds.right);
42649505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            primariesBounds.bottom = Math.max(max, primariesBounds.bottom);
426515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
42669505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float scaleX = max / primariesBounds.width();
42679505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            float scaleY = max / primariesBounds.height();
426815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            float scale = Math.min(scaleX, scaleY);
426915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
427015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            canvas.scale(mSize / (float) NATIVE_SIZE, mSize / (float) NATIVE_SIZE);
427115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            canvas.scale(scale, scale);
427215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            canvas.translate(
42739505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    (primariesBounds.width() - max) * width / 2.0f,
42749505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    (primariesBounds.height() - max) * height / 2.0f);
427515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
427615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            // The spectrum extends ~0.85 vertically and ~0.65 horizontally
427715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            // We shift the canvas a little bit to get nicer margins
427815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            canvas.translate(0.05f * width, -0.05f * height);
427915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
428015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
42819505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        /**
42829505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * Computes and applies the Canvas transforms required to render the CIE
42839505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * 197 UCS chromaticity diagram.
42849505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         *
42859505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * @param canvas The canvas to transform
42869505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         * @param height Height in pixel of the final image
42879505a6552764461c22ce48f1ac13d025d23e1579Romain Guy         */
42889505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        private void setUcsTransform(@NonNull Canvas canvas, int height) {
42899505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            if (mUcs) {
42909505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                canvas.translate(0.0f, (height - height * UCS_SCALE));
42919505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                canvas.scale(UCS_SCALE, UCS_SCALE);
42929505a6552764461c22ce48f1ac13d025d23e1579Romain Guy            }
42939505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        }
42949505a6552764461c22ce48f1ac13d025d23e1579Romain Guy
429515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        // X coordinates of the spectral locus in CIE 1931
429615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private static final float[] SPECTRUM_LOCUS_X = {
429715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.175596f, 0.172787f, 0.170806f, 0.170085f, 0.160343f,
429815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.146958f, 0.139149f, 0.133536f, 0.126688f, 0.115830f,
429915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.109616f, 0.099146f, 0.091310f, 0.078130f, 0.068717f,
430015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.054675f, 0.040763f, 0.027497f, 0.016270f, 0.008169f,
430115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.004876f, 0.003983f, 0.003859f, 0.004646f, 0.007988f,
430215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.013870f, 0.022244f, 0.027273f, 0.032820f, 0.038851f,
430315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.045327f, 0.052175f, 0.059323f, 0.066713f, 0.074299f,
430415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.089937f, 0.114155f, 0.138695f, 0.154714f, 0.192865f,
430515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.229607f, 0.265760f, 0.301588f, 0.337346f, 0.373083f,
430615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.408717f, 0.444043f, 0.478755f, 0.512467f, 0.544767f,
430715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.575132f, 0.602914f, 0.627018f, 0.648215f, 0.665746f,
430815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.680061f, 0.691487f, 0.700589f, 0.707901f, 0.714015f,
430915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.719017f, 0.723016f, 0.734674f, 0.717203f, 0.699732f,
431015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.682260f, 0.664789f, 0.647318f, 0.629847f, 0.612376f,
431115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.594905f, 0.577433f, 0.559962f, 0.542491f, 0.525020f,
431215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.507549f, 0.490077f, 0.472606f, 0.455135f, 0.437664f,
431315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.420193f, 0.402721f, 0.385250f, 0.367779f, 0.350308f,
431415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.332837f, 0.315366f, 0.297894f, 0.280423f, 0.262952f,
431515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.245481f, 0.228010f, 0.210538f, 0.193067f, 0.175596f
431615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        };
431715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        // Y coordinates of the spectral locus in CIE 1931
431815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private static final float[] SPECTRUM_LOCUS_Y = {
431915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.005295f, 0.004800f, 0.005472f, 0.005976f, 0.014496f,
432015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.026643f, 0.035211f, 0.042704f, 0.053441f, 0.073601f,
432115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.086866f, 0.112037f, 0.132737f, 0.170464f, 0.200773f,
432215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.254155f, 0.317049f, 0.387997f, 0.463035f, 0.538504f,
432315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.587196f, 0.610526f, 0.654897f, 0.675970f, 0.715407f,
432415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.750246f, 0.779682f, 0.792153f, 0.802971f, 0.812059f,
432515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.819430f, 0.825200f, 0.829460f, 0.832306f, 0.833833f,
432615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.833316f, 0.826231f, 0.814796f, 0.805884f, 0.781648f,
432715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.754347f, 0.724342f, 0.692326f, 0.658867f, 0.624470f,
432815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.589626f, 0.554734f, 0.520222f, 0.486611f, 0.454454f,
432915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.424252f, 0.396516f, 0.372510f, 0.351413f, 0.334028f,
433015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.319765f, 0.308359f, 0.299317f, 0.292044f, 0.285945f,
433115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.280951f, 0.276964f, 0.265326f, 0.257200f, 0.249074f,
433215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.240948f, 0.232822f, 0.224696f, 0.216570f, 0.208444f,
433315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.200318f, 0.192192f, 0.184066f, 0.175940f, 0.167814f,
433415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.159688f, 0.151562f, 0.143436f, 0.135311f, 0.127185f,
433515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.119059f, 0.110933f, 0.102807f, 0.094681f, 0.086555f,
433615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.078429f, 0.070303f, 0.062177f, 0.054051f, 0.045925f,
433715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                0.037799f, 0.029673f, 0.021547f, 0.013421f, 0.005295f
433815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        };
433915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
434015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        /**
434115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * Computes a 2D mesh representation of the CIE 1931 chromaticity
434215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * diagram.
434315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         *
434415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param vertices Array of floats that will hold the mesh vertices
434515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         * @param colors Array of floats that will hold the mesh colors
434615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy         */
43479505a6552764461c22ce48f1ac13d025d23e1579Romain Guy        private static void computeChromaticityMesh(@NonNull float[] vertices,
43489505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                @NonNull int[] colors) {
434915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
435015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            ColorSpace colorSpace = get(Named.SRGB);
435115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
435215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            float[] color = new float[3];
435315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
435415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            int vertexIndex = 0;
435515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            int colorIndex = 0;
435615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
435715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            for (int x = 0; x < SPECTRUM_LOCUS_X.length; x++) {
435815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                int nextX = (x % (SPECTRUM_LOCUS_X.length - 1)) + 1;
435915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
436015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                float a1 = (float) Math.atan2(
436115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        SPECTRUM_LOCUS_Y[x] - ONE_THIRD,
436215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        SPECTRUM_LOCUS_X[x] - ONE_THIRD);
436315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                float a2 = (float) Math.atan2(
436415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        SPECTRUM_LOCUS_Y[nextX] - ONE_THIRD,
436515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        SPECTRUM_LOCUS_X[nextX] - ONE_THIRD);
436615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
436715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                float radius1 = (float) Math.pow(
436815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        sqr(SPECTRUM_LOCUS_X[x] - ONE_THIRD) +
436915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                                sqr(SPECTRUM_LOCUS_Y[x] - ONE_THIRD),
437015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        0.5);
437115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                float radius2 = (float) Math.pow(
437215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        sqr(SPECTRUM_LOCUS_X[nextX] - ONE_THIRD) +
437315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                                sqr(SPECTRUM_LOCUS_Y[nextX] - ONE_THIRD),
437415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                        0.5);
437515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
437615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                // Compute patches; each patch is a quad with a different
437715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                // color associated with each vertex
437815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                for (int c = 1; c <= CHROMATICITY_RESOLUTION; c++) {
437915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float f1 = c / (float) CHROMATICITY_RESOLUTION;
438015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float f2 = (c - 1) / (float) CHROMATICITY_RESOLUTION;
438115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
438215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    double cr1 = radius1 * Math.cos(a1);
438315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    double sr1 = radius1 * Math.sin(a1);
438415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    double cr2 = radius2 * Math.cos(a2);
438515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    double sr2 = radius2 * Math.sin(a2);
438615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
438715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    // Compute the XYZ coordinates of the 4 vertices of the patch
438815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v1x = (float) (ONE_THIRD + cr1 * f1);
438915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v1y = (float) (ONE_THIRD + sr1 * f1);
439015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v1z = 1 - v1x - v1y;
439115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
439215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v2x = (float) (ONE_THIRD + cr1 * f2);
439315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v2y = (float) (ONE_THIRD + sr1 * f2);
439415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v2z = 1 - v2x - v2y;
439515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
439615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v3x = (float) (ONE_THIRD + cr2 * f2);
439715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v3y = (float) (ONE_THIRD + sr2 * f2);
439815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v3z = 1 - v3x - v3y;
439915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
440015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v4x = (float) (ONE_THIRD + cr2 * f1);
440115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v4y = (float) (ONE_THIRD + sr2 * f1);
440215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    float v4z = 1 - v4x - v4y;
440315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
440415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    // Compute the sRGB representation of each XYZ coordinate of the patch
440515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    colors[colorIndex    ] = computeColor(color, v1x, v1y, v1z, colorSpace);
440615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    colors[colorIndex + 1] = computeColor(color, v2x, v2y, v2z, colorSpace);
440715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    colors[colorIndex + 2] = computeColor(color, v3x, v3y, v3z, colorSpace);
440815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    colors[colorIndex + 3] = colors[colorIndex];
440915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    colors[colorIndex + 4] = colors[colorIndex + 2];
441015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    colors[colorIndex + 5] = computeColor(color, v4x, v4y, v4z, colorSpace);
441115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    colorIndex += 6;
441215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
441315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    // Flip the mesh upside down to match Canvas' coordinates system
44149505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v1x;
44159505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v1y;
44169505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v2x;
44179505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v2y;
44189505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v3x;
44199505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v3y;
44209505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v1x;
44219505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v1y;
44229505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v3x;
44239505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v3y;
44249505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v4x;
44259505a6552764461c22ce48f1ac13d025d23e1579Romain Guy                    vertices[vertexIndex++] = v4y;
442615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                }
442715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
442815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
442915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
443015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        @ColorInt
443115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private static int computeColor(@NonNull @Size(3) float[] color,
443215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                float x, float y, float z, @NonNull ColorSpace cs) {
443315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            color[0] = x;
443415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            color[1] = y;
443515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            color[2] = z;
443615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            cs.fromXyz(color);
443715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            return 0xff000000 |
443815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    (((int) (color[0] * 255.0f) & 0xff) << 16) |
443915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    (((int) (color[1] * 255.0f) & 0xff) <<  8) |
444015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    (((int) (color[2] * 255.0f) & 0xff)      );
444115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
444215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
444315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private static double sqr(double v) {
444415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            return v * v;
444515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
444615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
444715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        private static class Point {
444815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            @NonNull final ColorSpace mColorSpace;
444915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            @NonNull final float[] mRgb;
445015296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            final int mColor;
445115296a2d3478f53402e2d98f49724bb791eb339dRomain Guy
445215296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            Point(@NonNull ColorSpace colorSpace,
445315296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                    @NonNull @Size(3) float[] rgb, @ColorInt int color) {
445415296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                mColorSpace = colorSpace;
445515296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                mRgb = rgb;
445615296a2d3478f53402e2d98f49724bb791eb339dRomain Guy                mColor = color;
445715296a2d3478f53402e2d98f49724bb791eb339dRomain Guy            }
445815296a2d3478f53402e2d98f49724bb791eb339dRomain Guy        }
445915296a2d3478f53402e2d98f49724bb791eb339dRomain Guy    }
446068bd5fdd1ad3cf0b74c225b31adf1f68393bfbb6Romain Guy}
4461