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