1 /* 2 * Copyright (C) 2024 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 package com.android.internal.widget.remotecompose.core.operations; 17 18 import android.annotation.NonNull; 19 20 /** Utilities to be used across all core operations */ 21 public class Utils { 22 /** 23 * Convert an integer id into a float 24 * 25 * @param v the integer id to convert 26 * @return the id as an float 27 */ asNan(int v)28 public static float asNan(int v) { 29 return Float.intBitsToFloat(v | -0x800000); 30 } 31 32 /** 33 * convert a float into an integer id 34 * 35 * @param value the float id to convert 36 * @return the id as an integer 37 */ idFromNan(float value)38 public static int idFromNan(float value) { 39 int b = Float.floatToRawIntBits(value); 40 return b & 0x3FFFFF; 41 } 42 43 /** 44 * Converts an id encoded in a float to the corresponding long id. 45 * 46 * @param value the float if to convert 47 * @return the float id converted to a long id 48 */ longIdFromNan(float value)49 public static long longIdFromNan(float value) { 50 return ((long) idFromNan(value)) + 0x100000000L; 51 } 52 53 /** 54 * convert a long into an ID 55 * 56 * @param v the long to convert 57 * @return the id still as a long 58 */ idFromLong(long v)59 public static long idFromLong(long v) { 60 return v - 0x100000000L; 61 } 62 63 /** 64 * convert a float id and turn it into a string 65 * 66 * @param value float to convert 67 * @return string form of an id 68 */ 69 @NonNull idStringFromNan(float value)70 public static String idStringFromNan(float value) { 71 int b = Float.floatToRawIntBits(value) & 0x3FFFFF; 72 return idString(b); 73 } 74 75 /** 76 * print an id as a string 77 * 78 * @param b the id 79 * @return the id as a string 80 */ 81 @NonNull idString(int b)82 public static String idString(int b) { 83 return (b > 0xFFFFF) ? "A_" + (b & 0xFFFFF) : "" + b; 84 } 85 86 /** 87 * trim a string to n characters if needing to trim end in "..." 88 * 89 * @param str 90 * @param n 91 * @return 92 */ 93 @NonNull trimString(@onNull String str, int n)94 public static String trimString(@NonNull String str, int n) { 95 if (str.length() > n) { 96 str = str.substring(0, n - 3) + "..."; 97 } 98 return str; 99 } 100 101 /** 102 * print the id and the value of a float 103 * 104 * @param idvalue 105 * @param value 106 * @return 107 */ floatToString(float idvalue, float value)108 public static @NonNull String floatToString(float idvalue, float value) { 109 if (Float.isNaN(idvalue)) { 110 if (idFromNan(value) == 0) { 111 return "NaN"; 112 } 113 return "[" + idFromNan(idvalue) + "]" + floatToString(value); 114 } 115 return floatToString(value); 116 } 117 118 /** 119 * Convert float to string but render nan id in brackets [n] 120 * 121 * @param value 122 * @return 123 */ floatToString(float value)124 public static @NonNull String floatToString(float value) { 125 if (Float.isNaN(value)) { 126 if (idFromNan(value) == 0) { 127 return "NaN"; 128 } 129 return "[" + idFromNan(value) + "]"; 130 } 131 return Float.toString(value); 132 } 133 134 /** 135 * Debugging util to print a message and include the file/line it came from 136 * 137 * @param str 138 */ log(@onNull String str)139 public static void log(@NonNull String str) { 140 StackTraceElement s = new Throwable().getStackTrace()[1]; 141 System.out.println( 142 "(" 143 + s.getFileName() 144 + ":" 145 + s.getLineNumber() 146 + "). " 147 + s.getMethodName() 148 + "() " 149 + str); 150 } 151 152 /** 153 * Debugging util to print the stack 154 * 155 * @param str 156 * @param n 157 */ logStack(@onNull String str, int n)158 public static void logStack(@NonNull String str, int n) { 159 StackTraceElement[] st = new Throwable().getStackTrace(); 160 for (int i = 1; i < n + 1; i++) { 161 StackTraceElement s = st[i]; 162 String space = new String(new char[i]).replace('\0', ' '); 163 System.out.println( 164 space + "(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str); 165 } 166 } 167 168 /** 169 * Is a variable Allowed int calculation and references. 170 * 171 * @param v 172 * @return 173 */ isVariable(float v)174 public static boolean isVariable(float v) { 175 if (Float.isNaN(v)) { 176 int id = idFromNan(v); 177 if (id == 0) return false; 178 return id > 40 || id < 10; 179 } 180 return false; 181 } 182 183 /** 184 * print a color in the familiar 0xAARRGGBB pattern 185 * 186 * @param color 187 * @return 188 */ 189 @NonNull colorInt(int color)190 public static String colorInt(int color) { 191 String str = "000000000000" + Integer.toHexString(color); 192 return "0x" + str.substring(str.length() - 8); 193 } 194 195 /** 196 * Interpolate two colors. gamma corrected colors are interpolated in the form c1 * (1-t) + c2 * 197 * t 198 * 199 * @param c1 200 * @param c2 201 * @param t 202 * @return 203 */ interpolateColor(int c1, int c2, float t)204 public static int interpolateColor(int c1, int c2, float t) { 205 if (Float.isNaN(t) || t == 0.0f) { 206 return c1; 207 } else if (t == 1.0f) { 208 return c2; 209 } 210 int a = 0xFF & (c1 >> 24); 211 int r = 0xFF & (c1 >> 16); 212 int g = 0xFF & (c1 >> 8); 213 int b = 0xFF & c1; 214 float f_r = (float) Math.pow(r / 255.0f, 2.2); 215 float f_g = (float) Math.pow(g / 255.0f, 2.2); 216 float f_b = (float) Math.pow(b / 255.0f, 2.2); 217 float c1fr = f_r; 218 float c1fg = f_g; 219 float c1fb = f_b; 220 float c1fa = a / 255f; 221 222 a = 0xFF & (c2 >> 24); 223 r = 0xFF & (c2 >> 16); 224 g = 0xFF & (c2 >> 8); 225 b = 0xFF & c2; 226 f_r = (float) Math.pow(r / 255.0f, 2.2); 227 f_g = (float) Math.pow(g / 255.0f, 2.2); 228 f_b = (float) Math.pow(b / 255.0f, 2.2); 229 float c2fr = f_r; 230 float c2fg = f_g; 231 float c2fb = f_b; 232 float c2fa = a / 255f; 233 f_r = c1fr + t * (c2fr - c1fr); 234 f_g = c1fg + t * (c2fg - c1fg); 235 f_b = c1fb + t * (c2fb - c1fb); 236 float f_a = c1fa + t * (c2fa - c1fa); 237 238 int outr = clamp((int) ((float) Math.pow(f_r, 1.0 / 2.2) * 255.0f)); 239 int outg = clamp((int) ((float) Math.pow(f_g, 1.0 / 2.2) * 255.0f)); 240 int outb = clamp((int) ((float) Math.pow(f_b, 1.0 / 2.2) * 255.0f)); 241 int outa = clamp((int) (f_a * 255.0f)); 242 243 return (outa << 24 | outr << 16 | outg << 8 | outb); 244 } 245 246 /** 247 * Efficient clamping function 248 * 249 * @param c 250 * @return number between 0 and 255 251 */ clamp(int c)252 public static int clamp(int c) { 253 int n = 255; 254 c &= ~(c >> 31); 255 c -= n; 256 c &= (c >> 31); 257 c += n; 258 return c; 259 } 260 261 /** 262 * convert hue saturation and value to RGB 263 * 264 * @param hue 0..1 265 * @param saturation 0..1 0=on the gray scale 266 * @param value 0..1 0=black 267 * @return 268 */ hsvToRgb(float hue, float saturation, float value)269 public static int hsvToRgb(float hue, float saturation, float value) { 270 int h = (int) (hue * 6); 271 float f = hue * 6 - h; 272 int p = (int) (0.5f + 255 * value * (1 - saturation)); 273 int q = (int) (0.5f + 255 * value * (1 - f * saturation)); 274 int t = (int) (0.5f + 255 * value * (1 - (1 - f) * saturation)); 275 int v = (int) (0.5f + 255 * value); 276 switch (h) { 277 case 0: 278 return 0XFF000000 | (v << 16) + (t << 8) + p; 279 case 1: 280 return 0XFF000000 | (q << 16) + (v << 8) + p; 281 case 2: 282 return 0XFF000000 | (p << 16) + (v << 8) + t; 283 case 3: 284 return 0XFF000000 | (p << 16) + (q << 8) + v; 285 case 4: 286 return 0XFF000000 | (t << 16) + (p << 8) + v; 287 case 5: 288 return 0XFF000000 | (v << 16) + (p << 8) + q; 289 } 290 return 0; 291 } 292 293 /** 294 * Convert float alpha, red,g reen, blue to ARGB int 295 * 296 * @param alpha alpha value 297 * @param red red value 298 * @param green green value 299 * @param blue blue value 300 * @return ARGB int 301 */ toARGB(float alpha, float red, float green, float blue)302 public static int toARGB(float alpha, float red, float green, float blue) { 303 int a = (int) (alpha * 255.0f + 0.5f); 304 int r = (int) (red * 255.0f + 0.5f); 305 int g = (int) (green * 255.0f + 0.5f); 306 int b = (int) (blue * 255.0f + 0.5f); 307 return (a << 24 | r << 16 | g << 8 | b); 308 } 309 310 /** 311 * Returns the hue of an ARGB int 312 * 313 * @param argb the color int 314 * @return 315 */ getHue(int argb)316 public static float getHue(int argb) { 317 int r = (argb >> 16) & 0xFF; 318 int g = (argb >> 8) & 0xFF; 319 int b = argb & 0xFF; 320 float hsl1, hsl2, hsl3; 321 final float rf = r / 255f; 322 final float gf = g / 255f; 323 final float bf = b / 255f; 324 final float max = Math.max(rf, Math.max(gf, bf)); 325 final float min = Math.min(rf, Math.min(gf, bf)); 326 final float deltaMaxMin = max - min; 327 float h; 328 if (max == min) { 329 // Monochromatic 330 h = 0f; 331 } else { 332 if (max == rf) { 333 h = ((gf - bf) / deltaMaxMin) % 6f; 334 } else if (max == gf) { 335 h = ((bf - rf) / deltaMaxMin) + 2f; 336 } else { 337 h = ((rf - gf) / deltaMaxMin) + 4f; 338 } 339 } 340 h = (h * 60f) % 360f; 341 if (h < 0) { 342 h += 360f; 343 } 344 return h / 360f; 345 } 346 347 /** 348 * Get the saturation of an ARGB int 349 * 350 * @param argb the color int 351 * @return 352 */ getSaturation(int argb)353 public static float getSaturation(int argb) { 354 int r = (argb >> 16) & 0xFF; 355 int g = (argb >> 8) & 0xFF; 356 int b = argb & 0xFF; 357 358 final float rf = r / 255f; 359 final float gf = g / 255f; 360 final float bf = b / 255f; 361 final float max = Math.max(rf, Math.max(gf, bf)); 362 final float min = Math.min(rf, Math.min(gf, bf)); 363 final float deltaMaxMin = max - min; 364 float s; 365 float l = (max + min) / 2f; 366 if (max == min) { 367 // Monochromatic 368 s = 0f; 369 } else { 370 371 s = deltaMaxMin / (1f - Math.abs(2f * l - 1f)); 372 } 373 374 return s; 375 } 376 377 /** 378 * Get the brightness of an ARGB int 379 * 380 * @param argb the color int 381 * @return 382 */ getBrightness(int argb)383 public static float getBrightness(int argb) { 384 int r = (argb >> 16) & 0xFF; 385 int g = (argb >> 8) & 0xFF; 386 int b = argb & 0xFF; 387 final float rf = r / 255f; 388 final float gf = g / 255f; 389 final float bf = b / 255f; 390 final float max = Math.max(rf, Math.max(gf, bf)); 391 final float min = Math.min(rf, Math.min(gf, bf)); 392 393 return (max + min) / 2f; 394 } 395 } 396