1 /* 2 * Copyright (C) 2023 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 static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT; 19 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT; 20 21 import android.annotation.NonNull; 22 23 import com.android.internal.widget.remotecompose.core.Operation; 24 import com.android.internal.widget.remotecompose.core.Operations; 25 import com.android.internal.widget.remotecompose.core.RemoteContext; 26 import com.android.internal.widget.remotecompose.core.VariableSupport; 27 import com.android.internal.widget.remotecompose.core.WireBuffer; 28 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; 29 import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; 30 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; 31 import com.android.internal.widget.remotecompose.core.serialize.Serializable; 32 33 import java.util.List; 34 35 /** 36 * Operation to Colors Color modes mMode = 0 two colors and a tween mMode = 1 color1 is a colorID. 37 * mMode = 2 color2 is a colorID. mMode = 3 color1 & color2 are ids mMode = 4 H S V mode 38 */ 39 public class ColorExpression extends Operation implements VariableSupport, Serializable { 40 private static final int OP_CODE = Operations.COLOR_EXPRESSIONS; 41 private static final String CLASS_NAME = "ColorExpression"; 42 public int mId; 43 44 /** 45 * Mode of the color expression 0 = two colors and a tween 1 = color1 is a colorID. 2 color2 is 46 * a colorID. 3 = color1 & color2 are ids 4 = H S V mode 5 = RGB mode 6 = ARGB mode 47 */ 48 int mMode; 49 50 public int mColor1; 51 public int mColor2; 52 public float mTween = 0.0f; 53 54 public float mHue = 0; // only in Mode 4 55 public float mSat = 0; 56 public float mValue = 0; 57 public float mOutHue = 0; // only in Mode 4 58 public float mOutSat = 0; 59 public float mOutValue = 0; 60 public int mAlpha = 0xFF; // only used in hsv mode 61 62 private float mArgbAlpha = 0.0f; 63 private float mArgbRed = 0.0f; 64 private float mArgbGreen = 0.0f; 65 private float mArgbBlue = 0.0f; 66 67 private float mOutArgbAlpha = 0.0f; 68 private float mOutArgbRed = 0.0f; 69 private float mOutArgbGreen = 0.0f; 70 private float mOutArgbBlue = 0.0f; 71 72 public float mOutTween = 0.0f; 73 public int mOutColor1; 74 public int mOutColor2; 75 76 /** COLOR_COLOR_INTERPOLATE */ 77 public static final byte COLOR_COLOR_INTERPOLATE = 0; 78 79 /** COLOR_ID_INTERPOLATE */ 80 public static final byte ID_COLOR_INTERPOLATE = 1; 81 82 /** ID_COLOR_INTERPOLATE */ 83 public static final byte COLOR_ID_INTERPOLATE = 2; 84 85 /** ID_ID_INTERPOLATE */ 86 public static final byte ID_ID_INTERPOLATE = 3; 87 88 /** H S V mode */ 89 public static final byte HSV_MODE = 4; 90 91 /** ARGB mode */ 92 public static final byte ARGB_MODE = 5; 93 94 /** ARGB mode with a being an id */ 95 public static final byte IDARGB_MODE = 6; 96 97 /** 98 * Create a new ColorExpression object 99 * 100 * @param id the id of the color 101 * @param hue the hue of the color 102 * @param sat the saturation of the color 103 * @param value the value of the color 104 */ ColorExpression(int id, float hue, float sat, float value)105 public ColorExpression(int id, float hue, float sat, float value) { 106 mMode = HSV_MODE; 107 mAlpha = 0xFF; 108 mOutHue = mHue = hue; 109 mOutSat = mSat = sat; 110 mOutValue = mValue = value; 111 mColor1 = Float.floatToRawIntBits(hue); 112 mColor2 = Float.floatToRawIntBits(sat); 113 mTween = value; 114 } 115 116 /** 117 * Create a new ColorExpression object based on HSV 118 * 119 * @param id id of the color 120 * @param mode the mode of the color 121 * @param alpha the alpha of the color 122 * @param hue the hue of the color 123 * @param sat the saturation of the color 124 * @param value the value (brightness) of the color 125 */ ColorExpression(int id, byte mode, int alpha, float hue, float sat, float value)126 public ColorExpression(int id, byte mode, int alpha, float hue, float sat, float value) { 127 if (mode != HSV_MODE) { 128 throw new RuntimeException("Invalid mode " + mode); 129 } 130 mId = id; 131 mMode = HSV_MODE; 132 mAlpha = alpha; 133 mOutHue = mHue = hue; 134 mOutSat = mSat = sat; 135 mOutValue = mValue = value; 136 mColor1 = Float.floatToRawIntBits(hue); 137 mColor2 = Float.floatToRawIntBits(sat); 138 mTween = value; 139 } 140 141 /** 142 * Create a new ColorExpression object based interpolationg two colors 143 * 144 * @param id the id of the color 145 * @param mode the type of mode (are colors ids or actual values) 146 * @param color1 the first color to use 147 * @param color2 the second color to use 148 * @param tween the value to use to interpolate between the two colors 149 */ ColorExpression(int id, int mode, int color1, int color2, float tween)150 public ColorExpression(int id, int mode, int color1, int color2, float tween) { 151 this.mId = id; 152 this.mMode = mode & 0xFF; 153 this.mAlpha = (mode >> 16) & 0xFF; 154 if (mMode == HSV_MODE) { 155 mOutHue = mHue = Float.intBitsToFloat(color1); 156 mOutSat = mSat = Float.intBitsToFloat(color2); 157 mOutValue = mValue = tween; 158 } 159 this.mColor1 = color1; 160 this.mColor2 = color2; 161 this.mTween = tween; 162 this.mOutTween = tween; 163 this.mOutColor1 = color1; 164 this.mOutColor2 = color2; 165 } 166 167 /** 168 * Create a new ColorExpression object based on ARGB 169 * 170 * @param id the id of the color 171 * @param mode the mode must be ARGB_MODE 172 * @param alpha the alpha value of the color 173 * @param red the red of component the color 174 * @param green the greej component of the color 175 * @param blue the blue of component the color 176 */ ColorExpression(int id, byte mode, float alpha, float red, float green, float blue)177 public ColorExpression(int id, byte mode, float alpha, float red, float green, float blue) { 178 if (mode != ARGB_MODE) { 179 throw new RuntimeException("Invalid mode " + mode); 180 } 181 mMode = ARGB_MODE; 182 mId = id; 183 mOutArgbAlpha = mArgbAlpha = alpha; 184 mOutArgbRed = mArgbRed = red; 185 mOutArgbGreen = mArgbGreen = green; 186 mOutArgbBlue = mArgbBlue = blue; 187 } 188 189 @Override updateVariables(@onNull RemoteContext context)190 public void updateVariables(@NonNull RemoteContext context) { 191 if (mMode == 4) { 192 if (Float.isNaN(mHue)) { 193 mOutHue = context.getFloat(Utils.idFromNan(mHue)); 194 } 195 if (Float.isNaN(mSat)) { 196 mOutSat = context.getFloat(Utils.idFromNan(mSat)); 197 } 198 if (Float.isNaN(mValue)) { 199 mOutValue = context.getFloat(Utils.idFromNan(mValue)); 200 } 201 } 202 if (mMode == ARGB_MODE) { 203 if (Float.isNaN(mArgbAlpha)) { 204 mOutArgbAlpha = context.getFloat(Utils.idFromNan(mArgbAlpha)); 205 } 206 if (Float.isNaN(mArgbRed)) { 207 mOutArgbRed = context.getFloat(Utils.idFromNan(mArgbRed)); 208 } 209 if (Float.isNaN(mArgbGreen)) { 210 mOutArgbGreen = context.getFloat(Utils.idFromNan(mArgbGreen)); 211 } 212 if (Float.isNaN(mArgbBlue)) { 213 mOutArgbBlue = context.getFloat(Utils.idFromNan(mArgbBlue)); 214 } 215 } 216 if (Float.isNaN(mTween)) { 217 mOutTween = context.getFloat(Utils.idFromNan(mTween)); 218 } 219 if ((mMode & 1) == 1) { 220 mOutColor1 = context.getColor(mColor1); 221 } 222 if ((mMode & 2) == 2) { 223 mOutColor2 = context.getColor(mColor2); 224 } 225 } 226 227 @Override registerListening(@onNull RemoteContext context)228 public void registerListening(@NonNull RemoteContext context) { 229 if (mMode == HSV_MODE) { 230 if (Float.isNaN(mHue)) { 231 context.listensTo(Utils.idFromNan(mHue), this); 232 } 233 if (Float.isNaN(mSat)) { 234 context.listensTo(Utils.idFromNan(mSat), this); 235 } 236 if (Float.isNaN(mValue)) { 237 context.listensTo(Utils.idFromNan(mValue), this); 238 } 239 return; 240 } 241 if (mMode == ARGB_MODE) { 242 if (Float.isNaN(mArgbAlpha)) { 243 context.listensTo(Utils.idFromNan(mArgbAlpha), this); 244 } 245 if (Float.isNaN(mArgbRed)) { 246 context.listensTo(Utils.idFromNan(mArgbRed), this); 247 } 248 if (Float.isNaN(mArgbGreen)) { 249 context.listensTo(Utils.idFromNan(mArgbGreen), this); 250 } 251 if (Float.isNaN(mArgbBlue)) { 252 context.listensTo(Utils.idFromNan(mArgbBlue), this); 253 } 254 return; 255 } 256 if (Float.isNaN(mTween)) { 257 context.listensTo(Utils.idFromNan(mTween), this); 258 } 259 if ((mMode & 1) == 1) { 260 context.listensTo(mColor1, this); 261 } 262 if ((mMode & 2) == 2) { 263 context.listensTo(mColor2, this); 264 } 265 } 266 267 @Override apply(@onNull RemoteContext context)268 public void apply(@NonNull RemoteContext context) { 269 if (mMode == HSV_MODE) { 270 context.loadColor( 271 mId, (mAlpha << 24) | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue))); 272 return; 273 } 274 if (mMode == ARGB_MODE) { 275 context.loadColor( 276 mId, Utils.toARGB(mOutArgbAlpha, mOutArgbRed, mOutArgbGreen, mOutArgbBlue)); 277 return; 278 } 279 if (mOutTween == 0.0) { 280 if ((mMode & 1) == 1) { 281 mOutColor1 = context.getColor(mColor1); 282 } 283 context.loadColor(mId, mOutColor1); 284 } else { 285 if ((mMode & 1) == 1) { 286 mOutColor1 = context.getColor(mColor1); 287 } 288 if ((mMode & 2) == 2) { 289 mOutColor2 = context.getColor(mColor2); 290 } 291 292 context.loadColor(mId, Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween)); 293 } 294 } 295 296 @Override write(@onNull WireBuffer buffer)297 public void write(@NonNull WireBuffer buffer) { 298 int mode; 299 switch (mMode) { 300 case ARGB_MODE: 301 apply(buffer, mId, mArgbAlpha, mArgbRed, mArgbGreen, mArgbBlue); 302 break; 303 304 case HSV_MODE: 305 mOutValue = mValue; 306 mColor1 = Float.floatToRawIntBits(mHue); 307 mColor2 = Float.floatToRawIntBits(mSat); 308 mode = mMode | (mAlpha << 16); 309 apply(buffer, mId, mode, mColor1, mColor2, mTween); 310 311 break; 312 case COLOR_ID_INTERPOLATE: 313 case ID_COLOR_INTERPOLATE: 314 case ID_ID_INTERPOLATE: 315 case COLOR_COLOR_INTERPOLATE: 316 apply(buffer, mId, mMode, mColor1, mColor2, mTween); 317 318 break; 319 default: 320 throw new RuntimeException("Invalid mode "); 321 } 322 } 323 324 @NonNull 325 @Override toString()326 public String toString() { 327 if (mMode == HSV_MODE) { 328 return "ColorExpression[" 329 + mId 330 + "] = hsv (" 331 + Utils.floatToString(mHue) 332 + ", " 333 + Utils.floatToString(mSat) 334 + ", " 335 + Utils.floatToString(mValue) 336 + ")"; 337 } 338 Utils.log(" ColorExpression toString" + mId + " " + mMode); 339 if (mMode == ARGB_MODE) { 340 return "ColorExpression[" 341 + mId 342 + "] = rgb (" 343 + Utils.floatToString(mArgbAlpha) 344 + ", " 345 + Utils.floatToString(mArgbRed) 346 + ", " 347 + Utils.floatToString(mArgbGreen) 348 + ", " 349 + Utils.floatToString(mArgbRed) 350 + ")"; 351 } 352 String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1); 353 String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2); 354 return "ColorExpression[" 355 + mId 356 + "] = tween(" 357 + c1 358 + ", " 359 + c2 360 + ", " 361 + Utils.floatToString(mTween) 362 + ")"; 363 } 364 365 /** 366 * The name of the class 367 * 368 * @return the name 369 */ 370 @NonNull name()371 public static String name() { 372 return CLASS_NAME; 373 } 374 375 /** 376 * The OP_CODE for this command 377 * 378 * @return the opcode 379 */ id()380 public static int id() { 381 return OP_CODE; 382 } 383 384 /** 385 * Call to write a ColorExpression object on the buffer 386 * 387 * @param buffer 388 * @param id of the ColorExpression object 389 * @param mode if colors are id or actual values 390 * @param color1 391 * @param color2 392 * @param tween 393 */ apply( @onNull WireBuffer buffer, int id, int mode, int color1, int color2, float tween)394 public static void apply( 395 @NonNull WireBuffer buffer, int id, int mode, int color1, int color2, float tween) { 396 apply(buffer, id, mode, color1, color2, Float.floatToRawIntBits(tween)); 397 } 398 399 /** 400 * Call to write a ColorExpression object on the buffer 401 * 402 * @param buffer 403 * @param id of the ColorExpression object 404 * @param alpha 405 * @param red 406 * @param green 407 * @param blue 408 */ apply( @onNull WireBuffer buffer, int id, float alpha, float red, float green, float blue)409 public static void apply( 410 @NonNull WireBuffer buffer, int id, float alpha, float red, float green, float blue) { 411 int param1 = (Float.isNaN(alpha)) ? IDARGB_MODE : ARGB_MODE; 412 param1 |= 413 (Float.isNaN(alpha)) ? Utils.idFromNan(alpha) << 16 : ((int) (alpha * 1024)) << 16; 414 int param2 = Float.floatToRawIntBits(red); 415 int param3 = Float.floatToRawIntBits(green); 416 int param4 = Float.floatToRawIntBits(blue); 417 apply(buffer, id, param1, param2, param3, param4); 418 } 419 apply( @onNull WireBuffer buffer, int id, int param1, int param2, int param3, int param4)420 private static void apply( 421 @NonNull WireBuffer buffer, int id, int param1, int param2, int param3, int param4) { 422 buffer.start(OP_CODE); 423 buffer.writeInt(id); 424 buffer.writeInt(param1); 425 buffer.writeInt(param2); 426 buffer.writeInt(param3); 427 buffer.writeInt(param4); 428 } 429 430 /** 431 * Read this operation and add it to the list of operations 432 * 433 * @param buffer the buffer to read 434 * @param operations the list of operations that will be added to 435 */ read(@onNull WireBuffer buffer, @NonNull List<Operation> operations)436 public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { 437 int id = buffer.readInt(); 438 int param1 = buffer.readInt(); 439 int param2 = buffer.readInt(); 440 int param3 = buffer.readInt(); 441 int param4 = buffer.readInt(); 442 int mode = param1 & 0xFF; 443 float alpha; 444 float red; 445 float green; 446 float blue; 447 switch (mode) { 448 case IDARGB_MODE: 449 alpha = Utils.asNan(param1 >> 16); 450 red = Float.intBitsToFloat(param2); 451 green = Float.intBitsToFloat(param3); 452 blue = Float.intBitsToFloat(param4); 453 operations.add(new ColorExpression(id, (byte) ARGB_MODE, alpha, red, green, blue)); 454 break; 455 case ARGB_MODE: 456 alpha = (param1 >> 16) / 1024.0f; 457 red = Float.intBitsToFloat(param2); 458 green = Float.intBitsToFloat(param3); 459 blue = Float.intBitsToFloat(param4); 460 operations.add(new ColorExpression(id, (byte) ARGB_MODE, alpha, red, green, blue)); 461 break; 462 case HSV_MODE: 463 alpha = (param1 >> 16) / 1024.0f; 464 float hue = Float.intBitsToFloat(param2); 465 float sat = Float.intBitsToFloat(param3); 466 float value = Float.intBitsToFloat(param4); 467 operations.add(new ColorExpression(id, HSV_MODE, (param1 >> 16), hue, sat, value)); 468 break; 469 case COLOR_ID_INTERPOLATE: 470 case ID_COLOR_INTERPOLATE: 471 case ID_ID_INTERPOLATE: 472 case COLOR_COLOR_INTERPOLATE: 473 operations.add( 474 new ColorExpression( 475 id, mode, param2, param3, Float.intBitsToFloat(param4))); 476 break; 477 default: 478 throw new RuntimeException("Invalid mode " + mode); 479 } 480 } 481 482 /** 483 * Populate the documentation with a description of this operation 484 * 485 * @param doc to append the description to. 486 */ documentation(@onNull DocumentationBuilder doc)487 public static void documentation(@NonNull DocumentationBuilder doc) { 488 doc.operation("Expressions Operations", OP_CODE, CLASS_NAME) 489 .description("A Color defined by an expression") 490 .field(DocumentedOperation.INT, "id", "Id of the color") 491 .field(INT, "mode", "The use of the next 3 fields") 492 .possibleValues("COLOR_COLOR_INTERPOLATE", 0) 493 .possibleValues("COLOR_ID_INTERPOLATE", 1) 494 .possibleValues("ID_COLOR_INTERPOLATE", 2) 495 .possibleValues("ID_ID_INTERPOLATE", 3) 496 .possibleValues("HSV", 4) 497 .field(INT, "color1", "32 bit ARGB color") 498 .field(INT, "color2", "32 bit ARGB color") 499 .field(FLOAT, "tween", "32 bit ARGB color"); 500 } 501 502 @NonNull 503 @Override deepToString(@onNull String indent)504 public String deepToString(@NonNull String indent) { 505 return indent + toString(); 506 } 507 508 @Override serialize(MapSerializer serializer)509 public void serialize(MapSerializer serializer) { 510 serializer.addType(CLASS_NAME).add("id", mId); 511 switch (mMode) { 512 case COLOR_COLOR_INTERPOLATE: 513 case ID_COLOR_INTERPOLATE: 514 case COLOR_ID_INTERPOLATE: 515 case ID_ID_INTERPOLATE: 516 serializer.add("mode", "TWEEN"); 517 serializer.add("startColor", mColor1, mOutColor1); 518 serializer.add("endColor", mColor2, mOutColor2); 519 serializer.add("startColor", mTween, mOutTween); 520 break; 521 case HSV_MODE: 522 serializer.add("mode", "HSV"); 523 serializer.add("hue", mHue, mOutHue); 524 serializer.add("sat", mSat, mOutSat); 525 serializer.add("val", mValue, mOutValue); 526 break; 527 case ARGB_MODE: 528 case IDARGB_MODE: 529 serializer.add("mode", "ARGB"); 530 serializer.add("a", mArgbAlpha, mOutArgbAlpha); 531 serializer.add("r", mArgbRed, mOutArgbRed); 532 serializer.add("g", mArgbGreen, mOutArgbGreen); 533 serializer.add("b", mArgbBlue, mOutArgbBlue); 534 break; 535 default: 536 serializer.add("mode", "NONE"); 537 } 538 } 539 } 540