1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.compose.ui.graphics.colorspace 18 19 import androidx.compose.ui.graphics.Color 20 import androidx.compose.ui.util.fastCoerceIn 21 import androidx.compose.ui.util.packFloats 22 import kotlin.math.cbrt 23 24 /** Implementation of the CIE L*a*b* color space. Its PCS is CIE XYZ with a white point of D50. */ 25 internal class Lab(name: String, id: Int) : ColorSpace(name, ColorModel.Lab, id) { 26 27 override val isWideGamut: Boolean 28 get() = true 29 getMinValuenull30 override fun getMinValue(component: Int): Float { 31 return if (component == 0) 0.0f else -128.0f 32 } 33 getMaxValuenull34 override fun getMaxValue(component: Int): Float { 35 return if (component == 0) 100.0f else 128.0f 36 } 37 toXyznull38 override fun toXyz(v: FloatArray): FloatArray { 39 v[0] = v[0].fastCoerceIn(0.0f, 100.0f) 40 v[1] = v[1].fastCoerceIn(-128.0f, 128.0f) 41 v[2] = v[2].fastCoerceIn(-128.0f, 128.0f) 42 43 val fy = (v[0] + 16.0f) / 116.0f 44 val fx = fy + (v[1] * 0.002f) 45 val fz = fy - (v[2] * 0.005f) 46 val x = if (fx > D) fx * fx * fx else (1.0f / B) * (fx - C) 47 val y = if (fy > D) fy * fy * fy else (1.0f / B) * (fy - C) 48 val z = if (fz > D) fz * fz * fz else (1.0f / B) * (fz - C) 49 50 v[0] = x * Illuminant.D50Xyz[0] 51 v[1] = y * Illuminant.D50Xyz[1] 52 v[2] = z * Illuminant.D50Xyz[2] 53 54 return v 55 } 56 toXynull57 override fun toXy(v0: Float, v1: Float, v2: Float): Long { 58 val v00 = v0.fastCoerceIn(0.0f, 100.0f) 59 val v10 = v1.fastCoerceIn(-128.0f, 128.0f) 60 61 val fy = (v00 + 16.0f) / 116.0f 62 val fx = fy + (v10 * 0.002f) 63 val x = if (fx > D) fx * fx * fx else (1.0f / B) * (fx - C) 64 val y = if (fy > D) fy * fy * fy else (1.0f / B) * (fy - C) 65 66 return packFloats(x * Illuminant.D50Xyz[0], y * Illuminant.D50Xyz[1]) 67 } 68 toZnull69 override fun toZ(v0: Float, v1: Float, v2: Float): Float { 70 val v00 = v0.fastCoerceIn(0.0f, 100.0f) 71 val v20 = v2.fastCoerceIn(-128.0f, 128.0f) 72 val fy = (v00 + 16.0f) / 116.0f 73 val fz = fy - (v20 * 0.005f) 74 val z = if (fz > D) fz * fz * fz else (1.0f / B) * (fz - C) 75 return z * Illuminant.D50Xyz[2] 76 } 77 xyzaToColornull78 override fun xyzaToColor( 79 x: Float, 80 y: Float, 81 z: Float, 82 a: Float, 83 colorSpace: ColorSpace 84 ): Color { 85 val x1 = x / Illuminant.D50Xyz[0] 86 val y1 = y / Illuminant.D50Xyz[1] 87 val z1 = z / Illuminant.D50Xyz[2] 88 89 val fx = if (x1 > A) cbrt(x1) else B * x1 + C 90 val fy = if (y1 > A) cbrt(y1) else B * y1 + C 91 val fz = if (z1 > A) cbrt(z1) else B * z1 + C 92 93 val l = 116.0f * fy - 16.0f 94 val a1 = 500.0f * (fx - fy) 95 val b = 200.0f * (fy - fz) 96 97 return Color( 98 l.fastCoerceIn(0.0f, 100.0f), 99 a1.fastCoerceIn(-128.0f, 128.0f), 100 b.fastCoerceIn(-128.0f, 128.0f), 101 a, 102 colorSpace 103 ) 104 } 105 fromXyznull106 override fun fromXyz(v: FloatArray): FloatArray { 107 val x = v[0] / Illuminant.D50Xyz[0] 108 val y = v[1] / Illuminant.D50Xyz[1] 109 val z = v[2] / Illuminant.D50Xyz[2] 110 111 val fx = if (x > A) cbrt(x) else B * x + C 112 val fy = if (y > A) cbrt(y) else B * y + C 113 val fz = if (z > A) cbrt(z) else B * z + C 114 115 val l = 116.0f * fy - 16.0f 116 val a = 500.0f * (fx - fy) 117 val b = 200.0f * (fy - fz) 118 119 v[0] = l.fastCoerceIn(0.0f, 100.0f) 120 v[1] = a.fastCoerceIn(-128.0f, 128.0f) 121 v[2] = b.fastCoerceIn(-128.0f, 128.0f) 122 123 return v 124 } 125 126 internal companion object { 127 private const val A = 216.0f / 24389.0f 128 private const val B = 841.0f / 108.0f 129 private const val C = 4.0f / 29.0f 130 private const val D = 6.0f / 29.0f 131 } 132 } 133