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