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