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

Commit 7e77b567 authored by Nader Jawad's avatar Nader Jawad Committed by Android (Google) Code Review
Browse files

Merge "oklab support" into main

parents 39b9bf57 4086386b
Loading
Loading
Loading
Loading
+1 −0
Original line number 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_SRGB;
    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 SMPTE_C;
    enum_constant public static final android.graphics.ColorSpace.Named SRGB;
+9 −0
Original line number Diff line number Diff line
@@ -24,3 +24,12 @@ flag {
    description: "Return null when decode from URI fails in Icon.loadDrawable()"
    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 Diff line number Diff line
@@ -17,6 +17,7 @@
package android.graphics;

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

import com.android.graphics.flags.Flags;

import libcore.util.NativeAllocationRegistry;

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

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

    // 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();

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

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

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

    static {
        sNamedColorSpaces[Named.SRGB.ordinal()] = new ColorSpace.Rgb(
        sNamedColorSpaceMap.put(Named.SRGB.ordinal(), new ColorSpace.Rgb(
                "sRGB IEC61966-2.1",
                SRGB_PRIMARIES,
                ILLUMINANT_D65,
                null,
                SRGB_TRANSFER_PARAMETERS,
                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_PRIMARIES,
                ILLUMINANT_D65,
                1.0,
                0.0f, 1.0f,
                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",
                SRGB_PRIMARIES,
                ILLUMINANT_D65,
@@ -1553,112 +1585,113 @@ public abstract class ColorSpace {
                -0.799f, 2.399f,
                SRGB_TRANSFER_PARAMETERS,
                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",
                SRGB_PRIMARIES,
                ILLUMINANT_D65,
                1.0,
                -0.5f, 7.499f,
                Named.LINEAR_EXTENDED_SRGB.ordinal()
        );
        ));
        sDataToColorSpaces.put(
                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",
                SRGB_PRIMARIES,
                ILLUMINANT_D65,
                null,
                SMPTE_170M_TRANSFER_PARAMETERS,
                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",
                BT2020_PRIMARIES,
                ILLUMINANT_D65,
                null,
                new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45),
                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)",
                DCI_P3_PRIMARIES,
                new float[] { 0.314f, 0.351f },
                2.6,
                0.0f, 1.0f,
                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",
                DCI_P3_PRIMARIES,
                ILLUMINANT_D65,
                null,
                SRGB_TRANSFER_PARAMETERS,
                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_PRIMARIES,
                ILLUMINANT_C,
                null,
                SMPTE_170M_TRANSFER_PARAMETERS,
                Named.NTSC_1953.ordinal()
        );
        sNamedColorSpaces[Named.SMPTE_C.ordinal()] = new ColorSpace.Rgb(
        ));
        sNamedColorSpaceMap.put(Named.SMPTE_C.ordinal(), new ColorSpace.Rgb(
                "SMPTE-C RGB",
                new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f },
                ILLUMINANT_D65,
                null,
                SMPTE_170M_TRANSFER_PARAMETERS,
                Named.SMPTE_C.ordinal()
        );
        sNamedColorSpaces[Named.ADOBE_RGB.ordinal()] = new ColorSpace.Rgb(
        ));
        sNamedColorSpaceMap.put(Named.ADOBE_RGB.ordinal(), new ColorSpace.Rgb(
                "Adobe RGB (1998)",
                new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f },
                ILLUMINANT_D65,
                2.2,
                0.0f, 1.0f,
                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",
                new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
                ILLUMINANT_D50,
                null,
                new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8),
                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",
                new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f },
                ILLUMINANT_D60,
                1.0,
                -65504.0f, 65504.0f,
                Named.ACES.ordinal()
        );
        sNamedColorSpaces[Named.ACESCG.ordinal()] = new ColorSpace.Rgb(
        ));
        sNamedColorSpaceMap.put(Named.ACESCG.ordinal(), new ColorSpace.Rgb(
                "Academy S-2014-004 ACEScg",
                new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f },
                ILLUMINANT_D60,
                1.0,
                -65504.0f, 65504.0f,
                Named.ACESCG.ordinal()
        );
        sNamedColorSpaces[Named.CIE_XYZ.ordinal()] = new Xyz(
        ));
        sNamedColorSpaceMap.put(Named.CIE_XYZ.ordinal(), new Xyz(
                "Generic XYZ",
                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*",
                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",
                BT2020_PRIMARIES,
                ILLUMINANT_D65,
@@ -1668,9 +1701,9 @@ public abstract class ColorSpace {
                0.0f, 1.0f,
                BT2020_HLG_TRANSFER_PARAMETERS,
                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",
                BT2020_PRIMARIES,
                ILLUMINANT_D65,
@@ -1680,8 +1713,14 @@ public abstract class ColorSpace {
                0.0f, 1.0f,
                BT2020_PQ_TRANSFER_PARAMETERS,
                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) {
@@ -2142,10 +2181,103 @@ public abstract class ColorSpace {

            return v;
        }
    }

    private static float clamp(float x, float min, float max) {
        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);
    }

    /**