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