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