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