ColorSpaces.kt

/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

@file:Suppress("NOTHING_TO_INLINE")

package androidx.compose.ui.graphics.colorspace

object ColorSpaces {
    internal val SrgbPrimaries = floatArrayOf(0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f)
    internal val Ntsc1953Primaries = floatArrayOf(0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f)
    internal val SrgbTransferParameters =
        TransferParameters(2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045)
    private val NoneTransferParameters =
        TransferParameters(2.2, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045)

    /**
     * [RGB][Rgb] color space sRGB standardized as IEC 61966-2.1:1999.
     * [See details on sRGB color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#SRGB)
     */
    val Srgb = Rgb(
        "sRGB IEC61966-2.1",
        SrgbPrimaries,
        Illuminant.D65,
        SrgbTransferParameters,
        id = 0
    )

    /**
     * [RGB][Rgb] color space sRGB standardized as IEC 61966-2.1:1999.
     * [See details on Linear sRGB color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#LINEAR_SRGB)
     */
    val LinearSrgb = Rgb(
        "sRGB IEC61966-2.1 (Linear)",
        SrgbPrimaries,
        Illuminant.D65,
        1.0,
        0.0f, 1.0f,
        id = 1
    )

    /**
     * [RGB][Rgb] color space scRGB-nl standardized as IEC 61966-2-2:2003.
     * [See details on Extended sRGB color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#EXTENDED_SRGB)
     */
    val ExtendedSrgb = Rgb(
        "scRGB-nl IEC 61966-2-2:2003",
        SrgbPrimaries,
        Illuminant.D65, null,
        { x ->
            absRcpResponse(
                x,
                1 / 1.055,
                0.055 / 1.055,
                1 / 12.92,
                0.04045,
                2.4
            )
        },
        { x ->
            absResponse(
                x,
                1 / 1.055,
                0.055 / 1.055,
                1 / 12.92,
                0.04045,
                2.4
            )
        },
        -0.799f, 2.399f, SrgbTransferParameters,
        id = 2
    )

    /**
     * [RGB][Rgb] color space scRGB standardized as IEC 61966-2-2:2003.
     * [See details on Linear Extended sRGB color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#LINEAR_EXTENDED_SRGB)
     */
    val LinearExtendedSrgb = Rgb(
        "scRGB IEC 61966-2-2:2003",
        SrgbPrimaries,
        Illuminant.D65,
        1.0,
        -0.5f, 7.499f,
        id = 3
    )

    /**
     * [RGB][Rgb] color space BT.709 standardized as Rec. ITU-R BT.709-5.
     * [See details on BT.709 color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#BT_709)
     */
    val Bt709 = Rgb(
        "Rec. ITU-R BT.709-5",
        floatArrayOf(0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f),
        Illuminant.D65,
        TransferParameters(1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
        id = 4
    )

    /**
     * [RGB][Rgb] color space BT.2020 standardized as Rec. ITU-R BT.2020-1.
     * [See details on BT.2020 color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#BT_2020)
     */
    val Bt2020 = Rgb(
        "Rec. ITU-R BT.2020-1",
        floatArrayOf(0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f),
        Illuminant.D65,
        TransferParameters(1 / 0.45, 1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145),
        id = 5
    )

    /**
     * [RGB][Rgb] color space DCI-P3 standardized as SMPTE RP 431-2-2007.
     * [See details on DCI-P3 color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#DCI_P3)
     */
    val DciP3 = Rgb(
        "SMPTE RP 431-2-2007 DCI (P3)",
        floatArrayOf(0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f),
        WhitePoint(0.314f, 0.351f),
        2.6,
        0.0f, 1.0f,
        id = 6
    )

    /**
     * [RGB][Rgb] color space Display P3 based on SMPTE RP 431-2-2007 and IEC 61966-2.1:1999.
     * [See details on Display P3 color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#DISPLAY_P3)
     */
    val DisplayP3 = Rgb(
        "Display P3",
        floatArrayOf(0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f),
        Illuminant.D65,
        SrgbTransferParameters,
        id = 7
    )

    /**
     * [RGB][Rgb] color space NTSC, 1953 standard.
     * [See details on NTSC 1953 color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#NTSC_1953)
     */
    val Ntsc1953 = Rgb(
        "NTSC (1953)",
        Ntsc1953Primaries,
        Illuminant.C,
        TransferParameters(1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
        id = 8
    )

    /**
     * [RGB][Rgb] color space SMPTE C.
     * [See details on SMPTE C color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#SMPTE_C)
     */
    val SmpteC = Rgb(
        "SMPTE-C RGB",
        floatArrayOf(0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f),
        Illuminant.D65,
        TransferParameters(1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
        id = 9
    )

    /**
     * [RGB][Rgb] color space Adobe RGB (1998).
     * [See details on Adobe RGB (1998) color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#ADOBE_RGB)
     */
    val AdobeRgb = Rgb(
        "Adobe RGB (1998)",
        floatArrayOf(0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f),
        Illuminant.D65,
        2.2,
        0.0f, 1.0f,
        id = 10
    )

    /**
     * [RGB][Rgb] color space ProPhoto RGB standardized as ROMM RGB ISO 22028-2:2013.
     * [See details on ProPhoto RGB color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#PRO_PHOTO_RGB)
     */
    val ProPhotoRgb = Rgb(
        "ROMM RGB ISO 22028-2:2013",
        floatArrayOf(0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f),
        Illuminant.D50,
        TransferParameters(1.8, 1.0, 0.0, 1 / 16.0, 0.031248),
        id = 11
    )

    /**
     * [RGB][Rgb] color space ACES standardized as SMPTE ST 2065-1:2012.
     * [See details on ACES color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#ACES)
     */
    val Aces = Rgb(
        "SMPTE ST 2065-1:2012 ACES",
        floatArrayOf(0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f),
        Illuminant.D60,
        1.0,
        -65504.0f, 65504.0f,
        id = 12
    )

    /**
     * [RGB][Rgb] color space ACEScg standardized as Academy S-2014-004.
     * [See details on ACEScg color space](https://d.android.com/reference/android/graphics/ColorSpace.Named.html#ACES_CG)
     */
    val Acescg = Rgb(
        "Academy S-2014-004 ACEScg",
        floatArrayOf(0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f),
        Illuminant.D60,
        1.0,
        -65504.0f, 65504.0f,
        id = 13
    )

    /**
     * [XYZ][ColorModel.Xyz] color space CIE XYZ. This color space assumes standard
     * illuminant D50 as its white point.
     *
     * ```
     * | Property                | Value                 |
     * |-------------------------|-----------------------|
     * | Name                    | Generic XYZ           |
     * | CIE standard illuminant | [D50][Illuminant.D50] |
     * | Range                   | `[-2.0, 2.0]`         |
     * ```
     */
    val CieXyz: ColorSpace = Xyz(
        "Generic XYZ",
        id = 14
    )

    /**
     * [Lab][ColorModel.Lab] color space CIE L*a*b*. This color space uses CIE XYZ D50
     * as a profile conversion space.
     *
     * ```
     * | Property                | Value                                                   |
     * |-------------------------|---------------------------------------------------------|
     * | Name                    | Generic L*a*b*                                          |
     * | CIE standard illuminant | [D50][Illuminant.D50]                                   |
     * | Range                   | (L: `[0.0, 100.0]`, a: `[-128, 128]`, b: `[-128, 128]`) |
     * ```
     */
    val CieLab: ColorSpace = Lab(
        "Generic L*a*b*",
        id = 15
    )

    /**
     * This identifies the 'None' color.
     */
    internal val Unspecified = Rgb(
        "None",
        SrgbPrimaries,
        Illuminant.D65,
        NoneTransferParameters,
        id = 16
    )

    /**
     * Returns a [ColorSpaces] instance of [ColorSpace] that matches
     * the specified RGB to CIE XYZ transform and transfer functions. If no
     * instance can be found, this method returns null.
     *
     * The color transform matrix is assumed to target the CIE XYZ space
     * a [D50][Illuminant.D50] standard illuminant.
     *
     * @param toXYZD50 3x3 column-major transform matrix from RGB to the profile
     * connection space CIE XYZ as an array of 9 floats, cannot be null
     * @param function Parameters for the transfer functions
     * @return A non-null [ColorSpace] if a match is found, null otherwise
     */
    fun match(
        /*@Size(9)*/
        toXYZD50: FloatArray,
        function: TransferParameters
    ): ColorSpace? {
        for (colorSpace in ColorSpacesArray) {
            if (colorSpace.model == ColorModel.Rgb) {
                val rgb = colorSpace.adapt(Illuminant.D50) as Rgb
                if ((
                    compare(toXYZD50, rgb.transform) &&
                        compare(function, rgb.transferParameters)
                    )
                ) {
                    return colorSpace
                }
            }
        }

        return null
    }

    internal inline fun getColorSpace(id: Int): ColorSpace =
        ColorSpacesArray[id]

    /**
     * These MUST be in the order of their IDs
     */
    internal val ColorSpacesArray = arrayOf(
        Srgb,
        LinearSrgb,
        ExtendedSrgb,
        LinearExtendedSrgb,
        Bt709,
        Bt2020,
        DciP3,
        DisplayP3,
        Ntsc1953,
        SmpteC,
        AdobeRgb,
        ProPhotoRgb,
        Aces,
        Acescg,
        CieXyz,
        CieLab,
        Unspecified
    )
}