• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 
17 package android.graphics;
18 
19 import com.android.ide.common.rendering.api.LayoutLog;
20 import com.android.layoutlib.bridge.Bridge;
21 import com.android.layoutlib.bridge.impl.DelegateManager;
22 import com.android.layoutlib.bridge.impl.GcSnapshot;
23 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
24 
25 import android.graphics.Bitmap.Config;
26 import android.graphics.Paint_Delegate.FontInfo;
27 import android.text.TextUtils;
28 
29 import java.awt.Color;
30 import java.awt.Composite;
31 import java.awt.Graphics2D;
32 import java.awt.Rectangle;
33 import java.awt.RenderingHints;
34 import java.awt.Shape;
35 import java.awt.geom.AffineTransform;
36 import java.awt.geom.Arc2D;
37 import java.awt.image.BufferedImage;
38 import java.util.List;
39 
40 
41 /**
42  * Delegate implementing the native methods of android.graphics.Canvas
43  *
44  * Through the layoutlib_create tool, the original native methods of Canvas have been replaced
45  * by calls to methods of the same name in this delegate class.
46  *
47  * This class behaves like the original native implementation, but in Java, keeping previously
48  * native data into its own objects and mapping them to int that are sent back and forth between
49  * it and the original Canvas class.
50  *
51  * @see DelegateManager
52  *
53  */
54 public final class Canvas_Delegate {
55 
56     // ---- delegate manager ----
57     private static final DelegateManager<Canvas_Delegate> sManager =
58             new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
59 
60     // ---- delegate helper data ----
61 
62     private final static boolean[] sBoolOut = new boolean[1];
63 
64     // ---- delegate data ----
65     private Bitmap_Delegate mBitmap;
66     private GcSnapshot mSnapshot;
67 
68     private DrawFilter_Delegate mDrawFilter = null;
69 
70     // ---- Public Helper methods ----
71 
72     /**
73      * Returns the native delegate associated to a given {@link Canvas} object.
74      */
getDelegate(Canvas canvas)75     public static Canvas_Delegate getDelegate(Canvas canvas) {
76         return sManager.getDelegate(canvas.mNativeCanvas);
77     }
78 
79     /**
80      * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
81      */
getDelegate(int native_canvas)82     public static Canvas_Delegate getDelegate(int native_canvas) {
83         return sManager.getDelegate(native_canvas);
84     }
85 
86     /**
87      * Returns the current {@link Graphics2D} used to draw.
88      */
getSnapshot()89     public GcSnapshot getSnapshot() {
90         return mSnapshot;
91     }
92 
93     /**
94      * Returns the {@link DrawFilter} delegate or null if none have been set.
95      *
96      * @return the delegate or null.
97      */
getDrawFilter()98     public DrawFilter_Delegate getDrawFilter() {
99         return mDrawFilter;
100     }
101 
102     // ---- native methods ----
103 
104     @LayoutlibDelegate
isOpaque(Canvas thisCanvas)105     /*package*/ static boolean isOpaque(Canvas thisCanvas) {
106         // get the delegate from the native int.
107         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
108         if (canvasDelegate == null) {
109             return false;
110         }
111 
112         return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
113     }
114 
115     @LayoutlibDelegate
getWidth(Canvas thisCanvas)116     /*package*/ static int getWidth(Canvas thisCanvas) {
117         // get the delegate from the native int.
118         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
119         if (canvasDelegate == null) {
120             return 0;
121         }
122 
123         return canvasDelegate.mBitmap.getImage().getWidth();
124     }
125 
126     @LayoutlibDelegate
getHeight(Canvas thisCanvas)127     /*package*/ static int getHeight(Canvas thisCanvas) {
128         // get the delegate from the native int.
129         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
130         if (canvasDelegate == null) {
131             return 0;
132         }
133 
134         return canvasDelegate.mBitmap.getImage().getHeight();
135     }
136 
137     @LayoutlibDelegate
translate(Canvas thisCanvas, float dx, float dy)138    /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
139         // get the delegate from the native int.
140         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
141         if (canvasDelegate == null) {
142             return;
143         }
144 
145         canvasDelegate.getSnapshot().translate(dx, dy);
146     }
147 
148     @LayoutlibDelegate
rotate(Canvas thisCanvas, float degrees)149     /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
150         // get the delegate from the native int.
151         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
152         if (canvasDelegate == null) {
153             return;
154         }
155 
156         canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
157     }
158 
159     @LayoutlibDelegate
scale(Canvas thisCanvas, float sx, float sy)160    /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
161         // get the delegate from the native int.
162         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
163         if (canvasDelegate == null) {
164             return;
165         }
166 
167         canvasDelegate.getSnapshot().scale(sx, sy);
168     }
169 
170     @LayoutlibDelegate
skew(Canvas thisCanvas, float kx, float ky)171    /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
172         // get the delegate from the native int.
173         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
174         if (canvasDelegate == null) {
175             return;
176         }
177 
178         // get the current top graphics2D object.
179         GcSnapshot g = canvasDelegate.getSnapshot();
180 
181         // get its current matrix
182         AffineTransform currentTx = g.getTransform();
183         // get the AffineTransform for the given skew.
184         float[] mtx = Matrix_Delegate.getSkew(kx, ky);
185         AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
186 
187         // combine them so that the given matrix is applied after.
188         currentTx.preConcatenate(matrixTx);
189 
190         // give it to the graphics2D as a new matrix replacing all previous transform
191         g.setTransform(currentTx);
192     }
193 
194     @LayoutlibDelegate
clipRect(Canvas thisCanvas, RectF rect)195     /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
196         return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
197     }
198 
199     @LayoutlibDelegate
clipRect(Canvas thisCanvas, Rect rect)200     /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
201         return clipRect(thisCanvas, (float) rect.left, (float) rect.top,
202                 (float) rect.right, (float) rect.bottom);
203     }
204 
205     @LayoutlibDelegate
clipRect(Canvas thisCanvas, float left, float top, float right, float bottom)206     /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
207             float bottom) {
208         // get the delegate from the native int.
209         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
210         if (canvasDelegate == null) {
211             return false;
212         }
213 
214         return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
215     }
216 
217     @LayoutlibDelegate
clipRect(Canvas thisCanvas, int left, int top, int right, int bottom)218     /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
219             int bottom) {
220 
221         return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom);
222     }
223 
224     @LayoutlibDelegate
save(Canvas thisCanvas)225     /*package*/ static int save(Canvas thisCanvas) {
226         return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG);
227     }
228 
229     @LayoutlibDelegate
save(Canvas thisCanvas, int saveFlags)230     /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
231         // get the delegate from the native int.
232         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
233         if (canvasDelegate == null) {
234             return 0;
235         }
236 
237         return canvasDelegate.save(saveFlags);
238     }
239 
240     @LayoutlibDelegate
restore(Canvas thisCanvas)241     /*package*/ static void restore(Canvas thisCanvas) {
242         // get the delegate from the native int.
243         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
244         if (canvasDelegate == null) {
245             return;
246         }
247 
248         canvasDelegate.restore();
249     }
250 
251     @LayoutlibDelegate
getSaveCount(Canvas thisCanvas)252     /*package*/ static int getSaveCount(Canvas thisCanvas) {
253         // get the delegate from the native int.
254         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
255         if (canvasDelegate == null) {
256             return 0;
257         }
258 
259         return canvasDelegate.getSnapshot().size();
260     }
261 
262     @LayoutlibDelegate
restoreToCount(Canvas thisCanvas, int saveCount)263     /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
264         // get the delegate from the native int.
265         Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
266         if (canvasDelegate == null) {
267             return;
268         }
269 
270         canvasDelegate.restoreTo(saveCount);
271     }
272 
273     @LayoutlibDelegate
drawPoints(Canvas thisCanvas, float[] pts, int offset, int count, Paint paint)274     /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
275             Paint paint) {
276         // FIXME
277         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
278                 "Canvas.drawPoint is not supported.", null, null /*data*/);
279     }
280 
281     @LayoutlibDelegate
drawPoint(Canvas thisCanvas, float x, float y, Paint paint)282     /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) {
283         // FIXME
284         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
285                 "Canvas.drawPoint is not supported.", null, null /*data*/);
286     }
287 
288     @LayoutlibDelegate
drawLines(Canvas thisCanvas, final float[] pts, final int offset, final int count, Paint paint)289     /*package*/ static void drawLines(Canvas thisCanvas,
290             final float[] pts, final int offset, final int count,
291             Paint paint) {
292         draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/,
293                 false /*forceSrcMode*/, new GcSnapshot.Drawable() {
294                     @Override
295                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
296                         for (int i = 0 ; i < count ; i += 4) {
297                             graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
298                                     (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
299                         }
300                     }
301                 });
302     }
303 
304     @LayoutlibDelegate
freeCaches()305     /*package*/ static void freeCaches() {
306         // nothing to be done here.
307     }
308 
309     @LayoutlibDelegate
freeTextLayoutCaches()310     /*package*/ static void freeTextLayoutCaches() {
311         // nothing to be done here yet.
312     }
313 
314     @LayoutlibDelegate
initRaster(int nativeBitmapOrZero)315     /*package*/ static int initRaster(int nativeBitmapOrZero) {
316         if (nativeBitmapOrZero > 0) {
317             // get the Bitmap from the int
318             Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
319 
320             // create a new Canvas_Delegate with the given bitmap and return its new native int.
321             Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
322 
323             return sManager.addNewDelegate(newDelegate);
324         }
325 
326         // create a new Canvas_Delegate and return its new native int.
327         Canvas_Delegate newDelegate = new Canvas_Delegate();
328 
329         return sManager.addNewDelegate(newDelegate);
330     }
331 
332     @LayoutlibDelegate
copyNativeCanvasState(int srcCanvas, int dstCanvas)333     /*package*/ static void copyNativeCanvasState(int srcCanvas, int dstCanvas) {
334         // get the delegate from the native int.
335         Canvas_Delegate srcCanvasDelegate = sManager.getDelegate(srcCanvas);
336         if (srcCanvasDelegate == null) {
337             return;
338         }
339 
340         // get the delegate from the native int.
341         Canvas_Delegate dstCanvasDelegate = sManager.getDelegate(dstCanvas);
342         if (dstCanvasDelegate == null) {
343             return;
344         }
345         // TODO: actually copy the canvas state.
346     }
347 
348     @LayoutlibDelegate
native_saveLayer(int nativeCanvas, RectF bounds, int paint, int layerFlags)349     /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds,
350                                                int paint, int layerFlags) {
351         // get the delegate from the native int.
352         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
353         if (canvasDelegate == null) {
354             return 0;
355         }
356 
357         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
358         if (paintDelegate == null) {
359             return 0;
360         }
361 
362         return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags);
363     }
364 
365     @LayoutlibDelegate
native_saveLayer(int nativeCanvas, float l, float t, float r, float b, int paint, int layerFlags)366     /*package*/ static int native_saveLayer(int nativeCanvas, float l,
367                                                float t, float r, float b,
368                                                int paint, int layerFlags) {
369         // get the delegate from the native int.
370         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
371         if (canvasDelegate == null) {
372             return 0;
373         }
374 
375         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
376         if (paintDelegate == null) {
377             return 0;
378         }
379 
380         return canvasDelegate.saveLayer(new RectF(l, t, r, b),
381                 paintDelegate, layerFlags);
382     }
383 
384     @LayoutlibDelegate
native_saveLayerAlpha(int nativeCanvas, RectF bounds, int alpha, int layerFlags)385     /*package*/ static int native_saveLayerAlpha(int nativeCanvas,
386                                                     RectF bounds, int alpha,
387                                                     int layerFlags) {
388         // get the delegate from the native int.
389         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
390         if (canvasDelegate == null) {
391             return 0;
392         }
393 
394         return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags);
395     }
396 
397     @LayoutlibDelegate
native_saveLayerAlpha(int nativeCanvas, float l, float t, float r, float b, int alpha, int layerFlags)398     /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l,
399                                                     float t, float r, float b,
400                                                     int alpha, int layerFlags) {
401         // get the delegate from the native int.
402         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
403         if (canvasDelegate == null) {
404             return 0;
405         }
406 
407         return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
408     }
409 
410 
411     @LayoutlibDelegate
native_concat(int nCanvas, int nMatrix)412     /*package*/ static void native_concat(int nCanvas, int nMatrix) {
413         // get the delegate from the native int.
414         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
415         if (canvasDelegate == null) {
416             return;
417         }
418 
419         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
420         if (matrixDelegate == null) {
421             return;
422         }
423 
424         // get the current top graphics2D object.
425         GcSnapshot snapshot = canvasDelegate.getSnapshot();
426 
427         // get its current matrix
428         AffineTransform currentTx = snapshot.getTransform();
429         // get the AffineTransform of the given matrix
430         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
431 
432         // combine them so that the given matrix is applied after.
433         currentTx.concatenate(matrixTx);
434 
435         // give it to the graphics2D as a new matrix replacing all previous transform
436         snapshot.setTransform(currentTx);
437     }
438 
439     @LayoutlibDelegate
native_setMatrix(int nCanvas, int nMatrix)440     /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) {
441         // get the delegate from the native int.
442         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
443         if (canvasDelegate == null) {
444             return;
445         }
446 
447         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
448         if (matrixDelegate == null) {
449             return;
450         }
451 
452         // get the current top graphics2D object.
453         GcSnapshot snapshot = canvasDelegate.getSnapshot();
454 
455         // get the AffineTransform of the given matrix
456         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
457 
458         // give it to the graphics2D as a new matrix replacing all previous transform
459         snapshot.setTransform(matrixTx);
460 
461         if (matrixDelegate.hasPerspective()) {
462             assert false;
463             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE,
464                     "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
465                     "supports affine transformations.", null, null /*data*/);
466         }
467     }
468 
469     @LayoutlibDelegate
native_clipRect(int nCanvas, float left, float top, float right, float bottom, int regionOp)470     /*package*/ static boolean native_clipRect(int nCanvas,
471                                                   float left, float top,
472                                                   float right, float bottom,
473                                                   int regionOp) {
474 
475         // get the delegate from the native int.
476         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
477         if (canvasDelegate == null) {
478             return false;
479         }
480 
481         return canvasDelegate.clipRect(left, top, right, bottom, regionOp);
482     }
483 
484     @LayoutlibDelegate
native_clipPath(int nativeCanvas, int nativePath, int regionOp)485     /*package*/ static boolean native_clipPath(int nativeCanvas,
486                                                   int nativePath,
487                                                   int regionOp) {
488         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
489         if (canvasDelegate == null) {
490             return true;
491         }
492 
493         Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath);
494         if (pathDelegate == null) {
495             return true;
496         }
497 
498         return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp);
499     }
500 
501     @LayoutlibDelegate
native_clipRegion(int nativeCanvas, int nativeRegion, int regionOp)502     /*package*/ static boolean native_clipRegion(int nativeCanvas,
503                                                     int nativeRegion,
504                                                     int regionOp) {
505         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
506         if (canvasDelegate == null) {
507             return true;
508         }
509 
510         Region_Delegate region = Region_Delegate.getDelegate(nativeRegion);
511         if (region == null) {
512             return true;
513         }
514 
515         return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp);
516     }
517 
518     @LayoutlibDelegate
nativeSetDrawFilter(int nativeCanvas, int nativeFilter)519     /*package*/ static void nativeSetDrawFilter(int nativeCanvas, int nativeFilter) {
520         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
521         if (canvasDelegate == null) {
522             return;
523         }
524 
525         canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter);
526 
527         if (canvasDelegate.mDrawFilter != null &&
528                 canvasDelegate.mDrawFilter.isSupported() == false) {
529             Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER,
530                     canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/);
531         }
532     }
533 
534     @LayoutlibDelegate
native_getClipBounds(int nativeCanvas, Rect bounds)535     /*package*/ static boolean native_getClipBounds(int nativeCanvas,
536                                                        Rect bounds) {
537         // get the delegate from the native int.
538         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
539         if (canvasDelegate == null) {
540             return false;
541         }
542 
543         Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds();
544         if (rect != null && rect.isEmpty() == false) {
545             bounds.left = rect.x;
546             bounds.top = rect.y;
547             bounds.right = rect.x + rect.width;
548             bounds.bottom = rect.y + rect.height;
549             return true;
550         }
551 
552         return false;
553     }
554 
555     @LayoutlibDelegate
native_getCTM(int canvas, int matrix)556     /*package*/ static void native_getCTM(int canvas, int matrix) {
557         // get the delegate from the native int.
558         Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
559         if (canvasDelegate == null) {
560             return;
561         }
562 
563         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
564         if (matrixDelegate == null) {
565             return;
566         }
567 
568         AffineTransform transform = canvasDelegate.getSnapshot().getTransform();
569         matrixDelegate.set(Matrix_Delegate.makeValues(transform));
570     }
571 
572     @LayoutlibDelegate
native_quickReject(int nativeCanvas, RectF rect)573     /*package*/ static boolean native_quickReject(int nativeCanvas,
574                                                      RectF rect) {
575         // FIXME properly implement quickReject
576         return false;
577     }
578 
579     @LayoutlibDelegate
native_quickReject(int nativeCanvas, int path)580     /*package*/ static boolean native_quickReject(int nativeCanvas,
581                                                      int path) {
582         // FIXME properly implement quickReject
583         return false;
584     }
585 
586     @LayoutlibDelegate
native_quickReject(int nativeCanvas, float left, float top, float right, float bottom)587     /*package*/ static boolean native_quickReject(int nativeCanvas,
588                                                      float left, float top,
589                                                      float right, float bottom) {
590         // FIXME properly implement quickReject
591         return false;
592     }
593 
594     @LayoutlibDelegate
native_drawRGB(int nativeCanvas, int r, int g, int b)595     /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, int b) {
596         native_drawColor(nativeCanvas, 0xFF000000 | r << 16 | (g&0xFF) << 8 | (b&0xFF),
597                 PorterDuff.Mode.SRC_OVER.nativeInt);
598 
599     }
600 
601     @LayoutlibDelegate
native_drawARGB(int nativeCanvas, int a, int r, int g, int b)602     /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, int g, int b) {
603         native_drawColor(nativeCanvas, a << 24 | (r&0xFF) << 16 | (g&0xFF) << 8 | (b&0xFF),
604                 PorterDuff.Mode.SRC_OVER.nativeInt);
605     }
606 
607     @LayoutlibDelegate
native_drawColor(int nativeCanvas, int color)608     /*package*/ static void native_drawColor(int nativeCanvas, int color) {
609         native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt);
610     }
611 
612     @LayoutlibDelegate
native_drawColor(int nativeCanvas, final int color, final int mode)613     /*package*/ static void native_drawColor(int nativeCanvas, final int color, final int mode) {
614         // get the delegate from the native int.
615         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
616         if (canvasDelegate == null) {
617             return;
618         }
619 
620         final int w = canvasDelegate.mBitmap.getImage().getWidth();
621         final int h = canvasDelegate.mBitmap.getImage().getHeight();
622         draw(nativeCanvas, new GcSnapshot.Drawable() {
623 
624             @Override
625             public void draw(Graphics2D graphics, Paint_Delegate paint) {
626                 // reset its transform just in case
627                 graphics.setTransform(new AffineTransform());
628 
629                 // set the color
630                 graphics.setColor(new Color(color, true /*alpha*/));
631 
632                 Composite composite = PorterDuffXfermode_Delegate.getComposite(
633                         PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
634                 if (composite != null) {
635                     graphics.setComposite(composite);
636                 }
637 
638                 graphics.fillRect(0, 0, w, h);
639             }
640         });
641     }
642 
643     @LayoutlibDelegate
native_drawPaint(int nativeCanvas, int paint)644     /*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
645         // FIXME
646         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
647                 "Canvas.drawPaint is not supported.", null, null /*data*/);
648     }
649 
650     @LayoutlibDelegate
native_drawLine(int nativeCanvas, final float startX, final float startY, final float stopX, final float stopY, int paint)651     /*package*/ static void native_drawLine(int nativeCanvas,
652             final float startX, final float startY, final float stopX, final float stopY,
653             int paint) {
654 
655         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
656                 new GcSnapshot.Drawable() {
657                     @Override
658                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
659                         graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
660                     }
661         });
662     }
663 
664     @LayoutlibDelegate
native_drawRect(int nativeCanvas, RectF rect, int paint)665     /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
666                                                int paint) {
667         native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
668     }
669 
670     @LayoutlibDelegate
native_drawRect(int nativeCanvas, final float left, final float top, final float right, final float bottom, int paint)671     /*package*/ static void native_drawRect(int nativeCanvas,
672             final float left, final float top, final float right, final float bottom, int paint) {
673 
674         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
675                 new GcSnapshot.Drawable() {
676                     @Override
677                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
678                         int style = paintDelegate.getStyle();
679 
680                         // draw
681                         if (style == Paint.Style.FILL.nativeInt ||
682                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
683                             graphics.fillRect((int)left, (int)top,
684                                     (int)(right-left), (int)(bottom-top));
685                         }
686 
687                         if (style == Paint.Style.STROKE.nativeInt ||
688                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
689                             graphics.drawRect((int)left, (int)top,
690                                     (int)(right-left), (int)(bottom-top));
691                         }
692                     }
693         });
694     }
695 
696     @LayoutlibDelegate
native_drawOval(int nativeCanvas, final RectF oval, int paint)697     /*package*/ static void native_drawOval(int nativeCanvas, final RectF oval, int paint) {
698         if (oval.right > oval.left && oval.bottom > oval.top) {
699             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
700                     new GcSnapshot.Drawable() {
701                         @Override
702                         public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
703                             int style = paintDelegate.getStyle();
704 
705                             // draw
706                             if (style == Paint.Style.FILL.nativeInt ||
707                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
708                                 graphics.fillOval((int)oval.left, (int)oval.top,
709                                         (int)oval.width(), (int)oval.height());
710                             }
711 
712                             if (style == Paint.Style.STROKE.nativeInt ||
713                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
714                                 graphics.drawOval((int)oval.left, (int)oval.top,
715                                         (int)oval.width(), (int)oval.height());
716                             }
717                         }
718             });
719         }
720     }
721 
722     @LayoutlibDelegate
native_drawCircle(int nativeCanvas, float cx, float cy, float radius, int paint)723     /*package*/ static void native_drawCircle(int nativeCanvas,
724             float cx, float cy, float radius, int paint) {
725         native_drawOval(nativeCanvas,
726                 new RectF(cx - radius, cy - radius, cx + radius, cy + radius),
727                 paint);
728     }
729 
730     @LayoutlibDelegate
native_drawArc(int nativeCanvas, final RectF oval, final float startAngle, final float sweep, final boolean useCenter, int paint)731     /*package*/ static void native_drawArc(int nativeCanvas,
732             final RectF oval, final float startAngle, final float sweep,
733             final boolean useCenter, int paint) {
734         if (oval.right > oval.left && oval.bottom > oval.top) {
735             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
736                     new GcSnapshot.Drawable() {
737                         @Override
738                         public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
739                             int style = paintDelegate.getStyle();
740 
741                             Arc2D.Float arc = new Arc2D.Float(
742                                     oval.left, oval.top, oval.width(), oval.height(),
743                                     -startAngle, -sweep,
744                                     useCenter ? Arc2D.PIE : Arc2D.OPEN);
745 
746                             // draw
747                             if (style == Paint.Style.FILL.nativeInt ||
748                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
749                                 graphics.fill(arc);
750                             }
751 
752                             if (style == Paint.Style.STROKE.nativeInt ||
753                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
754                                 graphics.draw(arc);
755                             }
756                         }
757             });
758         }
759     }
760 
761     @LayoutlibDelegate
native_drawRoundRect(int nativeCanvas, final RectF rect, final float rx, final float ry, int paint)762     /*package*/ static void native_drawRoundRect(int nativeCanvas,
763             final RectF rect, final float rx, final float ry, int paint) {
764 
765         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
766                 new GcSnapshot.Drawable() {
767                     @Override
768                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
769                         int style = paintDelegate.getStyle();
770 
771                         // draw
772                         if (style == Paint.Style.FILL.nativeInt ||
773                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
774                             graphics.fillRoundRect(
775                                     (int)rect.left, (int)rect.top,
776                                     (int)rect.width(), (int)rect.height(),
777                                     (int)rx, (int)ry);
778                         }
779 
780                         if (style == Paint.Style.STROKE.nativeInt ||
781                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
782                             graphics.drawRoundRect(
783                                     (int)rect.left, (int)rect.top,
784                                     (int)rect.width(), (int)rect.height(),
785                                     (int)rx, (int)ry);
786                         }
787                     }
788         });
789     }
790 
791     @LayoutlibDelegate
native_drawPath(int nativeCanvas, int path, int paint)792     /*package*/ static void native_drawPath(int nativeCanvas, int path, int paint) {
793         final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
794         if (pathDelegate == null) {
795             return;
796         }
797 
798         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
799                 new GcSnapshot.Drawable() {
800                     @Override
801                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
802                         Shape shape = pathDelegate.getJavaShape();
803                         int style = paintDelegate.getStyle();
804 
805                         if (style == Paint.Style.FILL.nativeInt ||
806                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
807                             graphics.fill(shape);
808                         }
809 
810                         if (style == Paint.Style.STROKE.nativeInt ||
811                                 style == Paint.Style.FILL_AND_STROKE.nativeInt) {
812                             graphics.draw(shape);
813                         }
814                     }
815         });
816     }
817 
818     @LayoutlibDelegate
native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, float left, float top, int nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity)819     /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
820                                                  float left, float top,
821                                                  int nativePaintOrZero,
822                                                  int canvasDensity,
823                                                  int screenDensity,
824                                                  int bitmapDensity) {
825         // get the delegate from the native int.
826         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
827         if (bitmapDelegate == null) {
828             return;
829         }
830 
831         BufferedImage image = bitmapDelegate.getImage();
832         float right = left + image.getWidth();
833         float bottom = top + image.getHeight();
834 
835         drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
836                 0, 0, image.getWidth(), image.getHeight(),
837                 (int)left, (int)top, (int)right, (int)bottom);
838     }
839 
840     @LayoutlibDelegate
native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, Rect src, RectF dst, int nativePaintOrZero, int screenDensity, int bitmapDensity)841     /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
842                                                  Rect src, RectF dst,
843                                                  int nativePaintOrZero,
844                                                  int screenDensity,
845                                                  int bitmapDensity) {
846         // get the delegate from the native int.
847         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
848         if (bitmapDelegate == null) {
849             return;
850         }
851 
852         BufferedImage image = bitmapDelegate.getImage();
853 
854         if (src == null) {
855             drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
856                     0, 0, image.getWidth(), image.getHeight(),
857                     (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
858         } else {
859             drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
860                     src.left, src.top, src.width(), src.height(),
861                     (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
862         }
863     }
864 
865     @LayoutlibDelegate
native_drawBitmap(int nativeCanvas, int bitmap, Rect src, Rect dst, int nativePaintOrZero, int screenDensity, int bitmapDensity)866     /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
867                                                  Rect src, Rect dst,
868                                                  int nativePaintOrZero,
869                                                  int screenDensity,
870                                                  int bitmapDensity) {
871         // get the delegate from the native int.
872         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
873         if (bitmapDelegate == null) {
874             return;
875         }
876 
877         BufferedImage image = bitmapDelegate.getImage();
878 
879         if (src == null) {
880             drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
881                     0, 0, image.getWidth(), image.getHeight(),
882                     dst.left, dst.top, dst.right, dst.bottom);
883         } else {
884             drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
885                     src.left, src.top, src.width(), src.height(),
886                     dst.left, dst.top, dst.right, dst.bottom);
887         }
888     }
889 
890     @LayoutlibDelegate
native_drawBitmap(int nativeCanvas, int[] colors, int offset, int stride, final float x, final float y, int width, int height, boolean hasAlpha, int nativePaintOrZero)891     /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
892                                                 int offset, int stride, final float x,
893                                                  final float y, int width, int height,
894                                                  boolean hasAlpha,
895                                                  int nativePaintOrZero) {
896 
897         // create a temp BufferedImage containing the content.
898         final BufferedImage image = new BufferedImage(width, height,
899                 hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
900         image.setRGB(0, 0, width, height, colors, offset, stride);
901 
902         draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
903                 new GcSnapshot.Drawable() {
904                     @Override
905                     public void draw(Graphics2D graphics, Paint_Delegate paint) {
906                         if (paint != null && paint.isFilterBitmap()) {
907                             graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
908                                     RenderingHints.VALUE_INTERPOLATION_BILINEAR);
909                         }
910 
911                         graphics.drawImage(image, (int) x, (int) y, null);
912                     }
913         });
914     }
915 
916     @LayoutlibDelegate
nativeDrawBitmapMatrix(int nCanvas, int nBitmap, int nMatrix, int nPaint)917     /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
918                                                       int nMatrix, int nPaint) {
919         // get the delegate from the native int.
920         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
921         if (canvasDelegate == null) {
922             return;
923         }
924 
925         // get the delegate from the native int, which can be null
926         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
927 
928         // get the delegate from the native int.
929         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nBitmap);
930         if (bitmapDelegate == null) {
931             return;
932         }
933 
934         final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
935 
936         Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
937         if (matrixDelegate == null) {
938             return;
939         }
940 
941         final AffineTransform mtx = matrixDelegate.getAffineTransform();
942 
943         canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
944                 @Override
945                 public void draw(Graphics2D graphics, Paint_Delegate paint) {
946                     if (paint != null && paint.isFilterBitmap()) {
947                         graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
948                                 RenderingHints.VALUE_INTERPOLATION_BILINEAR);
949                     }
950 
951                     //FIXME add support for canvas, screen and bitmap densities.
952                     graphics.drawImage(image, mtx, null);
953                 }
954         }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
955     }
956 
957     @LayoutlibDelegate
nativeDrawBitmapMesh(int nCanvas, int nBitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, int nPaint)958     /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
959             int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
960             int colorOffset, int nPaint) {
961         // FIXME
962         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
963                 "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
964     }
965 
966     @LayoutlibDelegate
nativeDrawVertices(int nCanvas, int mode, int n, float[] verts, int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices, int indexOffset, int indexCount, int nPaint)967     /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n,
968             float[] verts, int vertOffset,
969             float[] texs, int texOffset,
970             int[] colors, int colorOffset,
971             short[] indices, int indexOffset,
972             int indexCount, int nPaint) {
973         // FIXME
974         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
975                 "Canvas.drawVertices is not supported.", null, null /*data*/);
976     }
977 
978     @LayoutlibDelegate
native_drawText(int nativeCanvas, final char[] text, final int index, final int count, final float startX, final float startY, int flags, int paint)979     /*package*/ static void native_drawText(int nativeCanvas,
980             final char[] text, final int index, final int count,
981             final float startX, final float startY, int flags, int paint) {
982         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
983                 new GcSnapshot.Drawable() {
984             @Override
985             public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
986                 // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
987                 // Any change to this method should be reflected in Paint.measureText
988                 // Paint.TextAlign indicates how the text is positioned relative to X.
989                 // LEFT is the default and there's nothing to do.
990                 float x = startX;
991                 float y = startY;
992                 if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
993                     // TODO: check the value of bidiFlags.
994                     float m = paintDelegate.measureText(text, index, count, 0);
995                     if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
996                         x -= m / 2;
997                     } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
998                         x -= m;
999                     }
1000                 }
1001 
1002                 List<FontInfo> fonts = paintDelegate.getFonts();
1003 
1004                 if (fonts.size() > 0) {
1005                     FontInfo mainFont = fonts.get(0);
1006                     int i = index;
1007                     int lastIndex = index + count;
1008                     while (i < lastIndex) {
1009                         // always start with the main font.
1010                         int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
1011                         if (upTo == -1) {
1012                             // draw all the rest and exit.
1013                             graphics.setFont(mainFont.mFont);
1014                             graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y);
1015                             return;
1016                         } else if (upTo > 0) {
1017                             // draw what's possible
1018                             graphics.setFont(mainFont.mFont);
1019                             graphics.drawChars(text, i, upTo - i, (int)x, (int)y);
1020 
1021                             // compute the width that was drawn to increase x
1022                             x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
1023 
1024                             // move index to the first non displayed char.
1025                             i = upTo;
1026 
1027                             // don't call continue at this point. Since it is certain the main font
1028                             // cannot display the font a index upTo (now ==i), we move on to the
1029                             // fallback fonts directly.
1030                         }
1031 
1032                         // no char supported, attempt to read the next char(s) with the
1033                         // fallback font. In this case we only test the first character
1034                         // and then go back to test with the main font.
1035                         // Special test for 2-char characters.
1036                         boolean foundFont = false;
1037                         for (int f = 1 ; f < fonts.size() ; f++) {
1038                             FontInfo fontInfo = fonts.get(f);
1039 
1040                             // need to check that the font can display the character. We test
1041                             // differently if the char is a high surrogate.
1042                             int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
1043                             upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
1044                             if (upTo == -1) {
1045                                 // draw that char
1046                                 graphics.setFont(fontInfo.mFont);
1047                                 graphics.drawChars(text, i, charCount, (int)x, (int)y);
1048 
1049                                 // update x
1050                                 x += fontInfo.mMetrics.charsWidth(text, i, charCount);
1051 
1052                                 // update the index in the text, and move on
1053                                 i += charCount;
1054                                 foundFont = true;
1055                                 break;
1056 
1057                             }
1058                         }
1059 
1060                         // in case no font can display the char, display it with the main font.
1061                         // (it'll put a square probably)
1062                         if (foundFont == false) {
1063                             int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
1064 
1065                             graphics.setFont(mainFont.mFont);
1066                             graphics.drawChars(text, i, charCount, (int)x, (int)y);
1067 
1068                             // measure it to advance x
1069                             x += mainFont.mMetrics.charsWidth(text, i, charCount);
1070 
1071                             // and move to the next chars.
1072                             i += charCount;
1073                         }
1074                     }
1075                 }
1076             }
1077         });
1078     }
1079 
1080     @LayoutlibDelegate
native_drawText(int nativeCanvas, String text, int start, int end, float x, float y, int flags, int paint)1081     /*package*/ static void native_drawText(int nativeCanvas, String text,
1082             int start, int end, float x, float y, int flags, int paint) {
1083         int count = end - start;
1084         char[] buffer = TemporaryBuffer.obtain(count);
1085         TextUtils.getChars(text, start, end, buffer, 0);
1086 
1087         native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
1088     }
1089 
1090     @LayoutlibDelegate
native_drawTextRun(int nativeCanvas, String text, int start, int end, int contextStart, int contextEnd, float x, float y, int flags, int paint)1091     /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
1092             int start, int end, int contextStart, int contextEnd,
1093             float x, float y, int flags, int paint) {
1094         int count = end - start;
1095         char[] buffer = TemporaryBuffer.obtain(count);
1096         TextUtils.getChars(text, start, end, buffer, 0);
1097 
1098         native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
1099     }
1100 
1101     @LayoutlibDelegate
native_drawTextRun(int nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, float x, float y, int flags, int paint)1102     /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
1103             int start, int count, int contextStart, int contextCount,
1104             float x, float y, int flags, int paint) {
1105         native_drawText(nativeCanvas, text, start, count, x, y, flags, paint);
1106     }
1107 
1108     @LayoutlibDelegate
native_drawPosText(int nativeCanvas, char[] text, int index, int count, float[] pos, int paint)1109     /*package*/ static void native_drawPosText(int nativeCanvas,
1110                                                   char[] text, int index,
1111                                                   int count, float[] pos,
1112                                                   int paint) {
1113         // FIXME
1114         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1115                 "Canvas.drawPosText is not supported.", null, null /*data*/);
1116     }
1117 
1118     @LayoutlibDelegate
native_drawPosText(int nativeCanvas, String text, float[] pos, int paint)1119     /*package*/ static void native_drawPosText(int nativeCanvas,
1120                                                   String text, float[] pos,
1121                                                   int paint) {
1122         // FIXME
1123         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1124                 "Canvas.drawPosText is not supported.", null, null /*data*/);
1125     }
1126 
1127     @LayoutlibDelegate
native_drawTextOnPath(int nativeCanvas, char[] text, int index, int count, int path, float hOffset, float vOffset, int bidiFlags, int paint)1128     /*package*/ static void native_drawTextOnPath(int nativeCanvas,
1129                                                      char[] text, int index,
1130                                                      int count, int path,
1131                                                      float hOffset,
1132                                                      float vOffset, int bidiFlags,
1133                                                      int paint) {
1134         // FIXME
1135         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1136                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
1137     }
1138 
1139     @LayoutlibDelegate
native_drawTextOnPath(int nativeCanvas, String text, int path, float hOffset, float vOffset, int flags, int paint)1140     /*package*/ static void native_drawTextOnPath(int nativeCanvas,
1141                                                      String text, int path,
1142                                                      float hOffset,
1143                                                      float vOffset,
1144                                                      int flags, int paint) {
1145         // FIXME
1146         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1147                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
1148     }
1149 
1150     @LayoutlibDelegate
native_drawPicture(int nativeCanvas, int nativePicture)1151     /*package*/ static void native_drawPicture(int nativeCanvas,
1152                                                   int nativePicture) {
1153         // FIXME
1154         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
1155                 "Canvas.drawPicture is not supported.", null, null /*data*/);
1156     }
1157 
1158     @LayoutlibDelegate
finalizer(int nativeCanvas)1159     /*package*/ static void finalizer(int nativeCanvas) {
1160         // get the delegate from the native int so that it can be disposed.
1161         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
1162         if (canvasDelegate == null) {
1163             return;
1164         }
1165 
1166         canvasDelegate.dispose();
1167 
1168         // remove it from the manager.
1169         sManager.removeJavaReferenceFor(nativeCanvas);
1170     }
1171 
1172     // ---- Private delegate/helper methods ----
1173 
1174     /**
1175      * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
1176      * <p>Note that the drawable may actually be executed several times if there are
1177      * layers involved (see {@link #saveLayer(RectF, int, int)}.
1178      */
draw(int nCanvas, int nPaint, boolean compositeOnly, boolean forceSrcMode, GcSnapshot.Drawable drawable)1179     private static void draw(int nCanvas, int nPaint, boolean compositeOnly, boolean forceSrcMode,
1180             GcSnapshot.Drawable drawable) {
1181         // get the delegate from the native int.
1182         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
1183         if (canvasDelegate == null) {
1184             return;
1185         }
1186 
1187         // get the paint which can be null if nPaint is 0;
1188         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
1189 
1190         canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
1191     }
1192 
1193     /**
1194      * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
1195      * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
1196      * <p>Note that the drawable may actually be executed several times if there are
1197      * layers involved (see {@link #saveLayer(RectF, int, int)}.
1198      */
draw(int nCanvas, GcSnapshot.Drawable drawable)1199     private static void draw(int nCanvas, GcSnapshot.Drawable drawable) {
1200         // get the delegate from the native int.
1201         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
1202         if (canvasDelegate == null) {
1203             return;
1204         }
1205 
1206         canvasDelegate.mSnapshot.draw(drawable);
1207     }
1208 
Canvas_Delegate(Bitmap_Delegate bitmap)1209     private Canvas_Delegate(Bitmap_Delegate bitmap) {
1210         mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
1211     }
1212 
Canvas_Delegate()1213     private Canvas_Delegate() {
1214         mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
1215     }
1216 
1217     /**
1218      * Disposes of the {@link Graphics2D} stack.
1219      */
dispose()1220     private void dispose() {
1221         mSnapshot.dispose();
1222     }
1223 
save(int saveFlags)1224     private int save(int saveFlags) {
1225         // get the current save count
1226         int count = mSnapshot.size();
1227 
1228         mSnapshot = mSnapshot.save(saveFlags);
1229 
1230         // return the old save count
1231         return count;
1232     }
1233 
saveLayerAlpha(RectF rect, int alpha, int saveFlags)1234     private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
1235         Paint_Delegate paint = new Paint_Delegate();
1236         paint.setAlpha(alpha);
1237         return saveLayer(rect, paint, saveFlags);
1238     }
1239 
saveLayer(RectF rect, Paint_Delegate paint, int saveFlags)1240     private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
1241         // get the current save count
1242         int count = mSnapshot.size();
1243 
1244         mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
1245 
1246         // return the old save count
1247         return count;
1248     }
1249 
1250     /**
1251      * Restores the {@link GcSnapshot} to <var>saveCount</var>
1252      * @param saveCount the saveCount
1253      */
restoreTo(int saveCount)1254     private void restoreTo(int saveCount) {
1255         mSnapshot = mSnapshot.restoreTo(saveCount);
1256     }
1257 
1258     /**
1259      * Restores the {@link GcSnapshot} to <var>saveCount</var>
1260      * @param saveCount the saveCount
1261      */
restore()1262     private void restore() {
1263         mSnapshot = mSnapshot.restore();
1264     }
1265 
clipRect(float left, float top, float right, float bottom, int regionOp)1266     private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
1267         return mSnapshot.clipRect(left, top, right, bottom, regionOp);
1268     }
1269 
setBitmap(Bitmap_Delegate bitmap)1270     private void setBitmap(Bitmap_Delegate bitmap) {
1271         mBitmap = bitmap;
1272         assert mSnapshot.size() == 1;
1273         mSnapshot.setBitmap(mBitmap);
1274     }
1275 
drawBitmap( int nativeCanvas, Bitmap_Delegate bitmap, int nativePaintOrZero, final int sleft, final int stop, final int sright, final int sbottom, final int dleft, final int dtop, final int dright, final int dbottom)1276     private static void drawBitmap(
1277             int nativeCanvas,
1278             Bitmap_Delegate bitmap,
1279             int nativePaintOrZero,
1280             final int sleft, final int stop, final int sright, final int sbottom,
1281             final int dleft, final int dtop, final int dright, final int dbottom) {
1282         // get the delegate from the native int.
1283         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
1284         if (canvasDelegate == null) {
1285             return;
1286         }
1287 
1288         // get the paint, which could be null if the int is 0
1289         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
1290 
1291         final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
1292 
1293         draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
1294                 new GcSnapshot.Drawable() {
1295                     @Override
1296                     public void draw(Graphics2D graphics, Paint_Delegate paint) {
1297                         if (paint != null && paint.isFilterBitmap()) {
1298                             graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
1299                                     RenderingHints.VALUE_INTERPOLATION_BILINEAR);
1300                         }
1301 
1302                         //FIXME add support for canvas, screen and bitmap densities.
1303                         graphics.drawImage(image, dleft, dtop, dright, dbottom,
1304                                 sleft, stop, sright, sbottom, null);
1305                     }
1306         });
1307     }
1308 
1309 
1310     /**
1311      * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
1312      * The image returns, through a 1-size boolean array, whether the drawing code should
1313      * use a SRC composite no matter what the paint says.
1314      *
1315      * @param bitmap the bitmap
1316      * @param paint the paint that will be used to draw
1317      * @param forceSrcMode whether the composite will have to be SRC
1318      * @return the image to draw
1319      */
getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint, boolean[] forceSrcMode)1320     private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
1321             boolean[] forceSrcMode) {
1322         BufferedImage image = bitmap.getImage();
1323         forceSrcMode[0] = false;
1324 
1325         // if the bitmap config is alpha_8, then we erase all color value from it
1326         // before drawing it.
1327         if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
1328             fixAlpha8Bitmap(image);
1329         } else if (bitmap.hasAlpha() == false) {
1330             // hasAlpha is merely a rendering hint. There can in fact be alpha values
1331             // in the bitmap but it should be ignored at drawing time.
1332             // There is two ways to do this:
1333             // - override the composite to be SRC. This can only be used if the composite
1334             //   was going to be SRC or SRC_OVER in the first place
1335             // - Create a different bitmap to draw in which all the alpha channel values is set
1336             //   to 0xFF.
1337             if (paint != null) {
1338                 Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
1339                 if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
1340                     PorterDuff.Mode mode =
1341                         ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
1342 
1343                     forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER ||
1344                             mode == PorterDuff.Mode.SRC;
1345                 }
1346             }
1347 
1348             // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
1349             if (forceSrcMode[0] == false) {
1350                 image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
1351             }
1352         }
1353 
1354         return image;
1355     }
1356 
fixAlpha8Bitmap(final BufferedImage image)1357     private static void fixAlpha8Bitmap(final BufferedImage image) {
1358         int w = image.getWidth();
1359         int h = image.getHeight();
1360         int[] argb = new int[w * h];
1361         image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
1362 
1363         final int length = argb.length;
1364         for (int i = 0 ; i < length; i++) {
1365             argb[i] &= 0xFF000000;
1366         }
1367         image.setRGB(0, 0, w, h, argb, 0, w);
1368     }
1369 }
1370 
1371