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