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