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