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