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