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.player.platform;
17 
18 import android.graphics.Bitmap;
19 import android.graphics.BlendMode;
20 import android.graphics.Canvas;
21 import android.graphics.LinearGradient;
22 import android.graphics.Paint;
23 import android.graphics.Path;
24 import android.graphics.PorterDuff;
25 import android.graphics.PorterDuffColorFilter;
26 import android.graphics.RadialGradient;
27 import android.graphics.Rect;
28 import android.graphics.RectF;
29 import android.graphics.RuntimeShader;
30 import android.graphics.Shader;
31 import android.graphics.SweepGradient;
32 import android.graphics.Typeface;
33 
34 import com.android.internal.widget.remotecompose.core.PaintContext;
35 import com.android.internal.widget.remotecompose.core.RemoteContext;
36 import com.android.internal.widget.remotecompose.core.operations.ClipPath;
37 import com.android.internal.widget.remotecompose.core.operations.ShaderData;
38 import com.android.internal.widget.remotecompose.core.operations.Utils;
39 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
40 import com.android.internal.widget.remotecompose.core.operations.paint.PaintChanges;
41 
42 /**
43  * An implementation of PaintContext for the Android Canvas.
44  * This is used to play the RemoteCompose operations on Android.
45  */
46 public class AndroidPaintContext extends PaintContext {
47     Paint mPaint = new Paint();
48     Canvas mCanvas;
49     Rect mTmpRect = new Rect(); // use in calculation of bounds
50 
AndroidPaintContext(RemoteContext context, Canvas canvas)51     public AndroidPaintContext(RemoteContext context, Canvas canvas) {
52         super(context);
53         this.mCanvas = canvas;
54     }
55 
getCanvas()56     public Canvas getCanvas() {
57         return mCanvas;
58     }
59 
setCanvas(Canvas canvas)60     public void setCanvas(Canvas canvas) {
61         this.mCanvas = canvas;
62     }
63 
64     /**
65      * Draw an image onto the canvas
66      *
67      * @param imageId   the id of the image
68      * @param srcLeft   left coordinate of the source area
69      * @param srcTop    top coordinate of the source area
70      * @param srcRight  right coordinate of the source area
71      * @param srcBottom bottom coordinate of the source area
72      * @param dstLeft   left coordinate of the destination area
73      * @param dstTop    top coordinate of the destination area
74      * @param dstRight  right coordinate of the destination area
75      * @param dstBottom bottom coordinate of the destination area
76      */
77 
78     @Override
drawBitmap(int imageId, int srcLeft, int srcTop, int srcRight, int srcBottom, int dstLeft, int dstTop, int dstRight, int dstBottom, int cdId)79     public void drawBitmap(int imageId,
80                            int srcLeft,
81                            int srcTop,
82                            int srcRight,
83                            int srcBottom,
84                            int dstLeft,
85                            int dstTop,
86                            int dstRight,
87                            int dstBottom,
88                            int cdId) {
89         AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
90         if (androidContext.mRemoteComposeState.containsId(imageId)) {
91             Bitmap bitmap = (Bitmap) androidContext.mRemoteComposeState
92                     .getFromId(imageId);
93             mCanvas.drawBitmap(
94                     bitmap,
95                     new Rect(srcLeft, srcTop, srcRight, srcBottom),
96                     new Rect(dstLeft, dstTop, dstRight, dstBottom), mPaint
97             );
98         }
99     }
100 
101     @Override
scale(float scaleX, float scaleY)102     public void scale(float scaleX, float scaleY) {
103         mCanvas.scale(scaleX, scaleY);
104     }
105 
106     @Override
translate(float translateX, float translateY)107     public void translate(float translateX, float translateY) {
108         mCanvas.translate(translateX, translateY);
109     }
110 
111     @Override
drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle)112     public void drawArc(float left,
113                         float top,
114                         float right,
115                         float bottom,
116                         float startAngle,
117                         float sweepAngle) {
118         mCanvas.drawArc(left, top, right, bottom, startAngle,
119                 sweepAngle, true, mPaint);
120     }
121 
122     @Override
drawBitmap(int id, float left, float top, float right, float bottom)123     public void drawBitmap(int id,
124                            float left,
125                            float top,
126                            float right,
127                            float bottom) {
128         AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
129         if (androidContext.mRemoteComposeState.containsId(id)) {
130             Bitmap bitmap =
131                     (Bitmap) androidContext.mRemoteComposeState.getFromId(id);
132             Rect src = new Rect(0, 0,
133                     bitmap.getWidth(), bitmap.getHeight());
134             RectF dst = new RectF(left, top, right, bottom);
135             mCanvas.drawBitmap(bitmap, src, dst, mPaint);
136         }
137     }
138 
139     @Override
drawCircle(float centerX, float centerY, float radius)140     public void drawCircle(float centerX, float centerY, float radius) {
141         mCanvas.drawCircle(centerX, centerY, radius, mPaint);
142     }
143 
144     @Override
drawLine(float x1, float y1, float x2, float y2)145     public void drawLine(float x1, float y1, float x2, float y2) {
146         mCanvas.drawLine(x1, y1, x2, y2, mPaint);
147     }
148 
149     @Override
drawOval(float left, float top, float right, float bottom)150     public void drawOval(float left, float top, float right, float bottom) {
151         mCanvas.drawOval(left, top, right, bottom, mPaint);
152     }
153 
154     @Override
drawPath(int id, float start, float end)155     public void drawPath(int id, float start, float end) {
156         mCanvas.drawPath(getPath(id, start, end), mPaint);
157     }
158 
159     @Override
drawRect(float left, float top, float right, float bottom)160     public void drawRect(float left, float top, float right, float bottom) {
161         mCanvas.drawRect(left, top, right, bottom, mPaint);
162     }
163 
164     @Override
drawRoundRect(float left, float top, float right, float bottom, float radiusX, float radiusY)165     public void drawRoundRect(float left,
166                               float top,
167                               float right,
168                               float bottom,
169                               float radiusX,
170                               float radiusY) {
171         mCanvas.drawRoundRect(left, top, right, bottom,
172                 radiusX, radiusY, mPaint);
173     }
174 
175     @Override
drawTextOnPath(int textId, int pathId, float hOffset, float vOffset)176     public void drawTextOnPath(int textId,
177                                int pathId,
178                                float hOffset,
179                                float vOffset) {
180         mCanvas.drawTextOnPath(getText(textId), getPath(pathId, 0, 1), hOffset, vOffset, mPaint);
181     }
182 
183     @Override
getTextBounds(int textId, int start, int end, boolean monospace, float[] bounds)184     public void getTextBounds(int textId, int start, int end, boolean monospace, float[] bounds) {
185         String str = getText(textId);
186         if (end == -1) {
187             end = str.length();
188         }
189 
190         mPaint.getTextBounds(str, start, end, mTmpRect);
191 
192         bounds[0] = mTmpRect.left;
193         bounds[1] = mTmpRect.top;
194         bounds[2] = monospace ? (mPaint.measureText(str, start, end) - mTmpRect.left)
195                 : mTmpRect.right;
196         bounds[3] = mTmpRect.bottom;
197     }
198 
199     @Override
drawTextRun(int textID, int start, int end, int contextStart, int contextEnd, float x, float y, boolean rtl)200     public void drawTextRun(int textID,
201                             int start,
202                             int end,
203                             int contextStart,
204                             int contextEnd,
205                             float x,
206                             float y,
207                             boolean rtl) {
208 
209         String textToPaint = getText(textID);
210         if (end == -1) {
211             if (start != 0) {
212                 textToPaint = textToPaint.substring(start);
213             }
214         } else {
215             textToPaint = textToPaint.substring(start, end);
216         }
217 
218         mCanvas.drawText(textToPaint, x, y, mPaint);
219     }
220 
221     @Override
drawTweenPath(int path1Id, int path2Id, float tween, float start, float end)222     public void drawTweenPath(int path1Id,
223                               int path2Id,
224                               float tween,
225                               float start,
226                               float end) {
227         mCanvas.drawPath(getPath(path1Id, path2Id, tween, start, end), mPaint);
228     }
229 
origamiToPorterDuffMode(int mode)230     private static PorterDuff.Mode origamiToPorterDuffMode(int mode) {
231         switch (mode) {
232             case PaintBundle.BLEND_MODE_CLEAR:
233                 return PorterDuff.Mode.CLEAR;
234             case PaintBundle.BLEND_MODE_SRC:
235                 return PorterDuff.Mode.SRC;
236             case PaintBundle.BLEND_MODE_DST:
237                 return PorterDuff.Mode.DST;
238             case PaintBundle.BLEND_MODE_SRC_OVER:
239                 return PorterDuff.Mode.SRC_OVER;
240             case PaintBundle.BLEND_MODE_DST_OVER:
241                 return PorterDuff.Mode.DST_OVER;
242             case PaintBundle.BLEND_MODE_SRC_IN:
243                 return PorterDuff.Mode.SRC_IN;
244             case PaintBundle.BLEND_MODE_DST_IN:
245                 return PorterDuff.Mode.DST_IN;
246             case PaintBundle.BLEND_MODE_SRC_OUT:
247                 return PorterDuff.Mode.SRC_OUT;
248             case PaintBundle.BLEND_MODE_DST_OUT:
249                 return PorterDuff.Mode.DST_OUT;
250             case PaintBundle.BLEND_MODE_SRC_ATOP:
251                 return PorterDuff.Mode.SRC_ATOP;
252             case PaintBundle.BLEND_MODE_DST_ATOP:
253                 return PorterDuff.Mode.DST_ATOP;
254             case PaintBundle.BLEND_MODE_XOR:
255                 return PorterDuff.Mode.XOR;
256             case PaintBundle.BLEND_MODE_SCREEN:
257                 return PorterDuff.Mode.SCREEN;
258             case PaintBundle.BLEND_MODE_OVERLAY:
259                 return PorterDuff.Mode.OVERLAY;
260             case PaintBundle.BLEND_MODE_DARKEN:
261                 return PorterDuff.Mode.DARKEN;
262             case PaintBundle.BLEND_MODE_LIGHTEN:
263                 return PorterDuff.Mode.LIGHTEN;
264             case PaintBundle.BLEND_MODE_MULTIPLY:
265                 return PorterDuff.Mode.MULTIPLY;
266             case PaintBundle.PORTER_MODE_ADD:
267                 return PorterDuff.Mode.ADD;
268         }
269         return PorterDuff.Mode.SRC_OVER;
270     }
271 
origamiToBlendMode(int mode)272     public static BlendMode origamiToBlendMode(int mode) {
273         switch (mode) {
274             case PaintBundle.BLEND_MODE_CLEAR:
275                 return BlendMode.CLEAR;
276             case PaintBundle.BLEND_MODE_SRC:
277                 return BlendMode.SRC;
278             case PaintBundle.BLEND_MODE_DST:
279                 return BlendMode.DST;
280             case PaintBundle.BLEND_MODE_SRC_OVER:
281                 return BlendMode.SRC_OVER;
282             case PaintBundle.BLEND_MODE_DST_OVER:
283                 return BlendMode.DST_OVER;
284             case PaintBundle.BLEND_MODE_SRC_IN:
285                 return BlendMode.SRC_IN;
286             case PaintBundle.BLEND_MODE_DST_IN:
287                 return BlendMode.DST_IN;
288             case PaintBundle.BLEND_MODE_SRC_OUT:
289                 return BlendMode.SRC_OUT;
290             case PaintBundle.BLEND_MODE_DST_OUT:
291                 return BlendMode.DST_OUT;
292             case PaintBundle.BLEND_MODE_SRC_ATOP:
293                 return BlendMode.SRC_ATOP;
294             case PaintBundle.BLEND_MODE_DST_ATOP:
295                 return BlendMode.DST_ATOP;
296             case PaintBundle.BLEND_MODE_XOR:
297                 return BlendMode.XOR;
298             case PaintBundle.BLEND_MODE_PLUS:
299                 return BlendMode.PLUS;
300             case PaintBundle.BLEND_MODE_MODULATE:
301                 return BlendMode.MODULATE;
302             case PaintBundle.BLEND_MODE_SCREEN:
303                 return BlendMode.SCREEN;
304             case PaintBundle.BLEND_MODE_OVERLAY:
305                 return BlendMode.OVERLAY;
306             case PaintBundle.BLEND_MODE_DARKEN:
307                 return BlendMode.DARKEN;
308             case PaintBundle.BLEND_MODE_LIGHTEN:
309                 return BlendMode.LIGHTEN;
310             case PaintBundle.BLEND_MODE_COLOR_DODGE:
311                 return BlendMode.COLOR_DODGE;
312             case PaintBundle.BLEND_MODE_COLOR_BURN:
313                 return BlendMode.COLOR_BURN;
314             case PaintBundle.BLEND_MODE_HARD_LIGHT:
315                 return BlendMode.HARD_LIGHT;
316             case PaintBundle.BLEND_MODE_SOFT_LIGHT:
317                 return BlendMode.SOFT_LIGHT;
318             case PaintBundle.BLEND_MODE_DIFFERENCE:
319                 return BlendMode.DIFFERENCE;
320             case PaintBundle.BLEND_MODE_EXCLUSION:
321                 return BlendMode.EXCLUSION;
322             case PaintBundle.BLEND_MODE_MULTIPLY:
323                 return BlendMode.MULTIPLY;
324             case PaintBundle.BLEND_MODE_HUE:
325                 return BlendMode.HUE;
326             case PaintBundle.BLEND_MODE_SATURATION:
327                 return BlendMode.SATURATION;
328             case PaintBundle.BLEND_MODE_COLOR:
329                 return BlendMode.COLOR;
330             case PaintBundle.BLEND_MODE_LUMINOSITY:
331                 return BlendMode.LUMINOSITY;
332             case PaintBundle.BLEND_MODE_NULL:
333                 return null;
334         }
335         return null;
336     }
337 
338     @Override
applyPaint(PaintBundle mPaintData)339     public void applyPaint(PaintBundle mPaintData) {
340         mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() {
341             @Override
342             public void setTextSize(float size) {
343                 mPaint.setTextSize(size);
344             }
345 
346             @Override
347             public void setTypeFace(int fontType, int weight, boolean italic) {
348                 int[] type = new int[]{Typeface.NORMAL, Typeface.BOLD,
349                         Typeface.ITALIC, Typeface.BOLD_ITALIC};
350 
351                 switch (fontType) {
352                     case PaintBundle.FONT_TYPE_DEFAULT: {
353                         if (weight == 400 && !italic) { // for normal case
354                             mPaint.setTypeface(Typeface.DEFAULT);
355                         } else {
356                             mPaint.setTypeface(Typeface.create(Typeface.DEFAULT,
357                                     weight, italic));
358                         }
359                         break;
360                     }
361                     case PaintBundle.FONT_TYPE_SERIF: {
362                         if (weight == 400 && !italic) { // for normal case
363                             mPaint.setTypeface(Typeface.SERIF);
364                         } else {
365                             mPaint.setTypeface(Typeface.create(Typeface.SERIF,
366                                     weight, italic));
367                         }
368                         break;
369                     }
370                     case PaintBundle.FONT_TYPE_SANS_SERIF: {
371                         if (weight == 400 && !italic) { //  for normal case
372                             mPaint.setTypeface(Typeface.SANS_SERIF);
373                         } else {
374                             mPaint.setTypeface(
375                                     Typeface.create(Typeface.SANS_SERIF,
376                                             weight, italic));
377                         }
378                         break;
379                     }
380                     case PaintBundle.FONT_TYPE_MONOSPACE: {
381                         if (weight == 400 && !italic) { //  for normal case
382                             mPaint.setTypeface(Typeface.MONOSPACE);
383                         } else {
384                             mPaint.setTypeface(
385                                     Typeface.create(Typeface.MONOSPACE,
386                                             weight, italic));
387                         }
388 
389                         break;
390                     }
391                 }
392 
393             }
394 
395             @Override
396             public void setStrokeWidth(float width) {
397                 mPaint.setStrokeWidth(width);
398             }
399 
400             @Override
401             public void setColor(int color) {
402                 mPaint.setColor(color);
403             }
404 
405             @Override
406             public void setStrokeCap(int cap) {
407                 mPaint.setStrokeCap(Paint.Cap.values()[cap]);
408             }
409 
410             @Override
411             public void setStyle(int style) {
412                 mPaint.setStyle(Paint.Style.values()[style]);
413             }
414 
415             @Override
416             public void setShader(int shaderId) {
417                 // TODO this stuff should check the shader creation
418                 if (shaderId == 0) {
419                     mPaint.setShader(null);
420                     return;
421                 }
422                 ShaderData data = getShaderData(shaderId);
423                 RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
424                 String[] names = data.getUniformFloatNames();
425                 for (int i = 0; i < names.length; i++) {
426                     String name = names[i];
427                     float[] val = data.getUniformFloats(name);
428                     shader.setFloatUniform(name, val);
429                 }
430                 names = data.getUniformIntegerNames();
431                 for (int i = 0; i < names.length; i++) {
432                     String name = names[i];
433                     int[] val = data.getUniformInts(name);
434                     shader.setIntUniform(name, val);
435                 }
436                 names = data.getUniformBitmapNames();
437                 for (int i = 0; i < names.length; i++) {
438                     String name = names[i];
439                     int val = data.getUniformBitmapId(name);
440                 }
441                 mPaint.setShader(shader);
442             }
443 
444             @Override
445             public void setImageFilterQuality(int quality) {
446                 Utils.log(" quality =" + quality);
447             }
448 
449             @Override
450             public void setBlendMode(int mode) {
451                 mPaint.setBlendMode(origamiToBlendMode(mode));
452             }
453 
454             @Override
455             public void setAlpha(float a) {
456                 mPaint.setAlpha((int) (255 * a));
457             }
458 
459             @Override
460             public void setStrokeMiter(float miter) {
461                 mPaint.setStrokeMiter(miter);
462             }
463 
464             @Override
465             public void setStrokeJoin(int join) {
466                 mPaint.setStrokeJoin(Paint.Join.values()[join]);
467             }
468 
469             @Override
470             public void setFilterBitmap(boolean filter) {
471                 mPaint.setFilterBitmap(filter);
472             }
473 
474             @Override
475             public void setAntiAlias(boolean aa) {
476                 mPaint.setAntiAlias(aa);
477             }
478 
479             @Override
480             public void clear(long mask) {
481                 if (true) return;
482                 long m = mask;
483                 int k = 1;
484                 while (m > 0) {
485                     if ((m & 1) == 1L) {
486                         switch (k) {
487 
488                             case PaintBundle.COLOR_FILTER:
489                                 mPaint.setColorFilter(null);
490                                 break;
491                         }
492                     }
493                     k++;
494                     m = m >> 1;
495                 }
496             }
497 
498             Shader.TileMode[] mTileModes = new Shader.TileMode[]{
499                     Shader.TileMode.CLAMP,
500                     Shader.TileMode.REPEAT,
501                     Shader.TileMode.MIRROR};
502 
503             @Override
504             public void setLinearGradient(int[] colors,
505                                           float[] stops,
506                                           float startX,
507                                           float startY,
508                                           float endX,
509                                           float endY,
510                                           int tileMode) {
511                 mPaint.setShader(new LinearGradient(startX,
512                         startY,
513                         endX,
514                         endY, colors, stops, mTileModes[tileMode]));
515 
516             }
517 
518             @Override
519             public void setRadialGradient(int[] colors,
520                                           float[] stops,
521                                           float centerX,
522                                           float centerY,
523                                           float radius,
524                                           int tileMode) {
525                 mPaint.setShader(new RadialGradient(centerX, centerY, radius,
526                         colors, stops, mTileModes[tileMode]));
527             }
528 
529             @Override
530             public void setSweepGradient(int[] colors,
531                                          float[] stops,
532                                          float centerX,
533                                          float centerY) {
534                 mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
535 
536             }
537 
538             @Override
539             public void setColorFilter(int color, int mode) {
540                 PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
541                 if (pmode != null) {
542                     mPaint.setColorFilter(
543                             new PorterDuffColorFilter(color, pmode));
544                 }
545             }
546         });
547     }
548 
549     @Override
matrixScale(float scaleX, float scaleY, float centerX, float centerY)550     public void matrixScale(float scaleX,
551                             float scaleY,
552                             float centerX,
553                             float centerY) {
554         if (Float.isNaN(centerX)) {
555             mCanvas.scale(scaleX, scaleY);
556         } else {
557             mCanvas.scale(scaleX, scaleY, centerX, centerY);
558         }
559     }
560 
561     @Override
matrixTranslate(float translateX, float translateY)562     public void matrixTranslate(float translateX, float translateY) {
563         mCanvas.translate(translateX, translateY);
564     }
565 
566     @Override
matrixSkew(float skewX, float skewY)567     public void matrixSkew(float skewX, float skewY) {
568         mCanvas.skew(skewX, skewY);
569     }
570 
571     @Override
matrixRotate(float rotate, float pivotX, float pivotY)572     public void matrixRotate(float rotate, float pivotX, float pivotY) {
573         if (Float.isNaN(pivotX)) {
574             mCanvas.rotate(rotate);
575         } else {
576             mCanvas.rotate(rotate, pivotX, pivotY);
577 
578         }
579     }
580 
581     @Override
matrixSave()582     public void matrixSave() {
583         mCanvas.save();
584     }
585 
586     @Override
matrixRestore()587     public void matrixRestore() {
588         mCanvas.restore();
589     }
590 
591     @Override
clipRect(float left, float top, float right, float bottom)592     public void clipRect(float left, float top, float right, float bottom) {
593         mCanvas.clipRect(left, top, right, bottom);
594     }
595 
596     @Override
clipPath(int pathId, int regionOp)597     public void clipPath(int pathId, int regionOp) {
598         Path path = getPath(pathId, 0, 1);
599         if (regionOp == ClipPath.DIFFERENCE) {
600             mCanvas.clipOutPath(path); // DIFFERENCE
601         } else {
602             mCanvas.clipPath(path);  // INTERSECT
603         }
604     }
605 
606     @Override
reset()607     public void reset() {
608         mPaint.reset();
609     }
610 
getPath(int path1Id, int path2Id, float tween, float start, float end)611     private Path getPath(int path1Id,
612                          int path2Id,
613                          float tween,
614                          float start,
615                          float end) {
616         if (tween == 0.0f) {
617             return getPath(path1Id, start, end);
618         }
619         if (tween == 1.0f) {
620             return getPath(path2Id, start, end);
621         }
622         AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
623         float[] data1 =
624                 (float[]) androidContext.mRemoteComposeState.getFromId(path1Id);
625         float[] data2 =
626                 (float[]) androidContext.mRemoteComposeState.getFromId(path2Id);
627         float[] tmp = new float[data2.length];
628         for (int i = 0; i < tmp.length; i++) {
629             if (Float.isNaN(data1[i]) || Float.isNaN(data2[i])) {
630                 tmp[i] = data1[i];
631             } else {
632                 tmp[i] = (data2[i] - data1[i]) * tween + data1[i];
633             }
634         }
635         Path path = new Path();
636         FloatsToPath.genPath(path, tmp, start, end);
637         return path;
638     }
639 
getPath(int id, float start, float end)640     private Path getPath(int id, float start, float end) {
641         AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
642         Path path = new Path();
643         if (androidContext.mRemoteComposeState.containsId(id)) {
644             float[] data =
645                     (float[]) androidContext.mRemoteComposeState.getFromId(id);
646             FloatsToPath.genPath(path, data, start, end);
647         }
648         return path;
649     }
650 
getText(int id)651     private String getText(int id) {
652         return (String) mContext.mRemoteComposeState.getFromId(id);
653     }
654 
getShaderData(int id)655     private ShaderData getShaderData(int id) {
656         return (ShaderData) mContext.mRemoteComposeState.getFromId(id);
657     }
658 }
659 
660