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