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