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 com.android.internal.widget.remotecompose.core.PaintContext;
19 import com.android.internal.widget.remotecompose.core.RemoteContext;
20 import com.android.internal.widget.remotecompose.core.VariableSupport;
21 import com.android.internal.widget.remotecompose.core.WireBuffer;
22 import com.android.internal.widget.remotecompose.core.operations.Utils;
23 
24 import java.util.Arrays;
25 
26 /**
27  * Paint Bundle represents a delta of changes to a paint object
28  */
29 public class PaintBundle {
30     int[] mArray = new int[200];
31     int[] mOutArray = null;
32     int mPos = 0;
33 
34     /**
35      * Apply changes to a PaintChanges interface
36      * @param paintContext
37      * @param p
38      */
applyPaintChange(PaintContext paintContext, PaintChanges p)39     public void applyPaintChange(PaintContext paintContext, PaintChanges p) {
40         int i = 0;
41         int mask = 0;
42         if (mOutArray == null) {
43             mOutArray = mArray;
44         }
45         while (i < mPos) {
46             int cmd = mOutArray[i++];
47             mask = mask | (1 << (cmd - 1));
48             switch (cmd & 0xFFFF) {
49                 case TEXT_SIZE: {
50                     p.setTextSize(Float.intBitsToFloat(mOutArray[i++]));
51                     break;
52                 }
53                 case TYPEFACE:
54                     int style = (cmd >> 16);
55                     int weight = style & 0x3ff;
56                     boolean italic = (style >> 10) > 0;
57                     int font_type = mOutArray[i++];
58 
59                     p.setTypeFace(font_type, weight, italic);
60                     break;
61                 case COLOR_ID: // mOutArray should have already decoded it
62                 case COLOR: {
63                     p.setColor(mOutArray[i++]);
64                     break;
65                 }
66                 case STROKE_WIDTH: {
67                     p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++]));
68                     break;
69                 }
70                 case STROKE_MITER: {
71                     p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++]));
72                     break;
73                 }
74                 case STROKE_CAP: {
75                     p.setStrokeCap(cmd >> 16);
76                     break;
77                 }
78                 case STYLE: {
79                     p.setStyle(cmd >> 16);
80                     break;
81                 }
82                 case SHADER: {
83                     p.setShader(mOutArray[i++]);
84                     break;
85                 }
86                 case STROKE_JOIN: {
87                     p.setStrokeJoin(cmd >> 16);
88                     break;
89                 }
90                 case IMAGE_FILTER_QUALITY: {
91                     p.setImageFilterQuality(cmd >> 16);
92                     break;
93                 }
94                 case BLEND_MODE: {
95                     p.setBlendMode(cmd >> 16);
96                     break;
97                 }
98                 case FILTER_BITMAP: {
99                     p.setFilterBitmap(!((cmd >> 16) == 0));
100                     break;
101                 }
102                 case GRADIENT: {
103                     i = callSetGradient(cmd, mOutArray, i, p);
104                     break;
105                 }
106                 case COLOR_FILTER: {
107                     p.setColorFilter(mOutArray[i++], cmd >> 16);
108                     break;
109                 }
110                 case ALPHA: {
111                     p.setAlpha(Float.intBitsToFloat(mOutArray[i++]));
112                     break;
113                 }
114             }
115         }
116 
117         mask = (~mask) & PaintChanges.VALID_BITS;
118 
119         p.clear(mask);
120     }
121 
toName(int id)122     private String toName(int id) {
123         switch (id) {
124             case TEXT_SIZE:
125                 return "TEXT_SIZE";
126             case COLOR:
127                 return "COLOR";
128             case STROKE_WIDTH:
129                 return "STROKE_WIDTH";
130             case STROKE_MITER:
131                 return "STROKE_MITER";
132             case TYPEFACE:
133                 return "TYPEFACE";
134             case STROKE_CAP:
135                 return "CAP";
136             case STYLE:
137                 return "STYLE";
138             case SHADER:
139                 return "SHADER";
140             case IMAGE_FILTER_QUALITY:
141                 return "IMAGE_FILTER_QUALITY";
142             case BLEND_MODE:
143                 return "BLEND_MODE";
144             case FILTER_BITMAP:
145                 return "FILTER_BITMAP";
146             case GRADIENT:
147                 return "GRADIENT_LINEAR";
148             case ALPHA:
149                 return "ALPHA";
150             case COLOR_FILTER:
151                 return "COLOR_FILTER";
152         }
153         return "????" + id + "????";
154     }
155 
colorInt(int color)156     private static String colorInt(int color) {
157         String str = "000000000000" + Integer.toHexString(color);
158         return "0x" + str.substring(str.length() - 8);
159     }
160 
colorInt(int[] color)161     private static String colorInt(int[] color) {
162         String str = "[";
163         for (int i = 0; i < color.length; i++) {
164             if (i > 0) {
165                 str += ", ";
166             }
167             str += colorInt(color[i]);
168         }
169         return str + "]";
170     }
171 
asFloatStr(int value)172     private static String asFloatStr(int value) {
173         float fValue = Float.intBitsToFloat(value);
174         if (Float.isNaN(fValue)) {
175             return "[" + Utils.idFromNan(fValue) + "]";
176         }
177         return Float.toString(fValue);
178     }
179 
180     @Override
toString()181     public String toString() {
182         StringBuilder ret = new StringBuilder("\n");
183         int i = 0;
184         while (i < mPos) {
185             int cmd = mArray[i++];
186             int type = cmd & 0xFFFF;
187             switch (type) {
188 
189                 case TEXT_SIZE: {
190                     ret.append("    TextSize("
191                             + asFloatStr(mArray[i++]));
192                 }
193 
194                 break;
195                 case TYPEFACE: {
196                     int style = (cmd >> 16);
197                     int weight = style & 0x3ff;
198                     boolean italic = (style >> 10) > 0;
199                     int font_type = mArray[i++];
200                     ret.append("    TypeFace(" + (font_type + ", "
201                             + weight + ", " + italic));
202                 }
203                 break;
204                 case COLOR: {
205                     ret.append("    Color(" + colorInt(mArray[i++]));
206                 }
207                 break;
208                 case COLOR_ID: {
209                     ret.append("    ColorId([" + mArray[i++] + "]");
210                 }
211                 break;
212                 case STROKE_WIDTH: {
213                     ret.append("    StrokeWidth("
214                             + (asFloatStr(mArray[i++])));
215                 }
216                 break;
217                 case STROKE_MITER: {
218                     ret.append("    StrokeMiter("
219                             + (asFloatStr(mArray[i++])));
220                 }
221                 break;
222                 case STROKE_CAP: {
223                     ret.append("    StrokeCap("
224                             + (cmd >> 16));
225                 }
226                 break;
227                 case STYLE: {
228                     ret.append("    Style(" + (cmd >> 16));
229                 }
230                 break;
231                 case COLOR_FILTER: {
232                     ret.append("    ColorFilter(color="
233                             + colorInt(mArray[i++])
234                             + ", mode=" + blendModeString(cmd >> 16));
235                 }
236                 break;
237                 case SHADER: {
238                     ret.append("    Shader(" + mArray[i++]);
239                 }
240                 break;
241                 case ALPHA: {
242                     ret.append("    Alpha("
243                             + (asFloatStr(mArray[i++])));
244                 }
245                 break;
246                 case IMAGE_FILTER_QUALITY: {
247                     ret.append("    ImageFilterQuality(" + (cmd >> 16));
248                 }
249                 break;
250                 case BLEND_MODE: {
251                     ret.append("    BlendMode(" + blendModeString(cmd >> 16));
252                 }
253                 break;
254                 case FILTER_BITMAP: {
255                     ret.append("    FilterBitmap("
256                             + (!((cmd >> 16) == 0)));
257                 }
258                 break;
259                 case STROKE_JOIN: {
260                     ret.append("    StrokeJoin(" + (cmd >> 16));
261                 }
262                 break;
263                 case ANTI_ALIAS: {
264                     ret.append("    AntiAlias(" + (cmd >> 16));
265                 }
266                 break;
267                 case GRADIENT: {
268                     i = callPrintGradient(cmd, mArray, i, ret);
269                 }
270             }
271             ret.append("),\n");
272         }
273         return ret.toString();
274     }
275 
callPrintGradient(int cmd, int[] array, int i, StringBuilder p)276     int callPrintGradient(int cmd, int[] array, int i, StringBuilder p) {
277         int ret = i;
278         int type = (cmd >> 16);
279         switch (type) {
280 
281             case 0: {
282                 p.append("    LinearGradient(\n");
283                 int len = array[ret++];
284                 int[] colors = null;
285                 if (len > 0) {
286                     colors = new int[len];
287                     for (int j = 0; j < colors.length; j++) {
288                         colors[j] = array[ret++];
289                     }
290                 }
291                 len = array[ret++];
292                 String[] stops = null;
293                 if (len > 0) {
294                     stops = new String[len];
295                     for (int j = 0; j < stops.length; j++) {
296                         stops[j] = asFloatStr(array[ret++]);
297                     }
298                 }
299 
300                 p.append("      colors = " + colorInt(colors) + ",\n");
301                 p.append("      stops = " + Arrays.toString(stops) + ",\n");
302                 p.append("      start = ");
303                 p.append("[" + asFloatStr(array[ret++]));
304                 p.append(", " + asFloatStr(array[ret++]) + "],\n");
305                 p.append("      end = ");
306                 p.append("[" + asFloatStr(array[ret++]));
307                 p.append(", " + asFloatStr(array[ret++]) + "],\n");
308                 int tileMode = array[ret++];
309                 p.append("      tileMode = " + tileMode + "\n    ");
310             }
311 
312             break;
313             case 1: {
314                 p.append("    RadialGradient(\n");
315                 int len = array[ret++];
316                 int[] colors = null;
317                 if (len > 0) {
318                     colors = new int[len];
319                     for (int j = 0; j < colors.length; j++) {
320                         colors[j] = array[ret++];
321 
322                     }
323                 }
324                 len = array[ret++];
325                 String[] stops = null;
326                 if (len > 0) {
327                     stops = new String[len];
328                     for (int j = 0; j < stops.length; j++) {
329                         stops[j] = asFloatStr(array[ret++]);
330                     }
331                 }
332 
333                 p.append("      colors = " + colorInt(colors) + ",\n");
334                 p.append("      stops = " + Arrays.toString(stops) + ",\n");
335                 p.append("      center = ");
336                 p.append("[" + asFloatStr(array[ret++]));
337                 p.append(", " + asFloatStr(array[ret++]) + "],\n");
338                 p.append("      radius =");
339                 p.append(" " + asFloatStr(array[ret++]) + ",\n");
340                 int tileMode = array[ret++];
341                 p.append("      tileMode = " + tileMode + "\n    ");
342             }
343 
344             break;
345             case 2: {
346                 p.append("    SweepGradient(\n");
347                 int len = array[ret++];
348                 int[] colors = null;
349                 if (len > 0) {
350                     colors = new int[len];
351                     for (int j = 0; j < colors.length; j++) {
352                         colors[j] = array[ret++];
353 
354                     }
355                 }
356                 len = array[ret++];
357                 String[] stops = null;
358                 if (len > 0) {
359                     stops = new String[len];
360                     for (int j = 0; j < stops.length; j++) {
361                         stops[j] = asFloatStr(array[ret++]);
362                     }
363                 }
364                 p.append("      colors = " + colorInt(colors) + ",\n");
365                 p.append("      stops = " + Arrays.toString(stops) + ",\n");
366                 p.append("      center = ");
367                 p.append("[" + asFloatStr(array[ret++]));
368                 p.append(", "
369                         + asFloatStr(array[ret++]) + "],\n    ");
370             }
371             break;
372             default: {
373                 p.append("GRADIENT_??????!!!!");
374             }
375         }
376 
377         return ret;
378     }
379 
callSetGradient(int cmd, int[] array, int i, PaintChanges p)380     int callSetGradient(int cmd, int[] array, int i, PaintChanges p) {
381         int ret = i;
382         int gradientType = (cmd >> 16);
383 
384         int len = array[ret++];
385         int[] colors = null;
386         if (len > 0) {
387             colors = new int[len];
388             for (int j = 0; j < colors.length; j++) {
389                 colors[j] = array[ret++];
390             }
391         }
392         len = array[ret++];
393         float[] stops = null;
394         if (len > 0) {
395             stops = new float[len];
396             for (int j = 0; j < colors.length; j++) {
397                 stops[j] = Float.intBitsToFloat(array[ret++]);
398             }
399         }
400 
401         if (colors == null) {
402             return ret;
403         }
404 
405         switch (gradientType) {
406 
407             case LINEAR_GRADIENT: {
408                 float startX = Float.intBitsToFloat(array[ret++]);
409                 float startY = Float.intBitsToFloat(array[ret++]);
410                 float endX = Float.intBitsToFloat(array[ret++]);
411                 float endY = Float.intBitsToFloat(array[ret++]);
412                 int tileMode = array[ret++];
413                 p.setLinearGradient(colors, stops, startX,
414                         startY, endX, endY, tileMode);
415             }
416 
417             break;
418             case RADIAL_GRADIENT: {
419                 float centerX = Float.intBitsToFloat(array[ret++]);
420                 float centerY = Float.intBitsToFloat(array[ret++]);
421                 float radius = Float.intBitsToFloat(array[ret++]);
422                 int tileMode = array[ret++];
423                 p.setRadialGradient(colors, stops, centerX, centerY,
424                         radius, tileMode);
425             }
426             break;
427             case SWEEP_GRADIENT: {
428                 float centerX = Float.intBitsToFloat(array[ret++]);
429                 float centerY = Float.intBitsToFloat(array[ret++]);
430                 p.setSweepGradient(colors, stops, centerX, centerY);
431             }
432         }
433 
434         return ret;
435     }
436 
writeBundle(WireBuffer buffer)437     public void writeBundle(WireBuffer buffer) {
438         buffer.writeInt(mPos);
439         for (int index = 0; index < mPos; index++) {
440             buffer.writeInt(mArray[index]);
441         }
442     }
443 
readBundle(WireBuffer buffer)444     public void readBundle(WireBuffer buffer) {
445         int len = buffer.readInt();
446         if (len <= 0 || len > 1024) {
447             throw new RuntimeException("buffer corrupt paint len = " + len);
448         }
449         mArray = new int[len];
450         for (int i = 0; i < mArray.length; i++) {
451             mArray[i] = buffer.readInt();
452         }
453         mPos = len;
454     }
455 
456     public static final int TEXT_SIZE = 1;  // float
457 
458     public static final int COLOR = 4;  // int
459     public static final int STROKE_WIDTH = 5; // float
460     public static final int STROKE_MITER = 6;
461     public static final int STROKE_CAP = 7; //  int
462     public static final int STYLE = 8; // int
463     public static final int SHADER = 9; // int
464     public static final int IMAGE_FILTER_QUALITY = 10; // int
465     public static final int GRADIENT = 11;
466     public static final int ALPHA = 12;
467     public static final int COLOR_FILTER = 13;
468     public static final int ANTI_ALIAS = 14;
469     public static final int STROKE_JOIN = 15;
470     public static final int TYPEFACE = 16;
471     public static final int FILTER_BITMAP = 17;
472     public static final int BLEND_MODE = 18;
473     public static final int COLOR_ID = 19;  // int
474 
475     public static final int BLEND_MODE_CLEAR = 0;
476     public static final int BLEND_MODE_SRC = 1;
477     public static final int BLEND_MODE_DST = 2;
478     public static final int BLEND_MODE_SRC_OVER = 3;
479     public static final int BLEND_MODE_DST_OVER = 4;
480     public static final int BLEND_MODE_SRC_IN = 5;
481     public static final int BLEND_MODE_DST_IN = 6;
482     public static final int BLEND_MODE_SRC_OUT = 7;
483     public static final int BLEND_MODE_DST_OUT = 8;
484     public static final int BLEND_MODE_SRC_ATOP = 9;
485     public static final int BLEND_MODE_DST_ATOP = 10;
486     public static final int BLEND_MODE_XOR = 11;
487     public static final int BLEND_MODE_PLUS = 12;
488     public static final int BLEND_MODE_MODULATE = 13;
489     public static final int BLEND_MODE_SCREEN = 14;
490     public static final int BLEND_MODE_OVERLAY = 15;
491     public static final int BLEND_MODE_DARKEN = 16;
492     public static final int BLEND_MODE_LIGHTEN = 17;
493     public static final int BLEND_MODE_COLOR_DODGE = 18;
494     public static final int BLEND_MODE_COLOR_BURN = 19;
495     public static final int BLEND_MODE_HARD_LIGHT = 20;
496     public static final int BLEND_MODE_SOFT_LIGHT = 21;
497     public static final int BLEND_MODE_DIFFERENCE = 22;
498     public static final int BLEND_MODE_EXCLUSION = 23;
499     public static final int BLEND_MODE_MULTIPLY = 24;
500     public static final int BLEND_MODE_HUE = 25;
501     public static final int BLEND_MODE_SATURATION = 26;
502     public static final int BLEND_MODE_COLOR = 27;
503     public static final int BLEND_MODE_LUMINOSITY = 28;
504     public static final int BLEND_MODE_NULL = 29;
505     public static final int PORTER_MODE_ADD = 30;
506 
507     public static final int FONT_NORMAL = 0;
508     public static final int FONT_BOLD = 1;
509     public static final int FONT_ITALIC = 2;
510     public static final int FONT_BOLD_ITALIC = 3;
511 
512     public static final int FONT_TYPE_DEFAULT = 0;
513     public static final int FONT_TYPE_SANS_SERIF = 1;
514     public static final int FONT_TYPE_SERIF = 2;
515     public static final int FONT_TYPE_MONOSPACE = 3;
516 
517     public static final int STYLE_FILL = 0;
518     public static final int STYLE_STROKE = 1;
519     public static final int STYLE_FILL_AND_STROKE = 2;
520     public static final int LINEAR_GRADIENT = 0;
521     public static final int RADIAL_GRADIENT = 1;
522     public static final int SWEEP_GRADIENT = 2;
523 
524     /**
525      * sets a shader that draws a linear gradient along a line.
526      *
527      * @param startX   The x-coordinate for the start of the gradient line
528      * @param startY   The y-coordinate for the start of the gradient line
529      * @param endX     The x-coordinate for the end of the gradient line
530      * @param endY     The y-coordinate for the end of the gradient line
531      * @param colors   The sRGB colors to be distributed along the gradient line
532      * @param stops    May be null. The relative positions [0..1] of
533      *                 each corresponding color in the colors array. If this is null,
534      *                 the colors are distributed evenly along the gradient line.
535      * @param tileMode The Shader tiling mode
536      */
setLinearGradient(int[] colors, float[] stops, float startX, float startY, float endX, float endY, int tileMode)537     public void setLinearGradient(int[] colors,
538                                   float[] stops,
539                                   float startX,
540                                   float startY,
541                                   float endX,
542                                   float endY,
543                                   int tileMode) {
544         int startPos = mPos;
545         int len;
546         mArray[mPos++] = GRADIENT | (LINEAR_GRADIENT << 16);
547         mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
548         for (int i = 0; i < len; i++) {
549             mArray[mPos++] = colors[i];
550         }
551 
552         mArray[mPos++] = len = (stops == null) ? 0 : stops.length;
553         for (int i = 0; i < len; i++) {
554             mArray[mPos++] = Float.floatToRawIntBits(stops[i]);
555         }
556         mArray[mPos++] = Float.floatToRawIntBits(startX);
557         mArray[mPos++] = Float.floatToRawIntBits(startY);
558         mArray[mPos++] = Float.floatToRawIntBits(endX);
559         mArray[mPos++] = Float.floatToRawIntBits(endY);
560         mArray[mPos++] = tileMode;
561     }
562 
563     /**
564      * Set a shader that draws a sweep gradient around a center point.
565      *
566      * @param centerX The x-coordinate of the center
567      * @param centerY The y-coordinate of the center
568      * @param colors  The sRGB colors to be distributed around the center.
569      *                There must be at least 2 colors in the array.
570      * @param stops   May be NULL. The relative position of
571      *                each corresponding color in the colors array, beginning
572      *                with 0 and ending with 1.0. If the values are not
573      *                monotonic, the drawing may produce unexpected results.
574      *                If positions is NULL, then the colors are automatically
575      *                spaced evenly.
576      */
setSweepGradient(int[] colors, float[] stops, float centerX, float centerY)577     public void setSweepGradient(int[] colors, float[] stops, float centerX, float centerY) {
578         int startPos = mPos;
579         int len;
580         mArray[mPos++] = GRADIENT | (SWEEP_GRADIENT << 16);
581         mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
582         for (int i = 0; i < len; i++) {
583             mArray[mPos++] = colors[i];
584         }
585 
586         mArray[mPos++] = len = (stops == null) ? 0 : stops.length;
587         for (int i = 0; i < len; i++) {
588             mArray[mPos++] = Float.floatToRawIntBits(stops[i]);
589         }
590         mArray[mPos++] = Float.floatToRawIntBits(centerX);
591         mArray[mPos++] = Float.floatToRawIntBits(centerY);
592     }
593 
594     /**
595      * Sets a shader that draws a radial gradient given the center and radius.
596      *
597      * @param centerX  The x-coordinate of the center of the radius
598      * @param centerY  The y-coordinate of the center of the radius
599      * @param radius   Must be positive. The radius of the gradient.
600      * @param colors   The sRGB colors distributed between the center and edge
601      * @param stops    May be <code>null</code>.
602      *                 Valid values are between <code>0.0f</code> and
603      *                 <code>1.0f</code>. The relative position of each
604      *                 corresponding color in
605      *                 the colors array. If <code>null</code>, colors are
606      *                 distributed evenly
607      *                 between the center and edge of the circle.
608      * @param tileMode The Shader tiling mode
609      */
setRadialGradient(int[] colors, float[] stops, float centerX, float centerY, float radius, int tileMode)610     public void setRadialGradient(int[] colors,
611                                   float[] stops,
612                                   float centerX,
613                                   float centerY,
614                                   float radius,
615                                   int tileMode) {
616         int startPos = mPos;
617         int len;
618         mArray[mPos++] = GRADIENT | (RADIAL_GRADIENT << 16);
619         mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
620         for (int i = 0; i < len; i++) {
621             mArray[mPos++] = colors[i];
622         }
623         mArray[mPos++] = len = (stops == null) ? 0 : stops.length;
624 
625         for (int i = 0; i < len; i++) {
626             mArray[mPos++] = Float.floatToRawIntBits(stops[i]);
627         }
628         mArray[mPos++] = Float.floatToRawIntBits(centerX);
629         mArray[mPos++] = Float.floatToRawIntBits(centerY);
630         mArray[mPos++] = Float.floatToRawIntBits(radius);
631         mArray[mPos++] = tileMode;
632 
633     }
634 
635     /**
636      * Create a color filter that uses the specified color and Porter-Duff mode.
637      *
638      * @param color The ARGB source color used with the Porter-Duff mode
639      * @param mode  The porter-duff mode that is applied
640      */
setColorFilter(int color, int mode)641     public void setColorFilter(int color, int mode) {
642         mArray[mPos] = COLOR_FILTER | (mode << 16);
643         mPos++;
644         mArray[mPos++] = color;
645     }
646 
647     /**
648      * Set the paint's text size. This value must be > 0
649      *
650      * @param size set the paint's text size in pixel units.
651      */
setTextSize(float size)652     public void setTextSize(float size) {
653         int p = mPos;
654         mArray[mPos] = TEXT_SIZE;
655         mPos++;
656         mArray[mPos] = Float.floatToRawIntBits(size);
657         mPos++;
658     }
659 
660     /**
661      * @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
662      * @param weight   100-1000
663      * @param italic   tur
664      */
setTextStyle(int fontType, int weight, boolean italic)665     public void setTextStyle(int fontType, int weight, boolean italic) {
666         int style = (weight & 0x3FF) | (italic ? 2048 : 0);  // pack the weight and italic
667         mArray[mPos++] = TYPEFACE | (style << 16);
668         mArray[mPos++] = fontType;
669     }
670 
671     /**
672      * Set the width for stroking.
673      * Pass 0 to stroke in hairline mode.
674      * Hairlines always draws a single pixel independent of the canvas's matrix.
675      *
676      * @param width set the paint's stroke width, used whenever the paint's
677      *              style is Stroke or StrokeAndFill.
678      */
setStrokeWidth(float width)679     public void setStrokeWidth(float width) {
680         mArray[mPos] = STROKE_WIDTH;
681         mPos++;
682         mArray[mPos] = Float.floatToRawIntBits(width);
683         mPos++;
684     }
685 
686     /**
687      * Set the Color based on Color
688      * @param color
689      */
setColor(int color)690     public void setColor(int color) {
691         mArray[mPos] = COLOR;
692         mPos++;
693         mArray[mPos] = color;
694         mPos++;
695     }
696 
697     /**
698      * Set the Color based on ID
699      * @param color
700      */
setColorId(int color)701     public void setColorId(int color) {
702         mArray[mPos] = COLOR_ID;
703         mPos++;
704         mArray[mPos] = color;
705         mPos++;
706     }
707 
708 
709     /**
710      * Set the paint's Cap.
711      *
712      * @param cap set the paint's line cap style, used whenever the paint's
713      *            style is Stroke or StrokeAndFill.
714      */
setStrokeCap(int cap)715     public void setStrokeCap(int cap) {
716         mArray[mPos] = STROKE_CAP | (cap << 16);
717         mPos++;
718     }
719 
720     /**
721      * Set the style STROKE and/or FILL
722      * @param style
723      */
setStyle(int style)724     public void setStyle(int style) {
725         mArray[mPos] = STYLE | (style << 16);
726         mPos++;
727     }
728 
729     /**
730      * Set the shader id to use
731      * @param shaderId
732      */
setShader(int shaderId)733     public void setShader(int shaderId) {
734         mArray[mPos] = SHADER;
735         mPos++;
736         mArray[mPos] = shaderId;
737         mPos++;
738     }
739 
740     /**
741      * Set the Alpha value
742      */
setAlpha(float alpha)743     public void setAlpha(float alpha) {
744         mArray[mPos] = ALPHA;
745         mPos++;
746         mArray[mPos] = Float.floatToRawIntBits(alpha);
747         mPos++;
748     }
749 
750     /**
751      * Set the paint's stroke miter value. This is used to control the behavior
752      * of miter joins when the joins angle is sharp. This value must be >= 0.
753      *
754      * @param miter set the miter limit on the paint, used whenever the paint's
755      *              style is Stroke or StrokeAndFill.
756      */
setStrokeMiter(float miter)757     public void setStrokeMiter(float miter) {
758         mArray[mPos] = STROKE_MITER;
759         mPos++;
760         mArray[mPos] = Float.floatToRawIntBits(miter);
761         mPos++;
762     }
763 
764     /**
765      * Set the paint's Join.
766      *
767      * @param join set the paint's Join, used whenever the paint's style is
768      *             Stroke or StrokeAndFill.
769      */
setStrokeJoin(int join)770     public void setStrokeJoin(int join) {
771         mArray[mPos] = STROKE_JOIN | (join << 16);
772         mPos++;
773     }
774 
setFilterBitmap(boolean filter)775     public void setFilterBitmap(boolean filter) {
776         mArray[mPos] = FILTER_BITMAP | (filter ? (1 << 16) : 0);
777         mPos++;
778     }
779 
780     /**
781      * Set or clear the blend mode. A blend mode defines how source pixels
782      * (generated by a drawing command) are composited with the
783      * destination pixels
784      * (content of the render target).
785      *
786      * @param blendmode The blend mode to be installed in the paint
787      */
setBlendMode(int blendmode)788     public void setBlendMode(int blendmode) {
789         mArray[mPos] = BLEND_MODE | (blendmode << 16);
790         mPos++;
791     }
792 
793     /**
794      * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit
795      * AntiAliasing smooths out the edges of what is being drawn, but is has
796      * no impact on the interior of the shape. See setDither() and
797      * setFilterBitmap() to affect how colors are treated.
798      *
799      * @param aa true to set the antialias bit in the flags, false to clear it
800      */
setAntiAlias(boolean aa)801     public void setAntiAlias(boolean aa) {
802         mArray[mPos] = ANTI_ALIAS | (((aa) ? 1 : 0) << 16);
803         mPos++;
804     }
805 
clear(long mask)806     public void clear(long mask) { // unused for now
807     }
808 
reset()809     public void reset() {
810         mPos = 0;
811     }
812 
blendModeString(int mode)813     public static String blendModeString(int mode) {
814         switch (mode) {
815             case PaintBundle.BLEND_MODE_CLEAR:
816                 return "CLEAR";
817             case PaintBundle.BLEND_MODE_SRC:
818                 return "SRC";
819             case PaintBundle.BLEND_MODE_DST:
820                 return "DST";
821             case PaintBundle.BLEND_MODE_SRC_OVER:
822                 return "SRC_OVER";
823             case PaintBundle.BLEND_MODE_DST_OVER:
824                 return "DST_OVER";
825             case PaintBundle.BLEND_MODE_SRC_IN:
826                 return "SRC_IN";
827             case PaintBundle.BLEND_MODE_DST_IN:
828                 return "DST_IN";
829             case PaintBundle.BLEND_MODE_SRC_OUT:
830                 return "SRC_OUT";
831             case PaintBundle.BLEND_MODE_DST_OUT:
832                 return "DST_OUT";
833             case PaintBundle.BLEND_MODE_SRC_ATOP:
834                 return "SRC_ATOP";
835             case PaintBundle.BLEND_MODE_DST_ATOP:
836                 return "DST_ATOP";
837             case PaintBundle.BLEND_MODE_XOR:
838                 return "XOR";
839             case PaintBundle.BLEND_MODE_PLUS:
840                 return "PLUS";
841             case PaintBundle.BLEND_MODE_MODULATE:
842                 return "MODULATE";
843             case PaintBundle.BLEND_MODE_SCREEN:
844                 return "SCREEN";
845             case PaintBundle.BLEND_MODE_OVERLAY:
846                 return "OVERLAY";
847             case PaintBundle.BLEND_MODE_DARKEN:
848                 return "DARKEN";
849             case PaintBundle.BLEND_MODE_LIGHTEN:
850                 return "LIGHTEN";
851             case PaintBundle.BLEND_MODE_COLOR_DODGE:
852                 return "COLOR_DODGE";
853             case PaintBundle.BLEND_MODE_COLOR_BURN:
854                 return "COLOR_BURN";
855             case PaintBundle.BLEND_MODE_HARD_LIGHT:
856                 return "HARD_LIGHT";
857             case PaintBundle.BLEND_MODE_SOFT_LIGHT:
858                 return "SOFT_LIGHT";
859             case PaintBundle.BLEND_MODE_DIFFERENCE:
860                 return "DIFFERENCE";
861             case PaintBundle.BLEND_MODE_EXCLUSION:
862                 return "EXCLUSION";
863             case PaintBundle.BLEND_MODE_MULTIPLY:
864                 return "MULTIPLY";
865             case PaintBundle.BLEND_MODE_HUE:
866                 return "HUE";
867             case PaintBundle.BLEND_MODE_SATURATION:
868                 return "SATURATION";
869             case PaintBundle.BLEND_MODE_COLOR:
870                 return "COLOR";
871             case PaintBundle.BLEND_MODE_LUMINOSITY:
872                 return "LUMINOSITY";
873             case PaintBundle.BLEND_MODE_NULL:
874                 return "null";
875             case PaintBundle.PORTER_MODE_ADD:
876                 return "ADD";
877         }
878         return "null";
879     }
880 
881     /**
882      * Check all the floats for Nan(id) floats and call listenTo
883      * @param context
884      * @param support
885      */
registerVars(RemoteContext context, VariableSupport support)886     public void registerVars(RemoteContext context, VariableSupport support) {
887         int i = 0;
888         while (i < mPos) {
889             int cmd = mArray[i++];
890             int type = cmd & 0xFFFF;
891             switch (type) {
892                 case STROKE_MITER:
893                 case STROKE_WIDTH:
894                 case ALPHA:
895                 case TEXT_SIZE:
896                     float v = Float.intBitsToFloat(mArray[i++]);
897                     if (Float.isNaN(v)) {
898                         context.listensTo(Utils.idFromNan(v), support);
899                     }
900                     break;
901                 case COLOR_ID:
902                     context.listensTo(mArray[i++], support);
903                     break;
904                 case COLOR:
905 
906                 case TYPEFACE:
907                 case SHADER:
908                 case COLOR_FILTER:
909                     i++;
910                     break;
911                 case STROKE_JOIN:
912                 case FILTER_BITMAP:
913                 case STROKE_CAP:
914                 case STYLE:
915                 case IMAGE_FILTER_QUALITY:
916                 case BLEND_MODE:
917                 case ANTI_ALIAS:
918                     break;
919 
920                 case GRADIENT: {
921                     // TODO gradients should be handled correctly
922                     i = callPrintGradient(cmd, mArray, i, new StringBuilder());
923                 }
924             }
925         }
926     }
927 
928     /**
929      * Update variables if any are float ids
930      * @param context
931      */
updateVariables(RemoteContext context)932     public void updateVariables(RemoteContext context) {
933         if (mOutArray == null) {
934             mOutArray = Arrays.copyOf(mArray, mArray.length);
935         } else {
936             System.arraycopy(mArray, 0, mOutArray, 0, mArray.length);
937         }
938         int i = 0;
939         while (i < mPos) {
940             int cmd = mArray[i++];
941             int type = cmd & 0xFFFF;
942             switch (type) {
943                 case STROKE_MITER:
944                 case STROKE_WIDTH:
945                 case ALPHA:
946                 case TEXT_SIZE:
947                     mOutArray[i] = fixFloatVar(mArray[i], context);
948                     i++;
949                     break;
950                 case COLOR_ID:
951                     mOutArray[i] = fixColor(mArray[i], context);
952                     i++;
953                     break;
954                 case COLOR:
955                 case TYPEFACE:
956                 case SHADER:
957                 case COLOR_FILTER:
958                     i++;
959                     break;
960                 case STROKE_JOIN:
961                 case FILTER_BITMAP:
962                 case STROKE_CAP:
963                 case STYLE:
964                 case IMAGE_FILTER_QUALITY:
965                 case BLEND_MODE:
966                 case ANTI_ALIAS:
967                     break;
968 
969                 case GRADIENT: {
970                     // TODO gradients should be handled correctly
971                     i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context);
972                 }
973             }
974         }
975     }
976 
fixFloatVar(int val, RemoteContext context)977     private int fixFloatVar(int val, RemoteContext context) {
978         float v = Float.intBitsToFloat(val);
979         if (Float.isNaN(v)) {
980             int id = Utils.idFromNan(v);
981             return Float.floatToRawIntBits(context.getFloat(id));
982         }
983         return val;
984     }
985 
fixColor(int colorId, RemoteContext context)986     private int fixColor(int colorId, RemoteContext context) {
987         int n = context.getColor(colorId);
988         return n;
989     }
990 
updateFloatsInGradient(int cmd, int[] out, int[] array, int i, RemoteContext context)991     int updateFloatsInGradient(int cmd, int[] out, int[] array,
992                                int i,
993                                RemoteContext context) {
994         int ret = i;
995         int type = (cmd >> 16);
996         switch (type) {
997             case 0: {
998                 int len = array[ret++];
999                 if (len > 0) {
1000                     for (int j = 0; j < len; j++) {
1001                         ret++;
1002                     }
1003                 }
1004                 len = array[ret++];
1005 
1006                 if (len > 0) {
1007                     for (int j = 0; j < len; j++) {
1008                         out[ret] = fixFloatVar(array[ret], context);
1009                         ret++;
1010                     }
1011                 }
1012 
1013                 out[ret] = fixFloatVar(array[ret], context);
1014                 ret++;
1015                 out[ret] = fixFloatVar(array[ret], context);
1016                 ret++;
1017 
1018                 //      end
1019                 out[ret] = fixFloatVar(array[ret], context);
1020                 ret++;
1021                 out[ret] = fixFloatVar(array[ret], context);
1022                 ret++;
1023                 ret++; // tileMode
1024             }
1025 
1026             break;
1027             case 1: {
1028                 //   RadialGradient
1029                 int len = array[ret++];
1030                 if (len > 0) {
1031                     for (int j = 0; j < len; j++) {
1032                         ret++;
1033                     }
1034                 }
1035                 len = array[ret++];
1036                 if (len > 0) {
1037                     for (int j = 0; j < len; j++) {
1038                         out[ret] = fixFloatVar(array[ret], context);
1039                         ret++;
1040                     }
1041                 }
1042 
1043 
1044                 //    center
1045                 out[ret] = fixFloatVar(array[ret], context);
1046                 ret++;
1047                 out[ret] = fixFloatVar(array[ret], context);
1048                 ret++;
1049                 //     radius
1050                 out[ret] = fixFloatVar(array[ret], context);
1051                 ret++;
1052                 ret++; // tileMode
1053 
1054             }
1055 
1056             break;
1057             case 2: {
1058                 //   SweepGradient
1059                 int len = array[ret++];
1060                 int[] colors = null;
1061                 if (len > 0) {
1062                     colors = new int[len];
1063                     for (int j = 0; j < colors.length; j++) {
1064                         colors[j] = array[ret++];
1065 
1066                     }
1067                 }
1068                 len = array[ret++];
1069                 float[] stops = null;
1070                 if (len > 0) {
1071                     stops = new float[len];
1072                     for (int j = 0; j < stops.length; j++) {
1073                         out[ret] = fixFloatVar(array[ret], context);
1074                         ret++;
1075                     }
1076                 }
1077 
1078                 //      center
1079                 out[ret] = fixFloatVar(array[ret], context);
1080                 ret++;
1081                 out[ret] = fixFloatVar(array[ret], context);
1082                 ret++;
1083             }
1084             break;
1085             default: {
1086                 System.err.println("gradient type unknown");
1087             }
1088         }
1089 
1090         return ret;
1091     }
1092 
1093 }