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