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