Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4086386b authored by Nader Jawad's avatar Nader Jawad
Browse files

oklab support

Add the oklab colorspace. Jetpack Compose already defines this
ColorSpace and is used internally to provide higher quality
gradients by converting colors to Oklab as an intermediate
step.

Fixes: 344038816
FLAG: com.android.graphics.flags.ok_lab_colorspace
Test: Added tests to ColorSpaceTest

Change-Id: I5a085e0e0865c4b8becda493b6c3ad5cce51d87e
parent 056e11e8
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -15974,6 +15974,7 @@ package android.graphics {
    enum_constant public static final android.graphics.ColorSpace.Named LINEAR_EXTENDED_SRGB;
    enum_constant public static final android.graphics.ColorSpace.Named LINEAR_EXTENDED_SRGB;
    enum_constant public static final android.graphics.ColorSpace.Named LINEAR_SRGB;
    enum_constant public static final android.graphics.ColorSpace.Named LINEAR_SRGB;
    enum_constant public static final android.graphics.ColorSpace.Named NTSC_1953;
    enum_constant public static final android.graphics.ColorSpace.Named NTSC_1953;
    enum_constant @FlaggedApi("com.android.graphics.flags.ok_lab_colorspace") public static final android.graphics.ColorSpace.Named OK_LAB;
    enum_constant public static final android.graphics.ColorSpace.Named PRO_PHOTO_RGB;
    enum_constant public static final android.graphics.ColorSpace.Named PRO_PHOTO_RGB;
    enum_constant public static final android.graphics.ColorSpace.Named SMPTE_C;
    enum_constant public static final android.graphics.ColorSpace.Named SMPTE_C;
    enum_constant public static final android.graphics.ColorSpace.Named SRGB;
    enum_constant public static final android.graphics.ColorSpace.Named SRGB;
+9 −0
Original line number Original line Diff line number Diff line
@@ -24,3 +24,12 @@ flag {
    description: "Return null when decode from URI fails in Icon.loadDrawable()"
    description: "Return null when decode from URI fails in Icon.loadDrawable()"
    bug: "335878768"
    bug: "335878768"
}
}

flag {
  name: "ok_lab_colorspace"
  is_exported: true
  is_fixed_read_only: true
  namespace: "core_graphics"
  description: "Add OkLab ColorSpace support"
  bug: "344038816"
}
+179 −47
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package android.graphics;
package android.graphics;


import android.annotation.AnyThread;
import android.annotation.AnyThread;
import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
@@ -27,9 +28,13 @@ import android.hardware.DataSpace;
import android.hardware.DataSpace.ColorDataSpace;
import android.hardware.DataSpace.ColorDataSpace;
import android.util.SparseIntArray;
import android.util.SparseIntArray;


import com.android.graphics.flags.Flags;

import libcore.util.NativeAllocationRegistry;
import libcore.util.NativeAllocationRegistry;


import java.util.Arrays;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.function.DoubleUnaryOperator;
import java.util.function.DoubleUnaryOperator;


/**
/**
@@ -230,7 +235,9 @@ public abstract class ColorSpace {
                    -2392 / 128.0, 8192 / 1305.0, Rgb.TransferParameters.TYPE_PQish);
                    -2392 / 128.0, 8192 / 1305.0, Rgb.TransferParameters.TYPE_PQish);


    // See static initialization block next to #get(Named)
    // See static initialization block next to #get(Named)
    private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length];
    private static final HashMap<Integer, ColorSpace> sNamedColorSpaceMap =
            new HashMap<>();

    private static final SparseIntArray sDataToColorSpaces = new SparseIntArray();
    private static final SparseIntArray sDataToColorSpaces = new SparseIntArray();


    @NonNull private final String mName;
    @NonNull private final String mName;
@@ -745,7 +752,23 @@ public abstract class ColorSpace {
         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
         * </table>
         * </table>
         */
         */
        BT2020_PQ
        BT2020_PQ,

        /**
         * <p>{@link ColorSpace.Lab Lab} color space OkLab standardized as
         * OkLab</p>
         * <table summary="Color space definition">
         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
         *     <tr><td>Name</td><td colspan="4">Oklab</td></tr>
         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
         *     <tr>
         *         <td>Range</td>
         *         <td colspan="4">\(L: `[0.0, 1.0]`, a: `[-2, 2]`, b: `[-2, 2]`\)</td>
         *     </tr>
         * </table>
         */
        @FlaggedApi(Flags.FLAG_OK_LAB_COLORSPACE)
        OK_LAB
        // Update the initialization block next to #get(Named) when adding new values
        // Update the initialization block next to #get(Named) when adding new values
    }
    }


@@ -1428,11 +1451,11 @@ public abstract class ColorSpace {
     */
     */
    @NonNull
    @NonNull
    static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) {
    static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) {
        if (index < 0 || index >= sNamedColorSpaces.length) {
        ColorSpace colorspace = sNamedColorSpaceMap.get(index);
            throw new IllegalArgumentException("Invalid ID, must be in the range [0.." +
        if (colorspace == null) {
                    sNamedColorSpaces.length + ")");
            throw new IllegalArgumentException("Invalid ID: " + index);
        }
        }
        return sNamedColorSpaces[index];
        return colorspace;
    }
    }


    /**
    /**
@@ -1485,12 +1508,20 @@ public abstract class ColorSpace {
     *
     *
     * <p>This method is thread-safe.</p>
     * <p>This method is thread-safe.</p>
     *
     *
     * Note that in the Android W release and later, this can return the SRGB ColorSpace if
     * the {@link ColorSpace.Named} parameter is not enabled in the corresponding release.
     *
     * @param name The name of the color space to get an instance of
     * @param name The name of the color space to get an instance of
     * @return A non-null {@link ColorSpace} instance
     * @return A non-null {@link ColorSpace} instance. If the ColorSpace is not supported
     * then the SRGB ColorSpace is returned.
     */
     */
    @NonNull
    @NonNull
    public static ColorSpace get(@NonNull Named name) {
    public static ColorSpace get(@NonNull Named name) {
        return sNamedColorSpaces[name.ordinal()];
        ColorSpace colorSpace = sNamedColorSpaceMap.get(name.ordinal());
        if (colorSpace == null) {
            return sNamedColorSpaceMap.get(Named.SRGB.ordinal());
        }
        return colorSpace;
    }
    }


    /**
    /**
@@ -1511,7 +1542,8 @@ public abstract class ColorSpace {
            @NonNull @Size(9) float[] toXYZD50,
            @NonNull @Size(9) float[] toXYZD50,
            @NonNull Rgb.TransferParameters function) {
            @NonNull Rgb.TransferParameters function) {


        for (ColorSpace colorSpace : sNamedColorSpaces) {
        Collection<ColorSpace> colorspaces = sNamedColorSpaceMap.values();
        for (ColorSpace colorSpace : colorspaces) {
            if (colorSpace.getModel() == Model.RGB) {
            if (colorSpace.getModel() == Model.RGB) {
                ColorSpace.Rgb rgb = (ColorSpace.Rgb) adapt(colorSpace, ILLUMINANT_D50_XYZ);
                ColorSpace.Rgb rgb = (ColorSpace.Rgb) adapt(colorSpace, ILLUMINANT_D50_XYZ);
                if (compare(toXYZD50, rgb.mTransform) &&
                if (compare(toXYZD50, rgb.mTransform) &&
@@ -1525,25 +1557,25 @@ public abstract class ColorSpace {
    }
    }


    static {
    static {
        sNamedColorSpaces[Named.SRGB.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.SRGB.ordinal(), new ColorSpace.Rgb(
                "sRGB IEC61966-2.1",
                "sRGB IEC61966-2.1",
                SRGB_PRIMARIES,
                SRGB_PRIMARIES,
                ILLUMINANT_D65,
                ILLUMINANT_D65,
                null,
                null,
                SRGB_TRANSFER_PARAMETERS,
                SRGB_TRANSFER_PARAMETERS,
                Named.SRGB.ordinal()
                Named.SRGB.ordinal()
        );
        ));
        sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB, Named.SRGB.ordinal());
        sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB, Named.SRGB.ordinal());
        sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.LINEAR_SRGB.ordinal(), new ColorSpace.Rgb(
                "sRGB IEC61966-2.1 (Linear)",
                "sRGB IEC61966-2.1 (Linear)",
                SRGB_PRIMARIES,
                SRGB_PRIMARIES,
                ILLUMINANT_D65,
                ILLUMINANT_D65,
                1.0,
                1.0,
                0.0f, 1.0f,
                0.0f, 1.0f,
                Named.LINEAR_SRGB.ordinal()
                Named.LINEAR_SRGB.ordinal()
        );
        ));
        sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB_LINEAR, Named.LINEAR_SRGB.ordinal());
        sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB_LINEAR, Named.LINEAR_SRGB.ordinal());
        sNamedColorSpaces[Named.EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.EXTENDED_SRGB.ordinal(), new ColorSpace.Rgb(
                "scRGB-nl IEC 61966-2-2:2003",
                "scRGB-nl IEC 61966-2-2:2003",
                SRGB_PRIMARIES,
                SRGB_PRIMARIES,
                ILLUMINANT_D65,
                ILLUMINANT_D65,
@@ -1553,112 +1585,113 @@ public abstract class ColorSpace {
                -0.799f, 2.399f,
                -0.799f, 2.399f,
                SRGB_TRANSFER_PARAMETERS,
                SRGB_TRANSFER_PARAMETERS,
                Named.EXTENDED_SRGB.ordinal()
                Named.EXTENDED_SRGB.ordinal()
        );
        ));
        sDataToColorSpaces.put(DataSpace.DATASPACE_SCRGB, Named.EXTENDED_SRGB.ordinal());
        sDataToColorSpaces.put(DataSpace.DATASPACE_SCRGB, Named.EXTENDED_SRGB.ordinal());
        sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.LINEAR_EXTENDED_SRGB.ordinal(), new ColorSpace.Rgb(
                "scRGB IEC 61966-2-2:2003",
                "scRGB IEC 61966-2-2:2003",
                SRGB_PRIMARIES,
                SRGB_PRIMARIES,
                ILLUMINANT_D65,
                ILLUMINANT_D65,
                1.0,
                1.0,
                -0.5f, 7.499f,
                -0.5f, 7.499f,
                Named.LINEAR_EXTENDED_SRGB.ordinal()
                Named.LINEAR_EXTENDED_SRGB.ordinal()
        );
        ));
        sDataToColorSpaces.put(
        sDataToColorSpaces.put(
                DataSpace.DATASPACE_SCRGB_LINEAR, Named.LINEAR_EXTENDED_SRGB.ordinal());
                DataSpace.DATASPACE_SCRGB_LINEAR, Named.LINEAR_EXTENDED_SRGB.ordinal());
        sNamedColorSpaces[Named.BT709.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.BT709.ordinal(), new ColorSpace.Rgb(
                "Rec. ITU-R BT.709-5",
                "Rec. ITU-R BT.709-5",
                SRGB_PRIMARIES,
                SRGB_PRIMARIES,
                ILLUMINANT_D65,
                ILLUMINANT_D65,
                null,
                null,
                SMPTE_170M_TRANSFER_PARAMETERS,
                SMPTE_170M_TRANSFER_PARAMETERS,
                Named.BT709.ordinal()
                Named.BT709.ordinal()
        );
        ));
        sDataToColorSpaces.put(DataSpace.DATASPACE_BT709, Named.BT709.ordinal());
        sDataToColorSpaces.put(DataSpace.DATASPACE_BT709, Named.BT709.ordinal());
        sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.BT2020.ordinal(), new ColorSpace.Rgb(
                "Rec. ITU-R BT.2020-1",
                "Rec. ITU-R BT.2020-1",
                BT2020_PRIMARIES,
                BT2020_PRIMARIES,
                ILLUMINANT_D65,
                ILLUMINANT_D65,
                null,
                null,
                new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45),
                new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45),
                Named.BT2020.ordinal()
                Named.BT2020.ordinal()
        );
        ));

        sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020, Named.BT2020.ordinal());
        sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020, Named.BT2020.ordinal());
        sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.DCI_P3.ordinal(), new ColorSpace.Rgb(
                "SMPTE RP 431-2-2007 DCI (P3)",
                "SMPTE RP 431-2-2007 DCI (P3)",
                DCI_P3_PRIMARIES,
                DCI_P3_PRIMARIES,
                new float[] { 0.314f, 0.351f },
                new float[] { 0.314f, 0.351f },
                2.6,
                2.6,
                0.0f, 1.0f,
                0.0f, 1.0f,
                Named.DCI_P3.ordinal()
                Named.DCI_P3.ordinal()
        );
        ));
        sDataToColorSpaces.put(DataSpace.DATASPACE_DCI_P3, Named.DCI_P3.ordinal());
        sDataToColorSpaces.put(DataSpace.DATASPACE_DCI_P3, Named.DCI_P3.ordinal());
        sNamedColorSpaces[Named.DISPLAY_P3.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.DISPLAY_P3.ordinal(), new ColorSpace.Rgb(
                "Display P3",
                "Display P3",
                DCI_P3_PRIMARIES,
                DCI_P3_PRIMARIES,
                ILLUMINANT_D65,
                ILLUMINANT_D65,
                null,
                null,
                SRGB_TRANSFER_PARAMETERS,
                SRGB_TRANSFER_PARAMETERS,
                Named.DISPLAY_P3.ordinal()
                Named.DISPLAY_P3.ordinal()
        );
        ));
        sDataToColorSpaces.put(DataSpace.DATASPACE_DISPLAY_P3, Named.DISPLAY_P3.ordinal());
        sDataToColorSpaces.put(DataSpace.DATASPACE_DISPLAY_P3, Named.DISPLAY_P3.ordinal());
        sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.NTSC_1953.ordinal(), new ColorSpace.Rgb(
                "NTSC (1953)",
                "NTSC (1953)",
                NTSC_1953_PRIMARIES,
                NTSC_1953_PRIMARIES,
                ILLUMINANT_C,
                ILLUMINANT_C,
                null,
                null,
                SMPTE_170M_TRANSFER_PARAMETERS,
                SMPTE_170M_TRANSFER_PARAMETERS,
                Named.NTSC_1953.ordinal()
                Named.NTSC_1953.ordinal()
        );
        ));
        sNamedColorSpaces[Named.SMPTE_C.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.SMPTE_C.ordinal(), new ColorSpace.Rgb(
                "SMPTE-C RGB",
                "SMPTE-C RGB",
                new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f },
                new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f },
                ILLUMINANT_D65,
                ILLUMINANT_D65,
                null,
                null,
                SMPTE_170M_TRANSFER_PARAMETERS,
                SMPTE_170M_TRANSFER_PARAMETERS,
                Named.SMPTE_C.ordinal()
                Named.SMPTE_C.ordinal()
        );
        ));
        sNamedColorSpaces[Named.ADOBE_RGB.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.ADOBE_RGB.ordinal(), new ColorSpace.Rgb(
                "Adobe RGB (1998)",
                "Adobe RGB (1998)",
                new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f },
                new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f },
                ILLUMINANT_D65,
                ILLUMINANT_D65,
                2.2,
                2.2,
                0.0f, 1.0f,
                0.0f, 1.0f,
                Named.ADOBE_RGB.ordinal()
                Named.ADOBE_RGB.ordinal()
        );
        ));
        sDataToColorSpaces.put(DataSpace.DATASPACE_ADOBE_RGB, Named.ADOBE_RGB.ordinal());
        sDataToColorSpaces.put(DataSpace.DATASPACE_ADOBE_RGB, Named.ADOBE_RGB.ordinal());
        sNamedColorSpaces[Named.PRO_PHOTO_RGB.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.PRO_PHOTO_RGB.ordinal(), new ColorSpace.Rgb(
                "ROMM RGB ISO 22028-2:2013",
                "ROMM RGB ISO 22028-2:2013",
                new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
                new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
                ILLUMINANT_D50,
                ILLUMINANT_D50,
                null,
                null,
                new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8),
                new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8),
                Named.PRO_PHOTO_RGB.ordinal()
                Named.PRO_PHOTO_RGB.ordinal()
        );
        ));
        sNamedColorSpaces[Named.ACES.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.ACES.ordinal(), new ColorSpace.Rgb(
                "SMPTE ST 2065-1:2012 ACES",
                "SMPTE ST 2065-1:2012 ACES",
                new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f },
                new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f },
                ILLUMINANT_D60,
                ILLUMINANT_D60,
                1.0,
                1.0,
                -65504.0f, 65504.0f,
                -65504.0f, 65504.0f,
                Named.ACES.ordinal()
                Named.ACES.ordinal()
        );
        ));
        sNamedColorSpaces[Named.ACESCG.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.ACESCG.ordinal(), new ColorSpace.Rgb(
                "Academy S-2014-004 ACEScg",
                "Academy S-2014-004 ACEScg",
                new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f },
                new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f },
                ILLUMINANT_D60,
                ILLUMINANT_D60,
                1.0,
                1.0,
                -65504.0f, 65504.0f,
                -65504.0f, 65504.0f,
                Named.ACESCG.ordinal()
                Named.ACESCG.ordinal()
        );
        ));
        sNamedColorSpaces[Named.CIE_XYZ.ordinal()] = new Xyz(
        sNamedColorSpaceMap.put(Named.CIE_XYZ.ordinal(), new Xyz(
                "Generic XYZ",
                "Generic XYZ",
                Named.CIE_XYZ.ordinal()
                Named.CIE_XYZ.ordinal()
        );
        ));
        sNamedColorSpaces[Named.CIE_LAB.ordinal()] = new ColorSpace.Lab(
        sNamedColorSpaceMap.put(Named.CIE_LAB.ordinal(), new ColorSpace.Lab(
                "Generic L*a*b*",
                "Generic L*a*b*",
                Named.CIE_LAB.ordinal()
                Named.CIE_LAB.ordinal()
        );
        ));
        sNamedColorSpaces[Named.BT2020_HLG.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.BT2020_HLG.ordinal(), new ColorSpace.Rgb(
                "Hybrid Log Gamma encoding",
                "Hybrid Log Gamma encoding",
                BT2020_PRIMARIES,
                BT2020_PRIMARIES,
                ILLUMINANT_D65,
                ILLUMINANT_D65,
@@ -1668,9 +1701,9 @@ public abstract class ColorSpace {
                0.0f, 1.0f,
                0.0f, 1.0f,
                BT2020_HLG_TRANSFER_PARAMETERS,
                BT2020_HLG_TRANSFER_PARAMETERS,
                Named.BT2020_HLG.ordinal()
                Named.BT2020_HLG.ordinal()
        );
        ));
        sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_HLG, Named.BT2020_HLG.ordinal());
        sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_HLG, Named.BT2020_HLG.ordinal());
        sNamedColorSpaces[Named.BT2020_PQ.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.BT2020_PQ.ordinal(), new ColorSpace.Rgb(
                "Perceptual Quantizer encoding",
                "Perceptual Quantizer encoding",
                BT2020_PRIMARIES,
                BT2020_PRIMARIES,
                ILLUMINANT_D65,
                ILLUMINANT_D65,
@@ -1680,8 +1713,14 @@ public abstract class ColorSpace {
                0.0f, 1.0f,
                0.0f, 1.0f,
                BT2020_PQ_TRANSFER_PARAMETERS,
                BT2020_PQ_TRANSFER_PARAMETERS,
                Named.BT2020_PQ.ordinal()
                Named.BT2020_PQ.ordinal()
        );
        ));
        sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_PQ, Named.BT2020_PQ.ordinal());
        sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_PQ, Named.BT2020_PQ.ordinal());
        if (Flags.okLabColorspace()) {
            sNamedColorSpaceMap.put(Named.OK_LAB.ordinal(), new ColorSpace.OkLab(
                    "Oklab",
                    Named.OK_LAB.ordinal()
            ));
        }
    }
    }


    private static double transferHLGOETF(Rgb.TransferParameters params, double x) {
    private static double transferHLGOETF(Rgb.TransferParameters params, double x) {
@@ -2142,10 +2181,103 @@ public abstract class ColorSpace {


            return v;
            return v;
        }
        }
    }


    private static float clamp(float x, float min, float max) {
    private static float clamp(float x, float min, float max) {
        return x < min ? min : x > max ? max : x;
        return x < min ? min : x > max ? max : x;
    }
    }

    /**
     * Implementation of the Oklab color space. Oklab uses a D65 white point.
     */
    @AnyThread
    private static final class OkLab extends ColorSpace {

        private OkLab(@NonNull String name, @IntRange(from = MIN_ID, to = MAX_ID) int id) {
            super(name, Model.LAB, id);
        }

        @Override
        public boolean isWideGamut() {
            return true;
        }

        @Override
        public float getMinValue(@IntRange(from = 0, to = 3) int component) {
            return component == 0 ? 0.0f : -0.5f;
        }

        @Override
        public float getMaxValue(@IntRange(from = 0, to = 3) int component) {
            return component == 0 ? 1.0f : 0.5f;
        }

        @Override
        public float[] toXyz(@NonNull @Size(min = 3) float[] v) {
            v[0] = clamp(v[0], 0.0f, 1.0f);
            v[1] = clamp(v[1], -0.5f, 0.5f);
            v[2] = clamp(v[2], -0.5f, 0.5f);

            mul3x3Float3(INVERSE_M2, v);
            v[0] = v[0] * v[0] * v[0];
            v[1] = v[1] * v[1] * v[1];
            v[2] = v[2] * v[2] * v[2];

            mul3x3Float3(INVERSE_M1, v);

            return v;
        }

        @Override
        public float[] fromXyz(@NonNull @Size(min = 3) float[] v) {
            mul3x3Float3(M1, v);

            v[0] = (float) Math.cbrt(v[0]);
            v[1] = (float) Math.cbrt(v[1]);
            v[2] = (float) Math.cbrt(v[2]);

            mul3x3Float3(M2, v);
            return v;
        }

        /**
         * Temp array used as input to compute M1 below
         */
        private static final float[] M1TMP = {
                0.8189330101f, 0.0329845436f, 0.0482003018f,
                0.3618667424f, 0.9293118715f, 0.2643662691f,
                -0.1288597137f, 0.0361456387f, 0.6338517070f
        };

        /**
         * This is the matrix applied before the nonlinear transform for (D50) XYZ-to-Oklab.
         * This combines the D50-to-D65 white point transform with the normal transform matrix
         * because this is always done together in [fromXyz].
         */
        private static final float[] M1 = mul3x3(
                M1TMP,
                chromaticAdaptation(Adaptation.BRADFORD, ILLUMINANT_D50, ILLUMINANT_D65)
        );

        /**
         * Matrix applied after the nonlinear transform.
         */
        private static final float[] M2 = {
                0.2104542553f, 1.9779984951f, 0.0259040371f,
                0.7936177850f, -2.4285922050f, 0.7827717662f,
                -0.0040720468f, 0.4505937099f, -0.8086757660f
        };

        /**
         * The inverse of the [M1] matrix, transforming back to XYZ (D50)
         */
        private static final float[] INVERSE_M1 = inverse3x3(M1);

        /**
         * The inverse of the [M2] matrix, doing the first linear transform in the
         * Oklab-to-XYZ before doing the nonlinear transform.
         */
        private static final float[] INVERSE_M2 = inverse3x3(M2);
    }
    }


    /**
    /**