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