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.paint; 17 18 import static com.android.internal.widget.remotecompose.core.serialize.MapSerializer.orderedOf; 19 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 23 import com.android.internal.widget.remotecompose.core.PaintContext; 24 import com.android.internal.widget.remotecompose.core.RemoteContext; 25 import com.android.internal.widget.remotecompose.core.VariableSupport; 26 import com.android.internal.widget.remotecompose.core.WireBuffer; 27 import com.android.internal.widget.remotecompose.core.operations.Utils; 28 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; 29 import com.android.internal.widget.remotecompose.core.serialize.Serializable; 30 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.List; 34 import java.util.Map; 35 36 /** Paint Bundle represents a delta of changes to a paint object */ 37 public class PaintBundle implements Serializable { 38 @NonNull int[] mArray = new int[200]; 39 @Nullable int[] mOutArray = null; 40 int mPos = 0; 41 42 /** 43 * Apply changes to a PaintChanges interface 44 * 45 * @param paintContext 46 * @param p 47 */ applyPaintChange(@onNull PaintContext paintContext, @NonNull PaintChanges p)48 public void applyPaintChange(@NonNull PaintContext paintContext, @NonNull PaintChanges p) { 49 int i = 0; 50 int mask = 0; 51 if (mOutArray == null) { 52 mOutArray = mArray; 53 } 54 while (i < mPos) { 55 int cmd = mOutArray[i++]; 56 mask = mask | (1 << (cmd - 1)); 57 switch (cmd & 0xFFFF) { 58 case TEXT_SIZE: 59 p.setTextSize(Float.intBitsToFloat(mOutArray[i++])); 60 break; 61 case TYPEFACE: 62 int style = (cmd >> 16); 63 int weight = style & 0x3ff; 64 boolean italic = (style >> 10) > 0; 65 int font_type = mOutArray[i++]; 66 67 p.setTypeFace(font_type, weight, italic); 68 break; 69 case COLOR_ID: // mOutArray should have already decoded it 70 case COLOR: 71 p.setColor(mOutArray[i++]); 72 break; 73 case STROKE_WIDTH: 74 p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++])); 75 break; 76 case STROKE_MITER: 77 p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++])); 78 break; 79 case STROKE_CAP: 80 p.setStrokeCap(cmd >> 16); 81 break; 82 case STYLE: 83 p.setStyle(cmd >> 16); 84 break; 85 case SHADER: 86 p.setShader(mOutArray[i++]); 87 break; 88 case STROKE_JOIN: 89 p.setStrokeJoin(cmd >> 16); 90 break; 91 case IMAGE_FILTER_QUALITY: 92 p.setImageFilterQuality(cmd >> 16); 93 break; 94 case BLEND_MODE: 95 p.setBlendMode(cmd >> 16); 96 break; 97 case FILTER_BITMAP: 98 p.setFilterBitmap(!((cmd >> 16) == 0)); 99 break; 100 case GRADIENT: 101 i = callSetGradient(cmd, mOutArray, i, p); 102 break; 103 case COLOR_FILTER_ID: 104 case COLOR_FILTER: 105 p.setColorFilter(mOutArray[i++], cmd >> 16); 106 break; 107 case ALPHA: 108 p.setAlpha(Float.intBitsToFloat(mOutArray[i++])); 109 break; 110 case CLEAR_COLOR_FILTER: 111 p.clear(0x1L << PaintBundle.COLOR_FILTER); 112 break; 113 } 114 } 115 } 116 117 // private String toName(int id) { 118 // switch (id) { 119 // case TEXT_SIZE: 120 // return "TEXT_SIZE"; 121 // case COLOR: 122 // return "COLOR"; 123 // case STROKE_WIDTH: 124 // return "STROKE_WIDTH"; 125 // case STROKE_MITER: 126 // return "STROKE_MITER"; 127 // case TYPEFACE: 128 // return "TYPEFACE"; 129 // case STROKE_CAP: 130 // return "CAP"; 131 // case STYLE: 132 // return "STYLE"; 133 // case SHADER: 134 // return "SHADER"; 135 // case IMAGE_FILTER_QUALITY: 136 // return "IMAGE_FILTER_QUALITY"; 137 // case BLEND_MODE: 138 // return "BLEND_MODE"; 139 // case FILTER_BITMAP: 140 // return "FILTER_BITMAP"; 141 // case GRADIENT: 142 // return "GRADIENT_LINEAR"; 143 // case ALPHA: 144 // return "ALPHA"; 145 // case COLOR_FILTER: 146 // return "COLOR_FILTER"; 147 // } 148 // return "????" + id + "????"; 149 // } 150 151 @NonNull colorInt(int color)152 private static String colorInt(int color) { 153 String str = "000000000000" + Integer.toHexString(color); 154 return "0x" + str.substring(str.length() - 8); 155 } 156 157 @NonNull colorInt(@onNull int[] color)158 private static String colorInt(@NonNull int[] color) { 159 String str = "["; 160 for (int i = 0; i < color.length; i++) { 161 if (i > 0) { 162 str += ", "; 163 } 164 str += colorInt(color[i]); 165 } 166 return str + "]"; 167 } 168 asFloatStr(int value)169 private static String asFloatStr(int value) { 170 float fValue = Float.intBitsToFloat(value); 171 if (Float.isNaN(fValue)) { 172 return "[" + Utils.idFromNan(fValue) + "]"; 173 } 174 return Float.toString(fValue); 175 } 176 177 @NonNull 178 @Override toString()179 public String toString() { 180 StringBuilder ret = new StringBuilder("\n"); 181 int i = 0; 182 while (i < mPos) { 183 int cmd = mArray[i++]; 184 int type = cmd & 0xFFFF; 185 switch (type) { 186 case TEXT_SIZE: 187 ret.append(" TextSize(" + asFloatStr(mArray[i++])); 188 break; 189 case TYPEFACE: 190 int style = (cmd >> 16); 191 int weight = style & 0x3ff; 192 boolean italic = (style >> 10) > 0; 193 int font_type = mArray[i++]; 194 ret.append(" TypeFace(" + (font_type + ", " + weight + ", " + italic)); 195 break; 196 case COLOR: 197 ret.append(" Color(" + colorInt(mArray[i++])); 198 break; 199 case COLOR_ID: 200 ret.append(" ColorId([" + mArray[i++] + "]"); 201 break; 202 case STROKE_WIDTH: 203 ret.append(" StrokeWidth(" + asFloatStr(mArray[i++])); 204 break; 205 case STROKE_MITER: 206 ret.append(" StrokeMiter(" + asFloatStr(mArray[i++])); 207 break; 208 case STROKE_CAP: 209 ret.append(" StrokeCap(" + (cmd >> 16)); 210 break; 211 case STYLE: 212 ret.append(" Style(" + (cmd >> 16)); 213 break; 214 case COLOR_FILTER: 215 ret.append( 216 " ColorFilter(color=" 217 + colorInt(mArray[i++]) 218 + ", mode=" 219 + blendModeString(cmd >> 16)); 220 break; 221 case COLOR_FILTER_ID: 222 ret.append( 223 " ColorFilterID(color=[" 224 + mArray[i++] 225 + "], mode=" 226 + blendModeString(cmd >> 16)); 227 break; 228 case CLEAR_COLOR_FILTER: 229 ret.append(" clearColorFilter"); 230 break; 231 case SHADER: 232 ret.append(" Shader(" + mArray[i++]); 233 break; 234 case ALPHA: 235 ret.append(" Alpha(" + asFloatStr(mArray[i++])); 236 break; 237 case IMAGE_FILTER_QUALITY: 238 ret.append(" ImageFilterQuality(" + (cmd >> 16)); 239 break; 240 case BLEND_MODE: 241 ret.append(" BlendMode(" + blendModeString(cmd >> 16)); 242 break; 243 case FILTER_BITMAP: 244 ret.append(" FilterBitmap(" + !(cmd >> 16 == 0)); 245 break; 246 case STROKE_JOIN: 247 ret.append(" StrokeJoin(" + (cmd >> 16)); 248 break; 249 case ANTI_ALIAS: 250 ret.append(" AntiAlias(" + (cmd >> 16)); 251 break; 252 case GRADIENT: 253 i = callPrintGradient(cmd, mArray, i, ret); 254 } 255 ret.append("),\n"); 256 } 257 return ret.toString(); 258 } 259 registerFloat( int iv, @NonNull RemoteContext context, @NonNull VariableSupport support)260 private void registerFloat( 261 int iv, @NonNull RemoteContext context, @NonNull VariableSupport support) { 262 float v = Float.intBitsToFloat(iv); 263 if (Float.isNaN(v)) { 264 context.listensTo(Utils.idFromNan(v), support); 265 } 266 } 267 callRegisterGradient( int cmd, int[] array, int i, @NonNull RemoteContext context, @NonNull VariableSupport support)268 int callRegisterGradient( 269 int cmd, 270 int[] array, 271 int i, 272 @NonNull RemoteContext context, 273 @NonNull VariableSupport support) { 274 int ret = i; 275 int type = (cmd >> 16); 276 int control = array[ret++]; 277 int len = 0xFF & control; // maximum 256 colors 278 int register = 0xFFFF & (control >> 16); 279 int tileMode = 0; 280 switch (type) { 281 /* see {@link #setLinearGradient} */ 282 case LINEAR_GRADIENT: 283 if (len > 0) { 284 285 for (int j = 0; j < len; j++) { 286 int color = array[ret++]; 287 if ((register & (1 << j)) != 0) { 288 context.listensTo(color, support); 289 } 290 } 291 } 292 len = array[ret++]; 293 294 if (len > 0) { 295 296 for (int j = 0; j < len; j++) { 297 registerFloat(array[ret++], context, support); 298 } 299 } 300 301 // start x 302 registerFloat(array[ret++], context, support); 303 // start y 304 registerFloat(array[ret++], context, support); 305 // end x 306 registerFloat(array[ret++], context, support); 307 // end y 308 registerFloat(array[ret++], context, support); 309 tileMode = array[ret++]; 310 break; 311 /* see {@link #setRadialGradient} */ 312 case RADIAL_GRADIENT: 313 if (len > 0) { 314 315 for (int j = 0; j < len; j++) { 316 int color = array[ret++]; 317 if ((register & (1 << j)) != 0) { 318 context.listensTo(color, support); 319 } 320 } 321 } 322 len = array[ret++]; // stops 323 for (int j = 0; j < len; j++) { 324 registerFloat(array[ret++], context, support); 325 } 326 327 // center x 328 registerFloat(array[ret++], context, support); 329 // center y 330 registerFloat(array[ret++], context, support); 331 // radius 332 registerFloat(array[ret++], context, support); 333 334 tileMode = array[ret++]; // tile Mode 335 break; 336 /* see {@link #setSweepGradient} */ 337 case SWEEP_GRADIENT: 338 if (len > 0) { 339 340 for (int j = 0; j < len; j++) { 341 int color = array[ret++]; 342 if ((register & (1 << j)) != 0) { 343 context.listensTo(color, support); 344 } 345 } 346 } 347 len = array[ret++]; // stops 348 for (int j = 0; j < len; j++) { 349 registerFloat(array[ret++], context, support); 350 } 351 // center x 352 registerFloat(array[ret++], context, support); 353 // center y 354 registerFloat(array[ret++], context, support); 355 break; 356 default: 357 System.out.println("error "); 358 } 359 360 return ret; 361 } 362 callPrintGradient(int cmd, int[] array, int i, @NonNull StringBuilder p)363 int callPrintGradient(int cmd, int[] array, int i, @NonNull StringBuilder p) { 364 int ret = i; 365 int type = (cmd >> 16); 366 int tileMode = 0; 367 int len = array[ret++]; 368 int[] colors = null; 369 String[] stops = null; 370 switch (type) { 371 case 0: 372 p.append(" LinearGradient(\n"); 373 if (len > 0) { 374 colors = new int[len]; 375 for (int j = 0; j < colors.length; j++) { 376 colors[j] = array[ret++]; 377 } 378 } 379 len = array[ret++]; 380 if (len > 0) { 381 stops = new String[len]; 382 for (int j = 0; j < stops.length; j++) { 383 stops[j] = asFloatStr(array[ret++]); 384 } 385 } 386 387 p.append(" colors = " + colorInt(colors) + ",\n"); 388 p.append(" stops = " + Arrays.toString(stops) + ",\n"); 389 p.append(" start = "); 390 p.append("[" + asFloatStr(array[ret++])); 391 p.append(", " + asFloatStr(array[ret++]) + "],\n"); 392 p.append(" end = "); 393 p.append("[" + asFloatStr(array[ret++])); 394 p.append(", " + asFloatStr(array[ret++]) + "],\n"); 395 tileMode = array[ret++]; 396 p.append(" tileMode = " + tileMode + "\n "); 397 break; 398 case 1: 399 p.append(" RadialGradient(\n"); 400 if (len > 0) { 401 colors = new int[len]; 402 for (int j = 0; j < colors.length; j++) { 403 colors[j] = array[ret++]; 404 } 405 } 406 len = array[ret++]; 407 if (len > 0) { 408 stops = new String[len]; 409 for (int j = 0; j < stops.length; j++) { 410 stops[j] = asFloatStr(array[ret++]); 411 } 412 } 413 414 p.append(" colors = " + colorInt(colors) + ",\n"); 415 p.append(" stops = " + Arrays.toString(stops) + ",\n"); 416 p.append(" center = "); 417 p.append("[" + asFloatStr(array[ret++])); 418 p.append(", " + asFloatStr(array[ret++]) + "],\n"); 419 p.append(" radius ="); 420 p.append(" " + asFloatStr(array[ret++]) + ",\n"); 421 tileMode = array[ret++]; 422 p.append(" tileMode = " + tileMode + "\n "); 423 break; 424 case 2: 425 p.append(" SweepGradient(\n"); 426 if (len > 0) { 427 colors = new int[len]; 428 for (int j = 0; j < colors.length; j++) { 429 colors[j] = array[ret++]; 430 } 431 } 432 len = array[ret++]; 433 if (len > 0) { 434 stops = new String[len]; 435 for (int j = 0; j < stops.length; j++) { 436 stops[j] = asFloatStr(array[ret++]); 437 } 438 } 439 p.append(" colors = " + colorInt(colors) + ",\n"); 440 p.append(" stops = " + Arrays.toString(stops) + ",\n"); 441 p.append(" center = "); 442 p.append("[" + asFloatStr(array[ret++])); 443 p.append(", " + asFloatStr(array[ret++]) + "],\n "); 444 break; 445 default: 446 p.append("GRADIENT_??????!!!!"); 447 } 448 449 return ret; 450 } 451 callSetGradient(int cmd, @NonNull int[] array, int i, @NonNull PaintChanges p)452 int callSetGradient(int cmd, @NonNull int[] array, int i, @NonNull PaintChanges p) { 453 int ret = i; 454 int gradientType = (cmd >> 16); 455 456 int len = 0xFF & array[ret++]; // maximum 256 colors 457 458 int[] colors = null; 459 if (len > 0) { 460 colors = new int[len]; 461 for (int j = 0; j < colors.length; j++) { 462 colors[j] = array[ret++]; 463 } 464 } 465 len = array[ret++]; 466 float[] stops = null; 467 if (len > 0) { 468 stops = new float[len]; 469 for (int j = 0; j < colors.length; j++) { 470 stops[j] = Float.intBitsToFloat(array[ret++]); 471 } 472 } 473 474 if (colors == null) { 475 return ret; 476 } 477 478 int tileMode = 0; 479 float centerX = 0f; 480 float centerY = 0f; 481 482 switch (gradientType) { 483 case LINEAR_GRADIENT: 484 float startX = Float.intBitsToFloat(array[ret++]); 485 float startY = Float.intBitsToFloat(array[ret++]); 486 float endX = Float.intBitsToFloat(array[ret++]); 487 float endY = Float.intBitsToFloat(array[ret++]); 488 tileMode = array[ret++]; 489 p.setLinearGradient(colors, stops, startX, startY, endX, endY, tileMode); 490 break; 491 case RADIAL_GRADIENT: 492 centerX = Float.intBitsToFloat(array[ret++]); 493 centerY = Float.intBitsToFloat(array[ret++]); 494 float radius = Float.intBitsToFloat(array[ret++]); 495 tileMode = array[ret++]; 496 p.setRadialGradient(colors, stops, centerX, centerY, radius, tileMode); 497 break; 498 case SWEEP_GRADIENT: 499 centerX = Float.intBitsToFloat(array[ret++]); 500 centerY = Float.intBitsToFloat(array[ret++]); 501 p.setSweepGradient(colors, stops, centerX, centerY); 502 } 503 504 return ret; 505 } 506 507 /** 508 * Write a bundle of paint changes to the buffer 509 * 510 * @param buffer bundle to write 511 */ writeBundle(@onNull WireBuffer buffer)512 public void writeBundle(@NonNull WireBuffer buffer) { 513 buffer.writeInt(mPos); 514 for (int index = 0; index < mPos; index++) { 515 buffer.writeInt(mArray[index]); 516 } 517 } 518 519 /** 520 * This will read the paint bundle off the wire buffer 521 * 522 * @param buffer the buffer to read 523 */ readBundle(@onNull WireBuffer buffer)524 public void readBundle(@NonNull WireBuffer buffer) { 525 int len = buffer.readInt(); 526 if (len <= 0 || len > 1024) { 527 throw new RuntimeException("buffer corrupt paint len = " + len); 528 } 529 mArray = new int[len]; 530 for (int i = 0; i < mArray.length; i++) { 531 mArray[i] = buffer.readInt(); 532 } 533 mPos = len; 534 } 535 536 public static final int TEXT_SIZE = 1; // float 537 538 public static final int COLOR = 4; // int 539 public static final int STROKE_WIDTH = 5; // float 540 public static final int STROKE_MITER = 6; 541 public static final int STROKE_CAP = 7; // int 542 public static final int STYLE = 8; // int 543 public static final int SHADER = 9; // int 544 public static final int IMAGE_FILTER_QUALITY = 10; // int 545 public static final int GRADIENT = 11; 546 public static final int ALPHA = 12; 547 public static final int COLOR_FILTER = 13; 548 public static final int ANTI_ALIAS = 14; 549 public static final int STROKE_JOIN = 15; 550 public static final int TYPEFACE = 16; 551 public static final int FILTER_BITMAP = 17; 552 public static final int BLEND_MODE = 18; 553 public static final int COLOR_ID = 19; 554 public static final int COLOR_FILTER_ID = 20; 555 public static final int CLEAR_COLOR_FILTER = 21; 556 557 public static final int BLEND_MODE_CLEAR = 0; 558 public static final int BLEND_MODE_SRC = 1; 559 public static final int BLEND_MODE_DST = 2; 560 public static final int BLEND_MODE_SRC_OVER = 3; 561 public static final int BLEND_MODE_DST_OVER = 4; 562 public static final int BLEND_MODE_SRC_IN = 5; 563 public static final int BLEND_MODE_DST_IN = 6; 564 public static final int BLEND_MODE_SRC_OUT = 7; 565 public static final int BLEND_MODE_DST_OUT = 8; 566 public static final int BLEND_MODE_SRC_ATOP = 9; 567 public static final int BLEND_MODE_DST_ATOP = 10; 568 public static final int BLEND_MODE_XOR = 11; 569 public static final int BLEND_MODE_PLUS = 12; 570 public static final int BLEND_MODE_MODULATE = 13; 571 public static final int BLEND_MODE_SCREEN = 14; 572 public static final int BLEND_MODE_OVERLAY = 15; 573 public static final int BLEND_MODE_DARKEN = 16; 574 public static final int BLEND_MODE_LIGHTEN = 17; 575 public static final int BLEND_MODE_COLOR_DODGE = 18; 576 public static final int BLEND_MODE_COLOR_BURN = 19; 577 public static final int BLEND_MODE_HARD_LIGHT = 20; 578 public static final int BLEND_MODE_SOFT_LIGHT = 21; 579 public static final int BLEND_MODE_DIFFERENCE = 22; 580 public static final int BLEND_MODE_EXCLUSION = 23; 581 public static final int BLEND_MODE_MULTIPLY = 24; 582 public static final int BLEND_MODE_HUE = 25; 583 public static final int BLEND_MODE_SATURATION = 26; 584 public static final int BLEND_MODE_COLOR = 27; 585 public static final int BLEND_MODE_LUMINOSITY = 28; 586 public static final int BLEND_MODE_NULL = 29; 587 public static final int PORTER_MODE_ADD = 30; 588 589 public static final int FONT_NORMAL = 0; 590 public static final int FONT_BOLD = 1; 591 public static final int FONT_ITALIC = 2; 592 public static final int FONT_BOLD_ITALIC = 3; 593 594 public static final int FONT_TYPE_DEFAULT = 0; 595 public static final int FONT_TYPE_SANS_SERIF = 1; 596 public static final int FONT_TYPE_SERIF = 2; 597 public static final int FONT_TYPE_MONOSPACE = 3; 598 599 public static final int STYLE_FILL = 0; 600 public static final int STYLE_STROKE = 1; 601 public static final int STYLE_FILL_AND_STROKE = 2; 602 public static final int LINEAR_GRADIENT = 0; 603 public static final int RADIAL_GRADIENT = 1; 604 public static final int SWEEP_GRADIENT = 2; 605 606 private int mLastShaderSet = -1; 607 private boolean mColorFilterSet = false; 608 609 /** 610 * sets a shader that draws a linear gradient along a line. 611 * 612 * @param startX The x-coordinate for the start of the gradient line 613 * @param startY The y-coordinate for the start of the gradient line 614 * @param endX The x-coordinate for the end of the gradient line 615 * @param endY The y-coordinate for the end of the gradient line 616 * @param colors The sRGB colors to be distributed along the gradient line 617 * @param stops May be null. The relative positions [0..1] of each corresponding color in the 618 * colors array. If this is null, the colors are distributed evenly along the gradient line. 619 * @param tileMode The Shader tiling mode 620 */ setLinearGradient( @onNull int[] colors, int idMask, @Nullable float[] stops, float startX, float startY, float endX, float endY, int tileMode)621 public void setLinearGradient( 622 @NonNull int[] colors, 623 int idMask, 624 @Nullable float[] stops, 625 float startX, 626 float startY, 627 float endX, 628 float endY, 629 int tileMode) { 630 // int startPos = mPos; 631 int len; 632 mArray[mPos++] = GRADIENT | (LINEAR_GRADIENT << 16); 633 mArray[mPos++] = (idMask << 16) | (len = colors.length); 634 for (int i = 0; i < len; i++) { 635 mArray[mPos++] = colors[i]; 636 } 637 638 mArray[mPos++] = len = (stops == null) ? 0 : stops.length; 639 for (int i = 0; i < len; i++) { 640 mArray[mPos++] = Float.floatToRawIntBits(stops[i]); 641 } 642 mArray[mPos++] = Float.floatToRawIntBits(startX); 643 mArray[mPos++] = Float.floatToRawIntBits(startY); 644 mArray[mPos++] = Float.floatToRawIntBits(endX); 645 mArray[mPos++] = Float.floatToRawIntBits(endY); 646 mArray[mPos++] = tileMode; 647 } 648 649 /** 650 * Set a shader that draws a sweep gradient around a center point. 651 * 652 * @param centerX The x-coordinate of the center 653 * @param centerY The y-coordinate of the center 654 * @param colors The sRGB colors to be distributed around the center. There must be at least 2 655 * colors in the array. 656 * @param stops May be NULL. The relative position of each corresponding color in the colors 657 * array, beginning with 0 and ending with 1.0. If the values are not monotonic, the drawing 658 * may produce unexpected results. If positions is NULL, then the colors are automatically 659 * spaced evenly. 660 */ setSweepGradient( @onNull int[] colors, int idMask, @Nullable float[] stops, float centerX, float centerY)661 public void setSweepGradient( 662 @NonNull int[] colors, 663 int idMask, 664 @Nullable float[] stops, // TODO: rename positions to stops or stops to positions, but 665 // don't have both in the same file 666 float centerX, 667 float centerY) { 668 int len; 669 mArray[mPos++] = GRADIENT | (SWEEP_GRADIENT << 16); 670 mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length); 671 for (int i = 0; i < len; i++) { 672 mArray[mPos++] = colors[i]; 673 } 674 675 mArray[mPos++] = len = (stops == null) ? 0 : stops.length; 676 for (int i = 0; i < len; i++) { 677 mArray[mPos++] = Float.floatToRawIntBits(stops[i]); 678 } 679 mArray[mPos++] = Float.floatToRawIntBits(centerX); 680 mArray[mPos++] = Float.floatToRawIntBits(centerY); 681 } 682 683 /** 684 * Sets a shader that draws a radial gradient given the center and radius. 685 * 686 * @param centerX The x-coordinate of the center of the radius 687 * @param centerY The y-coordinate of the center of the radius 688 * @param radius Must be positive. The radius of the gradient. 689 * @param colors The sRGB colors distributed between the center and edge 690 * @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and <code> 691 * 1.0f</code>. The relative position of each corresponding color in the colors 692 * array. If <code>null</code>, colors are distributed evenly between the center and edge of 693 * the circle. 694 * @param tileMode The Shader tiling mode 695 */ setRadialGradient( @onNull int[] colors, int idMask, @Nullable float[] stops, float centerX, float centerY, float radius, int tileMode)696 public void setRadialGradient( 697 @NonNull int[] colors, 698 int idMask, 699 @Nullable float[] stops, 700 float centerX, 701 float centerY, 702 float radius, 703 int tileMode) { 704 // int startPos = mPos; 705 int len; 706 mArray[mPos++] = GRADIENT | (RADIAL_GRADIENT << 16); 707 mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length); 708 for (int i = 0; i < len; i++) { 709 mArray[mPos++] = colors[i]; 710 } 711 mArray[mPos++] = len = (stops == null) ? 0 : stops.length; 712 713 for (int i = 0; i < len; i++) { 714 mArray[mPos++] = Float.floatToRawIntBits(stops[i]); 715 } 716 mArray[mPos++] = Float.floatToRawIntBits(centerX); 717 mArray[mPos++] = Float.floatToRawIntBits(centerY); 718 mArray[mPos++] = Float.floatToRawIntBits(radius); 719 mArray[mPos++] = tileMode; 720 } 721 722 /** 723 * Create a color filter that uses the specified color and Porter-Duff mode. 724 * 725 * @param color The ARGB source color used with the Porter-Duff mode 726 * @param mode The porter-duff mode that is applied 727 */ setColorFilter(int color, int mode)728 public void setColorFilter(int color, int mode) { 729 mArray[mPos] = COLOR_FILTER | (mode << 16); 730 mPos++; 731 mArray[mPos++] = color; 732 } 733 734 /** 735 * Create a color filter that uses the specified color and Porter-Duff mode. 736 * 737 * @param color The id source color used with the Porter-Duff mode 738 * @param mode The porter-duff mode that is applied 739 */ setColorFilterId(int color, int mode)740 public void setColorFilterId(int color, int mode) { 741 mArray[mPos] = COLOR_FILTER_ID | (mode << 16); 742 mPos++; 743 mArray[mPos++] = color; 744 mColorFilterSet = true; 745 } 746 747 /** This sets the color filter to null */ clearColorFilter()748 public void clearColorFilter() { 749 mArray[mPos] = CLEAR_COLOR_FILTER; 750 mPos++; 751 mColorFilterSet = false; 752 } 753 754 /** 755 * Set the paint's text size. This value must be > 0 756 * 757 * @param size set the paint's text size in pixel units. 758 */ setTextSize(float size)759 public void setTextSize(float size) { 760 mArray[mPos] = TEXT_SIZE; 761 mPos++; 762 mArray[mPos] = Float.floatToRawIntBits(size); 763 mPos++; 764 } 765 766 /** 767 * @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace 768 * @param weight 100-1000 769 * @param italic tur 770 */ setTextStyle(int fontType, int weight, boolean italic)771 public void setTextStyle(int fontType, int weight, boolean italic) { 772 int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic 773 mArray[mPos++] = TYPEFACE | (style << 16); 774 mArray[mPos++] = fontType; 775 } 776 777 /** 778 * Set the width for stroking. Pass 0 to stroke in hairline mode. Hairlines always draws a 779 * single pixel independent of the canvas's matrix. 780 * 781 * @param width set the paint's stroke width, used whenever the paint's style is Stroke or 782 * StrokeAndFill. 783 */ setStrokeWidth(float width)784 public void setStrokeWidth(float width) { 785 mArray[mPos] = STROKE_WIDTH; 786 mPos++; 787 mArray[mPos] = Float.floatToRawIntBits(width); 788 mPos++; 789 } 790 791 /** 792 * Set the Color based on Color 793 * 794 * @param color 795 */ setColor(int color)796 public void setColor(int color) { 797 mArray[mPos] = COLOR; 798 mPos++; 799 mArray[mPos] = color; 800 mPos++; 801 } 802 803 /** 804 * Set the color based the R,G,B,A values 805 * 806 * @param r red (0 to 255) 807 * @param g green (0 to 255) 808 * @param b blue (0 to 255) 809 * @param a alpha (0 to 255) 810 */ setColor(int r, int g, int b, int a)811 public void setColor(int r, int g, int b, int a) { 812 int color = (a << 24) | (r << 16) | (g << 8) | b; 813 setColor(color); 814 } 815 816 /** 817 * Set the color based the R,G,B,A values (Warning this does not support NaN ids) 818 * 819 * @param r red (0.0 to 1.0) 820 * @param g green (0.0 to 1.0) 821 * @param b blue (0.0 to 1.0) 822 * @param a alpha (0.0 to 1.0) 823 */ setColor(float r, float g, float b, float a)824 public void setColor(float r, float g, float b, float a) { 825 setColor(Utils.toARGB(a, r, g, b)); 826 } 827 828 /** 829 * Set the Color based on ID 830 * 831 * @param color 832 */ setColorId(int color)833 public void setColorId(int color) { 834 mArray[mPos] = COLOR_ID; 835 mPos++; 836 mArray[mPos] = color; 837 mPos++; 838 } 839 840 /** 841 * Set the paint's Cap. 842 * 843 * @param cap set the paint's line cap style, used whenever the paint's style is Stroke or 844 * StrokeAndFill. 845 */ setStrokeCap(int cap)846 public void setStrokeCap(int cap) { 847 mArray[mPos] = STROKE_CAP | (cap << 16); 848 mPos++; 849 } 850 851 /** 852 * Set the style STROKE and/or FILL 853 * 854 * @param style 855 */ setStyle(int style)856 public void setStyle(int style) { 857 mArray[mPos] = STYLE | (style << 16); 858 mPos++; 859 } 860 861 /** 862 * Set the shader id to use 863 * 864 * @param shaderId 865 */ setShader(int shaderId)866 public void setShader(int shaderId) { 867 mLastShaderSet = shaderId; 868 mArray[mPos] = SHADER; 869 mPos++; 870 mArray[mPos] = shaderId; 871 mPos++; 872 } 873 874 /** Set the Alpha value */ setAlpha(float alpha)875 public void setAlpha(float alpha) { 876 mArray[mPos] = ALPHA; 877 mPos++; 878 mArray[mPos] = Float.floatToRawIntBits(alpha); 879 mPos++; 880 } 881 882 /** 883 * Set the paint's stroke miter value. This is used to control the behavior of miter joins when 884 * the joins angle is sharp. This value must be >= 0. 885 * 886 * @param miter set the miter limit on the paint, used whenever the paint's style is Stroke or 887 * StrokeAndFill. 888 */ setStrokeMiter(float miter)889 public void setStrokeMiter(float miter) { 890 mArray[mPos] = STROKE_MITER; 891 mPos++; 892 mArray[mPos] = Float.floatToRawIntBits(miter); 893 mPos++; 894 } 895 896 /** 897 * Set the paint's Join. 898 * 899 * @param join set the paint's Join, used whenever the paint's style is Stroke or StrokeAndFill. 900 */ setStrokeJoin(int join)901 public void setStrokeJoin(int join) { 902 mArray[mPos] = STROKE_JOIN | (join << 16); 903 mPos++; 904 } 905 906 /** 907 * set Filter Bitmap 908 * 909 * @param filter set to false to disable interpolation 910 */ setFilterBitmap(boolean filter)911 public void setFilterBitmap(boolean filter) { 912 mArray[mPos] = FILTER_BITMAP | (filter ? (1 << 16) : 0); 913 mPos++; 914 } 915 916 /** 917 * Set or clear the blend mode. A blend mode defines how source pixels (generated by a drawing 918 * command) are composited with the destination pixels (content of the render target). 919 * 920 * @param blendmode The blend mode to be installed in the paint 921 */ setBlendMode(int blendmode)922 public void setBlendMode(int blendmode) { 923 mArray[mPos] = BLEND_MODE | (blendmode << 16); 924 mPos++; 925 } 926 927 /** 928 * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit AntiAliasing smooths out 929 * the edges of what is being drawn, but is has no impact on the interior of the shape. See 930 * setDither() and setFilterBitmap() to affect how colors are treated. 931 * 932 * @param aa true to set the antialias bit in the flags, false to clear it 933 */ setAntiAlias(boolean aa)934 public void setAntiAlias(boolean aa) { 935 mArray[mPos] = ANTI_ALIAS | (aa ? 1 : 0) << 16; 936 mPos++; 937 } 938 939 /** 940 * clear a series of paint parameters. Currently not used 941 * 942 * @param mask bit pattern of the attributes to clear 943 */ clear(long mask)944 public void clear(long mask) { // unused for now 945 } 946 947 /** Reset the content of the paint bundle so that it can be reused */ reset()948 public void reset() { 949 mPos = 0; 950 if (mColorFilterSet) { 951 clearColorFilter(); 952 } 953 if (mLastShaderSet != -1 && mLastShaderSet != 0) { 954 setShader(0); 955 } 956 } 957 958 /** 959 * Convert a blend mode integer as a string 960 * 961 * @param mode the blend mode 962 * @return the blend mode as a string 963 */ blendModeString(int mode)964 public static @NonNull String blendModeString(int mode) { 965 switch (mode) { 966 case PaintBundle.BLEND_MODE_CLEAR: 967 return "CLEAR"; 968 case PaintBundle.BLEND_MODE_SRC: 969 return "SRC"; 970 case PaintBundle.BLEND_MODE_DST: 971 return "DST"; 972 case PaintBundle.BLEND_MODE_SRC_OVER: 973 return "SRC_OVER"; 974 case PaintBundle.BLEND_MODE_DST_OVER: 975 return "DST_OVER"; 976 case PaintBundle.BLEND_MODE_SRC_IN: 977 return "SRC_IN"; 978 case PaintBundle.BLEND_MODE_DST_IN: 979 return "DST_IN"; 980 case PaintBundle.BLEND_MODE_SRC_OUT: 981 return "SRC_OUT"; 982 case PaintBundle.BLEND_MODE_DST_OUT: 983 return "DST_OUT"; 984 case PaintBundle.BLEND_MODE_SRC_ATOP: 985 return "SRC_ATOP"; 986 case PaintBundle.BLEND_MODE_DST_ATOP: 987 return "DST_ATOP"; 988 case PaintBundle.BLEND_MODE_XOR: 989 return "XOR"; 990 case PaintBundle.BLEND_MODE_PLUS: 991 return "PLUS"; 992 case PaintBundle.BLEND_MODE_MODULATE: 993 return "MODULATE"; 994 case PaintBundle.BLEND_MODE_SCREEN: 995 return "SCREEN"; 996 case PaintBundle.BLEND_MODE_OVERLAY: 997 return "OVERLAY"; 998 case PaintBundle.BLEND_MODE_DARKEN: 999 return "DARKEN"; 1000 case PaintBundle.BLEND_MODE_LIGHTEN: 1001 return "LIGHTEN"; 1002 case PaintBundle.BLEND_MODE_COLOR_DODGE: 1003 return "COLOR_DODGE"; 1004 case PaintBundle.BLEND_MODE_COLOR_BURN: 1005 return "COLOR_BURN"; 1006 case PaintBundle.BLEND_MODE_HARD_LIGHT: 1007 return "HARD_LIGHT"; 1008 case PaintBundle.BLEND_MODE_SOFT_LIGHT: 1009 return "SOFT_LIGHT"; 1010 case PaintBundle.BLEND_MODE_DIFFERENCE: 1011 return "DIFFERENCE"; 1012 case PaintBundle.BLEND_MODE_EXCLUSION: 1013 return "EXCLUSION"; 1014 case PaintBundle.BLEND_MODE_MULTIPLY: 1015 return "MULTIPLY"; 1016 case PaintBundle.BLEND_MODE_HUE: 1017 return "HUE"; 1018 case PaintBundle.BLEND_MODE_SATURATION: 1019 return "SATURATION"; 1020 case PaintBundle.BLEND_MODE_COLOR: 1021 return "COLOR"; 1022 case PaintBundle.BLEND_MODE_LUMINOSITY: 1023 return "LUMINOSITY"; 1024 case PaintBundle.BLEND_MODE_NULL: 1025 return "null"; 1026 case PaintBundle.PORTER_MODE_ADD: 1027 return "ADD"; 1028 } 1029 return "null"; 1030 } 1031 1032 /** 1033 * Check all the floats for Nan(id) floats and call listenTo 1034 * 1035 * @param context 1036 * @param support 1037 */ registerVars(@onNull RemoteContext context, @NonNull VariableSupport support)1038 public void registerVars(@NonNull RemoteContext context, @NonNull VariableSupport support) { 1039 int i = 0; 1040 while (i < mPos) { 1041 int cmd = mArray[i++]; 1042 int type = cmd & 0xFFFF; 1043 switch (type) { 1044 case STROKE_MITER: 1045 case STROKE_WIDTH: 1046 case ALPHA: 1047 case TEXT_SIZE: 1048 float v = Float.intBitsToFloat(mArray[i++]); 1049 if (Float.isNaN(v)) { 1050 context.listensTo(Utils.idFromNan(v), support); 1051 } 1052 break; 1053 case COLOR_FILTER_ID: 1054 case COLOR_ID: 1055 context.listensTo(mArray[i++], support); 1056 break; 1057 case COLOR: 1058 1059 case TYPEFACE: 1060 case SHADER: 1061 case COLOR_FILTER: 1062 i++; 1063 break; 1064 case STROKE_JOIN: 1065 case FILTER_BITMAP: 1066 case STROKE_CAP: 1067 case STYLE: 1068 case IMAGE_FILTER_QUALITY: 1069 case BLEND_MODE: 1070 case ANTI_ALIAS: 1071 break; 1072 1073 case GRADIENT: 1074 i = callRegisterGradient(cmd, mArray, i, context, support); 1075 } 1076 } 1077 } 1078 1079 /** 1080 * Update variables if any are float ids 1081 * 1082 * @param context 1083 */ updateVariables(@onNull RemoteContext context)1084 public void updateVariables(@NonNull RemoteContext context) { 1085 if (mOutArray == null) { 1086 mOutArray = Arrays.copyOf(mArray, mArray.length); 1087 } else { 1088 System.arraycopy(mArray, 0, mOutArray, 0, mArray.length); 1089 } 1090 int i = 0; 1091 while (i < mPos) { 1092 int cmd = mArray[i++]; 1093 int type = cmd & 0xFFFF; 1094 switch (type) { 1095 case STROKE_MITER: 1096 case STROKE_WIDTH: 1097 case ALPHA: 1098 case TEXT_SIZE: 1099 mOutArray[i] = fixFloatVar(mArray[i], context); 1100 i++; 1101 break; 1102 case COLOR_FILTER_ID: 1103 case COLOR_ID: 1104 mOutArray[i] = fixColor(mArray[i], context); 1105 i++; 1106 break; 1107 case COLOR: 1108 case TYPEFACE: 1109 case SHADER: 1110 case COLOR_FILTER: 1111 i++; 1112 break; 1113 case STROKE_JOIN: 1114 case FILTER_BITMAP: 1115 case STROKE_CAP: 1116 case STYLE: 1117 case IMAGE_FILTER_QUALITY: 1118 case BLEND_MODE: 1119 case ANTI_ALIAS: 1120 case CLEAR_COLOR_FILTER: 1121 break; 1122 1123 case GRADIENT: 1124 // TODO gradients should be handled correctly 1125 i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context); 1126 } 1127 } 1128 } 1129 fixFloatVar(int val, @NonNull RemoteContext context)1130 private int fixFloatVar(int val, @NonNull RemoteContext context) { 1131 float v = Float.intBitsToFloat(val); 1132 if (Float.isNaN(v)) { 1133 int id = Utils.idFromNan(v); 1134 return Float.floatToRawIntBits(context.getFloat(id)); 1135 } 1136 return val; 1137 } 1138 fixColor(int colorId, @NonNull RemoteContext context)1139 private int fixColor(int colorId, @NonNull RemoteContext context) { 1140 int n = context.getColor(colorId); 1141 return n; 1142 } 1143 updateFloatsInGradient( int cmd, int[] out, int[] array, int i, @NonNull RemoteContext context)1144 int updateFloatsInGradient( 1145 int cmd, int[] out, int[] array, int i, @NonNull RemoteContext context) { 1146 int ret = i; 1147 int type = (cmd >> 16); 1148 int control = array[ret++]; 1149 int len = 0xFF & control; // maximum 256 colors 1150 int register = 0xFFFF & (control >> 16); 1151 switch (type) { 1152 case LINEAR_GRADIENT: 1153 if (len > 0) { 1154 1155 for (int j = 0; j < len; j++) { 1156 int color = array[ret]; 1157 if ((register & (1 << j)) != 0) { 1158 out[ret] = fixColor(color, context); 1159 } 1160 ret++; 1161 } 1162 } 1163 len = array[ret++]; 1164 if (len > 0) { 1165 for (int j = 0; j < len; j++) { 1166 out[ret] = fixFloatVar(array[ret], context); 1167 ret++; 1168 } 1169 } 1170 1171 out[ret] = fixFloatVar(array[ret], context); 1172 ret++; 1173 out[ret] = fixFloatVar(array[ret], context); 1174 ret++; 1175 1176 // end 1177 out[ret] = fixFloatVar(array[ret], context); 1178 ret++; 1179 out[ret] = fixFloatVar(array[ret], context); 1180 ret++; 1181 ret++; // tileMode 1182 break; 1183 case RADIAL_GRADIENT: 1184 // RadialGradient 1185 if (len > 0) { 1186 1187 for (int j = 0; j < len; j++) { 1188 int color = array[ret]; 1189 if ((register & (1 << j)) != 0) { 1190 out[ret] = fixColor(color, context); 1191 } 1192 ret++; 1193 } 1194 } 1195 len = array[ret++]; 1196 if (len > 0) { 1197 for (int j = 0; j < len; j++) { 1198 out[ret] = fixFloatVar(array[ret], context); 1199 ret++; 1200 } 1201 } 1202 1203 // center 1204 out[ret] = fixFloatVar(array[ret], context); 1205 ret++; 1206 out[ret] = fixFloatVar(array[ret], context); 1207 ret++; 1208 // radius 1209 out[ret] = fixFloatVar(array[ret], context); 1210 ret++; 1211 ret++; // tileMode 1212 break; 1213 case SWEEP_GRADIENT: 1214 // SweepGradient 1215 if (len > 0) { 1216 1217 for (int j = 0; j < len; j++) { 1218 int color = array[ret]; 1219 if ((register & (1 << j)) != 0) { 1220 out[ret] = fixColor(color, context); 1221 } 1222 ret++; 1223 } 1224 } 1225 len = array[ret++]; 1226 float[] stops = null; 1227 if (len > 0) { 1228 stops = new float[len]; 1229 for (int j = 0; j < stops.length; j++) { 1230 out[ret] = fixFloatVar(array[ret], context); 1231 ret++; 1232 } 1233 } 1234 1235 // center 1236 out[ret] = fixFloatVar(array[ret], context); 1237 ret++; 1238 out[ret] = fixFloatVar(array[ret], context); 1239 ret++; 1240 1241 break; 1242 default: 1243 System.err.println("gradient type unknown"); 1244 } 1245 1246 return ret; 1247 } 1248 1249 @Override serialize(MapSerializer serializer)1250 public void serialize(MapSerializer serializer) { 1251 serializer.addType("PaintBundle"); 1252 List<Map<String, Object>> list = new ArrayList<>(); 1253 int i = 0; 1254 while (i < mPos) { 1255 int cmd = mArray[i++]; 1256 int type = cmd & 0xFFFF; 1257 switch (type) { 1258 case TEXT_SIZE: 1259 list.add(orderedOf("type", "TextSize", "size", getVariable(mArray[i++]))); 1260 break; 1261 case TYPEFACE: 1262 int style = (cmd >> 16); 1263 float weight = (float) (style & 0x3ff); 1264 boolean italic = (style >> 10) > 0; 1265 int fontFamily = mArray[i++]; 1266 list.add(orderedOf("type", "FontFamily", "fontFamily", fontFamily)); 1267 list.add(orderedOf("type", "FontWeight", "weight", weight)); 1268 list.add(orderedOf("type", "TypeFace", "italic", italic)); 1269 break; 1270 case COLOR: 1271 list.add(orderedOf("type", "Color", "color", colorInt(mArray[i++]))); 1272 break; 1273 case COLOR_ID: 1274 list.add(orderedOf("type", "ColorId", "id", mArray[i++])); 1275 break; 1276 case STROKE_WIDTH: 1277 list.add(orderedOf("type", "StrokeWidth", "width", getVariable(mArray[i++]))); 1278 break; 1279 case STROKE_MITER: 1280 list.add(orderedOf("type", "StrokeMiter", "miter", getVariable(mArray[i++]))); 1281 break; 1282 case STROKE_CAP: 1283 list.add(orderedOf("type", "StrokeCap", "cap", cmd >> 16)); 1284 break; 1285 case STYLE: 1286 list.add(orderedOf("type", "Style", "style", cmd >> 16)); 1287 break; 1288 case COLOR_FILTER: 1289 list.add( 1290 orderedOf( 1291 "type", 1292 "ColorFilter", 1293 "color", 1294 colorInt(mArray[i++]), 1295 "mode", 1296 blendModeString(cmd >> 16))); 1297 break; 1298 case COLOR_FILTER_ID: 1299 list.add( 1300 orderedOf( 1301 "type", 1302 "ColorFilterID", 1303 "id", 1304 mArray[i++], 1305 "mode", 1306 blendModeString(cmd >> 16))); 1307 break; 1308 case CLEAR_COLOR_FILTER: 1309 list.add(orderedOf("type", "ClearColorFilter")); 1310 break; 1311 case SHADER: 1312 list.add(orderedOf("type", "Shader", "id", mArray[i++])); 1313 break; 1314 case ALPHA: 1315 list.add(orderedOf("type", "Alpha", "alpha", getVariable(mArray[i++]))); 1316 break; 1317 case IMAGE_FILTER_QUALITY: 1318 list.add(orderedOf("type", "ImageFilterQuality", "quality", cmd >> 16)); 1319 break; 1320 case BLEND_MODE: 1321 list.add(orderedOf("type", "BlendMode", "mode", blendModeString(cmd >> 16))); 1322 break; 1323 case FILTER_BITMAP: 1324 list.add(orderedOf("type", "FilterBitmap", "enabled", !(cmd >> 16 == 0))); 1325 break; 1326 case STROKE_JOIN: 1327 list.add(orderedOf("type", "StrokeJoin", "strokeJoin", cmd >> 16)); 1328 break; 1329 case ANTI_ALIAS: 1330 list.add(orderedOf("type", "AntiAlias", "enabled", !(cmd >> 16 == 0))); 1331 break; 1332 case GRADIENT: 1333 i = serializeGradient(cmd, mArray, i, list); 1334 } 1335 } 1336 serializer.add("operations", list); 1337 } 1338 1339 @SuppressWarnings("JdkImmutableCollections") getVariable(int value)1340 private static Map<String, Object> getVariable(int value) { 1341 float fValue = Float.intBitsToFloat(value); 1342 if (Float.isNaN(fValue)) { 1343 return orderedOf("type", "Variable", "id", Utils.idFromNan(fValue)); 1344 } 1345 return orderedOf("type", "Value", "value", fValue); 1346 } 1347 1348 @SuppressWarnings("JdkImmutableCollections") serializeGradient( int cmd, int[] array, int i, List<Map<String, Object>> list)1349 private static int serializeGradient( 1350 int cmd, int[] array, int i, List<Map<String, Object>> list) { 1351 int ret = i; 1352 int gradientType = (cmd >> 16); 1353 1354 int len = 0xFF & array[ret++]; // maximum 256 colors 1355 1356 String[] colors = null; 1357 if (len > 0) { 1358 colors = new String[len]; 1359 for (int j = 0; j < colors.length; j++) { 1360 colors[j] = colorInt(array[ret++]); 1361 } 1362 } 1363 len = array[ret++]; 1364 float[] stops = null; 1365 if (len > 0) { 1366 stops = new float[len]; 1367 for (int j = 0; j < colors.length; j++) { 1368 stops[j] = Float.intBitsToFloat(array[ret++]); 1369 } 1370 } 1371 1372 if (colors == null) { 1373 return ret; 1374 } 1375 1376 int tileMode; 1377 int centerX; 1378 int centerY; 1379 1380 switch (gradientType) { 1381 case LINEAR_GRADIENT: 1382 int startX = array[ret++]; 1383 int startY = array[ret++]; 1384 int endX = array[ret++]; 1385 int endY = array[ret++]; 1386 tileMode = array[ret++]; 1387 list.add( 1388 orderedOf( 1389 "type", 1390 "LinearGradient", 1391 "colors", 1392 colors, 1393 "stops", 1394 stops == null ? List.of() : stops, 1395 "startX", 1396 getVariable(startX), 1397 "startY", 1398 getVariable(startY), 1399 "endX", 1400 getVariable(endX), 1401 "endY", 1402 getVariable(endY), 1403 "tileMode", 1404 tileMode)); 1405 break; 1406 case RADIAL_GRADIENT: 1407 centerX = array[ret++]; 1408 centerY = array[ret++]; 1409 int radius = array[ret++]; 1410 tileMode = array[ret++]; 1411 list.add( 1412 orderedOf( 1413 "type", 1414 "RadialGradient", 1415 "colors", 1416 colors, 1417 "stops", 1418 stops == null ? List.of() : stops, 1419 "centerX", 1420 getVariable(centerX), 1421 "centerY", 1422 getVariable(centerY), 1423 "radius", 1424 getVariable(radius), 1425 "tileMode", 1426 tileMode)); 1427 break; 1428 case SWEEP_GRADIENT: 1429 centerX = array[ret++]; 1430 centerY = array[ret++]; 1431 list.add( 1432 orderedOf( 1433 "type", 1434 "SweepGradient", 1435 "colors", 1436 colors, 1437 "stops", 1438 stops == null ? List.of() : stops, 1439 "centerX", 1440 getVariable(centerX), 1441 "centerY", 1442 getVariable(centerY))); 1443 } 1444 1445 return ret; 1446 } 1447 } 1448