1 /* 2 * Copyright 2021 Google LLC 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 // This file is automatically generated. Do not modify it. 18 19 package com.google.ux.material.libmonet.utils; 20 21 /** 22 * Color science utilities. 23 * 24 * <p>Utility methods for color science constants and color space conversions that aren't HCT or 25 * CAM16. 26 */ 27 public class ColorUtils { ColorUtils()28 private ColorUtils() {} 29 30 static final double[][] SRGB_TO_XYZ = 31 new double[][] { 32 new double[] {0.41233895, 0.35762064, 0.18051042}, 33 new double[] {0.2126, 0.7152, 0.0722}, 34 new double[] {0.01932141, 0.11916382, 0.95034478}, 35 }; 36 37 static final double[][] XYZ_TO_SRGB = 38 new double[][] { 39 new double[] { 40 3.2413774792388685, -1.5376652402851851, -0.49885366846268053, 41 }, 42 new double[] { 43 -0.9691452513005321, 1.8758853451067872, 0.04156585616912061, 44 }, 45 new double[] { 46 0.05562093689691305, -0.20395524564742123, 1.0571799111220335, 47 }, 48 }; 49 50 static final double[] WHITE_POINT_D65 = new double[] {95.047, 100.0, 108.883}; 51 52 /** Converts a color from RGB components to ARGB format. */ argbFromRgb(int red, int green, int blue)53 public static int argbFromRgb(int red, int green, int blue) { 54 return (255 << 24) | ((red & 255) << 16) | ((green & 255) << 8) | (blue & 255); 55 } 56 57 /** Converts a color from linear RGB components to ARGB format. */ argbFromLinrgb(double[] linrgb)58 public static int argbFromLinrgb(double[] linrgb) { 59 int r = delinearized(linrgb[0]); 60 int g = delinearized(linrgb[1]); 61 int b = delinearized(linrgb[2]); 62 return argbFromRgb(r, g, b); 63 } 64 65 /** Returns the alpha component of a color in ARGB format. */ alphaFromArgb(int argb)66 public static int alphaFromArgb(int argb) { 67 return (argb >> 24) & 255; 68 } 69 70 /** Returns the red component of a color in ARGB format. */ redFromArgb(int argb)71 public static int redFromArgb(int argb) { 72 return (argb >> 16) & 255; 73 } 74 75 /** Returns the green component of a color in ARGB format. */ greenFromArgb(int argb)76 public static int greenFromArgb(int argb) { 77 return (argb >> 8) & 255; 78 } 79 80 /** Returns the blue component of a color in ARGB format. */ blueFromArgb(int argb)81 public static int blueFromArgb(int argb) { 82 return argb & 255; 83 } 84 85 /** Returns whether a color in ARGB format is opaque. */ isOpaque(int argb)86 public static boolean isOpaque(int argb) { 87 return alphaFromArgb(argb) >= 255; 88 } 89 90 /** Converts a color from ARGB to XYZ. */ argbFromXyz(double x, double y, double z)91 public static int argbFromXyz(double x, double y, double z) { 92 double[][] matrix = XYZ_TO_SRGB; 93 double linearR = matrix[0][0] * x + matrix[0][1] * y + matrix[0][2] * z; 94 double linearG = matrix[1][0] * x + matrix[1][1] * y + matrix[1][2] * z; 95 double linearB = matrix[2][0] * x + matrix[2][1] * y + matrix[2][2] * z; 96 int r = delinearized(linearR); 97 int g = delinearized(linearG); 98 int b = delinearized(linearB); 99 return argbFromRgb(r, g, b); 100 } 101 102 /** Converts a color from XYZ to ARGB. */ xyzFromArgb(int argb)103 public static double[] xyzFromArgb(int argb) { 104 double r = linearized(redFromArgb(argb)); 105 double g = linearized(greenFromArgb(argb)); 106 double b = linearized(blueFromArgb(argb)); 107 return MathUtils.matrixMultiply(new double[] {r, g, b}, SRGB_TO_XYZ); 108 } 109 110 /** Converts a color represented in Lab color space into an ARGB integer. */ argbFromLab(double l, double a, double b)111 public static int argbFromLab(double l, double a, double b) { 112 double[] whitePoint = WHITE_POINT_D65; 113 double fy = (l + 16.0) / 116.0; 114 double fx = a / 500.0 + fy; 115 double fz = fy - b / 200.0; 116 double xNormalized = labInvf(fx); 117 double yNormalized = labInvf(fy); 118 double zNormalized = labInvf(fz); 119 double x = xNormalized * whitePoint[0]; 120 double y = yNormalized * whitePoint[1]; 121 double z = zNormalized * whitePoint[2]; 122 return argbFromXyz(x, y, z); 123 } 124 125 /** 126 * Converts a color from ARGB representation to L*a*b* representation. 127 * 128 * @param argb the ARGB representation of a color 129 * @return a Lab object representing the color 130 */ labFromArgb(int argb)131 public static double[] labFromArgb(int argb) { 132 double linearR = linearized(redFromArgb(argb)); 133 double linearG = linearized(greenFromArgb(argb)); 134 double linearB = linearized(blueFromArgb(argb)); 135 double[][] matrix = SRGB_TO_XYZ; 136 double x = matrix[0][0] * linearR + matrix[0][1] * linearG + matrix[0][2] * linearB; 137 double y = matrix[1][0] * linearR + matrix[1][1] * linearG + matrix[1][2] * linearB; 138 double z = matrix[2][0] * linearR + matrix[2][1] * linearG + matrix[2][2] * linearB; 139 double[] whitePoint = WHITE_POINT_D65; 140 double xNormalized = x / whitePoint[0]; 141 double yNormalized = y / whitePoint[1]; 142 double zNormalized = z / whitePoint[2]; 143 double fx = labF(xNormalized); 144 double fy = labF(yNormalized); 145 double fz = labF(zNormalized); 146 double l = 116.0 * fy - 16; 147 double a = 500.0 * (fx - fy); 148 double b = 200.0 * (fy - fz); 149 return new double[] {l, a, b}; 150 } 151 152 /** 153 * Converts an L* value to an ARGB representation. 154 * 155 * @param lstar L* in L*a*b* 156 * @return ARGB representation of grayscale color with lightness matching L* 157 */ argbFromLstar(double lstar)158 public static int argbFromLstar(double lstar) { 159 double y = yFromLstar(lstar); 160 int component = delinearized(y); 161 return argbFromRgb(component, component, component); 162 } 163 164 /** 165 * Computes the L* value of a color in ARGB representation. 166 * 167 * @param argb ARGB representation of a color 168 * @return L*, from L*a*b*, coordinate of the color 169 */ lstarFromArgb(int argb)170 public static double lstarFromArgb(int argb) { 171 double y = xyzFromArgb(argb)[1]; 172 return 116.0 * labF(y / 100.0) - 16.0; 173 } 174 175 /** 176 * Converts an L* value to a Y value. 177 * 178 * <p>L* in L*a*b* and Y in XYZ measure the same quantity, luminance. 179 * 180 * <p>L* measures perceptual luminance, a linear scale. Y in XYZ measures relative luminance, a 181 * logarithmic scale. 182 * 183 * @param lstar L* in L*a*b* 184 * @return Y in XYZ 185 */ yFromLstar(double lstar)186 public static double yFromLstar(double lstar) { 187 return 100.0 * labInvf((lstar + 16.0) / 116.0); 188 } 189 190 /** 191 * Converts a Y value to an L* value. 192 * 193 * <p>L* in L*a*b* and Y in XYZ measure the same quantity, luminance. 194 * 195 * <p>L* measures perceptual luminance, a linear scale. Y in XYZ measures relative luminance, a 196 * logarithmic scale. 197 * 198 * @param y Y in XYZ 199 * @return L* in L*a*b* 200 */ lstarFromY(double y)201 public static double lstarFromY(double y) { 202 return labF(y / 100.0) * 116.0 - 16.0; 203 } 204 205 /** 206 * Linearizes an RGB component. 207 * 208 * @param rgbComponent 0 <= rgb_component <= 255, represents R/G/B channel 209 * @return 0.0 <= output <= 100.0, color channel converted to linear RGB space 210 */ linearized(int rgbComponent)211 public static double linearized(int rgbComponent) { 212 double normalized = rgbComponent / 255.0; 213 if (normalized <= 0.040449936) { 214 return normalized / 12.92 * 100.0; 215 } else { 216 return Math.pow((normalized + 0.055) / 1.055, 2.4) * 100.0; 217 } 218 } 219 220 /** 221 * Delinearizes an RGB component. 222 * 223 * @param rgbComponent 0.0 <= rgb_component <= 100.0, represents linear R/G/B channel 224 * @return 0 <= output <= 255, color channel converted to regular RGB space 225 */ delinearized(double rgbComponent)226 public static int delinearized(double rgbComponent) { 227 double normalized = rgbComponent / 100.0; 228 double delinearized = 0.0; 229 if (normalized <= 0.0031308) { 230 delinearized = normalized * 12.92; 231 } else { 232 delinearized = 1.055 * Math.pow(normalized, 1.0 / 2.4) - 0.055; 233 } 234 return MathUtils.clampInt(0, 255, (int) Math.round(delinearized * 255.0)); 235 } 236 237 /** 238 * Returns the standard white point; white on a sunny day. 239 * 240 * @return The white point 241 */ whitePointD65()242 public static double[] whitePointD65() { 243 return WHITE_POINT_D65; 244 } 245 labF(double t)246 static double labF(double t) { 247 double e = 216.0 / 24389.0; 248 double kappa = 24389.0 / 27.0; 249 if (t > e) { 250 return Math.pow(t, 1.0 / 3.0); 251 } else { 252 return (kappa * t + 16) / 116; 253 } 254 } 255 labInvf(double ft)256 static double labInvf(double ft) { 257 double e = 216.0 / 24389.0; 258 double kappa = 24389.0 / 27.0; 259 double ft3 = ft * ft * ft; 260 if (ft3 > e) { 261 return ft3; 262 } else { 263 return (116 * ft - 16) / kappa; 264 } 265 } 266 } 267 268