Canvas_Delegate.java revision c810bea18e3f0e42aed4afb5e5159fba678a95e1
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(mode);
532            if (composite != null) {
533                graphics.setComposite(composite);
534            }
535
536            graphics.fillRect(0, 0, canvasDelegate.mBufferedImage.getWidth(),
537                    canvasDelegate.mBufferedImage.getHeight());
538        } finally {
539            // dispose Graphics2D object
540            graphics.dispose();
541        }
542    }
543
544    /*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
545        // FIXME
546        throw new UnsupportedOperationException();
547    }
548
549    /*package*/ static void native_drawLine(int nativeCanvas, float startX,
550                                               float startY, float stopX,
551                                               float stopY, int paint) {
552        // get the delegate from the native int.
553        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
554        if (canvasDelegate == null) {
555            assert false;
556            return;
557        }
558
559        // get the delegate from the native int.
560        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
561        if (paintDelegate == null) {
562            assert false;
563            return;
564        }
565
566        // get a Graphics2D object configured with the drawing parameters.
567        Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
568
569        try {
570            g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
571        } finally {
572            // dispose Graphics2D object
573            g.dispose();
574        }
575    }
576
577    /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
578                                               int paint) {
579        native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
580    }
581
582    /*package*/ static void native_drawRect(int nativeCanvas, float left,
583                                               float top, float right,
584                                               float bottom, int paint) {
585        // get the delegate from the native int.
586        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
587        if (canvasDelegate == null) {
588            assert false;
589            return;
590        }
591
592        // get the delegate from the native int.
593        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
594        if (paintDelegate == null) {
595            assert false;
596            return;
597        }
598
599        if (right > left && bottom > top) {
600            // get a Graphics2D object configured with the drawing parameters.
601            Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
602
603            try {
604                int style = paintDelegate.getStyle();
605
606                // draw
607                if (style == Paint.Style.FILL.nativeInt ||
608                        style == Paint.Style.FILL_AND_STROKE.nativeInt) {
609                    g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
610                }
611
612                if (style == Paint.Style.STROKE.nativeInt ||
613                        style == Paint.Style.FILL_AND_STROKE.nativeInt) {
614                    g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
615                }
616            } finally {
617                // dispose Graphics2D object
618                g.dispose();
619            }
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.createCustomGraphics(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.createCustomGraphics(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        final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
722        if (pathDelegate == null) {
723            assert false;
724            return;
725        }
726
727        draw(nativeCanvas, paint, new Drawable() {
728            public void draw(Graphics2D graphics, Paint_Delegate paint) {
729                Shape shape = pathDelegate.getJavaShape();
730                int style = paint.getStyle();
731
732                if (style == Paint.Style.FILL.nativeInt ||
733                        style == Paint.Style.FILL_AND_STROKE.nativeInt) {
734                    graphics.fill(shape);
735                }
736
737                if (style == Paint.Style.STROKE.nativeInt ||
738                        style == Paint.Style.FILL_AND_STROKE.nativeInt) {
739                    graphics.draw(shape);
740                }
741            }
742        });
743    }
744
745    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
746                                                 float left, float top,
747                                                 int nativePaintOrZero,
748                                                 int canvasDensity,
749                                                 int screenDensity,
750                                                 int bitmapDensity) {
751        // get the delegate from the native int.
752        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
753        if (bitmapDelegate == null) {
754            assert false;
755            return;
756        }
757
758        BufferedImage image = bitmapDelegate.getImage();
759        float right = left + image.getWidth();
760        float bottom = top + image.getHeight();
761
762        drawBitmap(nativeCanvas, image, nativePaintOrZero,
763                0, 0, image.getWidth(), image.getHeight(),
764                (int)left, (int)top, (int)right, (int)bottom);
765    }
766
767    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
768                                                 Rect src, RectF dst,
769                                                 int nativePaintOrZero,
770                                                 int screenDensity,
771                                                 int bitmapDensity) {
772        // get the delegate from the native int.
773        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
774        if (bitmapDelegate == null) {
775            assert false;
776            return;
777        }
778
779        BufferedImage image = bitmapDelegate.getImage();
780
781        if (src == null) {
782            drawBitmap(nativeCanvas, image, nativePaintOrZero,
783                    0, 0, image.getWidth(), image.getHeight(),
784                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
785        } else {
786            drawBitmap(nativeCanvas, image, nativePaintOrZero,
787                    src.left, src.top, src.width(), src.height(),
788                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
789        }
790    }
791
792    /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
793                                                 Rect src, Rect dst,
794                                                 int nativePaintOrZero,
795                                                 int screenDensity,
796                                                 int bitmapDensity) {
797        // get the delegate from the native int.
798        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
799        if (bitmapDelegate == null) {
800            assert false;
801            return;
802        }
803
804        BufferedImage image = bitmapDelegate.getImage();
805
806        if (src == null) {
807            drawBitmap(nativeCanvas, image, nativePaintOrZero,
808                    0, 0, image.getWidth(), image.getHeight(),
809                    dst.left, dst.top, dst.right, dst.bottom);
810        } else {
811            drawBitmap(nativeCanvas, image, nativePaintOrZero,
812                    src.left, src.top, src.width(), src.height(),
813                    dst.left, dst.top, dst.right, dst.bottom);
814        }
815    }
816
817    /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
818                                                int offset, int stride, float x,
819                                                 float y, int width, int height,
820                                                 boolean hasAlpha,
821                                                 int nativePaintOrZero) {
822        // FIXME
823        throw new UnsupportedOperationException();
824    }
825
826    /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
827                                                      int nMatrix, int nPaint) {
828        // FIXME
829        throw new UnsupportedOperationException();
830    }
831
832    /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
833                                                    int meshWidth, int meshHeight,
834                                                    float[] verts, int vertOffset,
835                                                    int[] colors, int colorOffset, int nPaint) {
836        // FIXME
837        throw new UnsupportedOperationException();
838    }
839
840    /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n,
841                   float[] verts, int vertOffset, float[] texs, int texOffset,
842                   int[] colors, int colorOffset, short[] indices,
843                   int indexOffset, int indexCount, int nPaint) {
844        // FIXME
845        throw new UnsupportedOperationException();
846    }
847
848    /*package*/ static void native_drawText(int nativeCanvas, char[] text,
849                                               int index, int count, float x,
850                                               float y, int flags, int paint) {
851        // WARNING: the logic in this method is similar to Paint.measureText.
852        // Any change to this method should be reflected in Paint.measureText
853
854        // get the delegate from the native int.
855        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
856        if (canvasDelegate == null) {
857            assert false;
858            return;
859        }
860
861        // get the delegate from the native int.
862        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
863        if (paintDelegate == null) {
864            assert false;
865            return;
866        }
867
868        Graphics2D g = (Graphics2D) canvasDelegate.createCustomGraphics(paintDelegate);
869        try {
870            // Paint.TextAlign indicates how the text is positioned relative to X.
871            // LEFT is the default and there's nothing to do.
872            if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
873                float m = paintDelegate.measureText(text, index, count);
874                if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
875                    x -= m / 2;
876                } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
877                    x -= m;
878                }
879            }
880
881            List<FontInfo> fonts = paintDelegate.getFonts();
882
883            if (fonts.size() > 0) {
884                FontInfo mainFont = fonts.get(0);
885                int i = index;
886                int lastIndex = index + count;
887                while (i < lastIndex) {
888                    // always start with the main font.
889                    int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
890                    if (upTo == -1) {
891                        // draw all the rest and exit.
892                        g.setFont(mainFont.mFont);
893                        g.drawChars(text, i, lastIndex - i, (int)x, (int)y);
894                        return;
895                    } else if (upTo > 0) {
896                        // draw what's possible
897                        g.setFont(mainFont.mFont);
898                        g.drawChars(text, i, upTo - i, (int)x, (int)y);
899
900                        // compute the width that was drawn to increase x
901                        x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
902
903                        // move index to the first non displayed char.
904                        i = upTo;
905
906                        // don't call continue at this point. Since it is certain the main font
907                        // cannot display the font a index upTo (now ==i), we move on to the
908                        // fallback fonts directly.
909                    }
910
911                    // no char supported, attempt to read the next char(s) with the
912                    // fallback font. In this case we only test the first character
913                    // and then go back to test with the main font.
914                    // Special test for 2-char characters.
915                    boolean foundFont = false;
916                    for (int f = 1 ; f < fonts.size() ; f++) {
917                        FontInfo fontInfo = fonts.get(f);
918
919                        // need to check that the font can display the character. We test
920                        // differently if the char is a high surrogate.
921                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
922                        upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
923                        if (upTo == -1) {
924                            // draw that char
925                            g.setFont(fontInfo.mFont);
926                            g.drawChars(text, i, charCount, (int)x, (int)y);
927
928                            // update x
929                            x += fontInfo.mMetrics.charsWidth(text, i, charCount);
930
931                            // update the index in the text, and move on
932                            i += charCount;
933                            foundFont = true;
934                            break;
935
936                        }
937                    }
938
939                    // in case no font can display the char, display it with the main font.
940                    // (it'll put a square probably)
941                    if (foundFont == false) {
942                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
943
944                        g.setFont(mainFont.mFont);
945                        g.drawChars(text, i, charCount, (int)x, (int)y);
946
947                        // measure it to advance x
948                        x += mainFont.mMetrics.charsWidth(text, i, charCount);
949
950                        // and move to the next chars.
951                        i += charCount;
952                    }
953                }
954            }
955        } finally {
956            g.dispose();
957        }
958    }
959
960    /*package*/ static void native_drawText(int nativeCanvas, String text,
961                                               int start, int end, float x,
962                                               float y, int flags, int paint) {
963        int count = end - start;
964        char[] buffer = TemporaryBuffer.obtain(count);
965        TextUtils.getChars(text, start, end, buffer, 0);
966
967        native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
968    }
969
970    /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
971            int start, int end, int contextStart, int contextEnd,
972            float x, float y, int flags, int paint) {
973        int count = end - start;
974        char[] buffer = TemporaryBuffer.obtain(count);
975        TextUtils.getChars(text, start, end, buffer, 0);
976
977        native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint);
978    }
979
980    /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
981            int start, int count, int contextStart, int contextCount,
982            float x, float y, int flags, int paint) {
983        native_drawText(nativeCanvas, text, 0, count, x, y, flags, paint);
984    }
985
986    /*package*/ static void native_drawPosText(int nativeCanvas,
987                                                  char[] text, int index,
988                                                  int count, float[] pos,
989                                                  int paint) {
990        // FIXME
991        throw new UnsupportedOperationException();
992    }
993
994    /*package*/ static void native_drawPosText(int nativeCanvas,
995                                                  String text, float[] pos,
996                                                  int paint) {
997        // FIXME
998        throw new UnsupportedOperationException();
999    }
1000
1001    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
1002                                                     char[] text, int index,
1003                                                     int count, int path,
1004                                                     float hOffset,
1005                                                     float vOffset, int bidiFlags,
1006                                                     int paint) {
1007        // FIXME
1008        throw new UnsupportedOperationException();
1009    }
1010
1011    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
1012                                                     String text, int path,
1013                                                     float hOffset,
1014                                                     float vOffset,
1015                                                     int flags, int paint) {
1016        // FIXME
1017        throw new UnsupportedOperationException();
1018    }
1019
1020    /*package*/ static void native_drawPicture(int nativeCanvas,
1021                                                  int nativePicture) {
1022        // FIXME
1023        throw new UnsupportedOperationException();
1024    }
1025
1026    /*package*/ static void finalizer(int nativeCanvas) {
1027        // get the delegate from the native int so that it can be disposed.
1028        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
1029        if (canvasDelegate == null) {
1030            assert false;
1031            return;
1032        }
1033
1034        canvasDelegate.dispose();
1035
1036        // remove it from the manager.
1037        sManager.removeDelegate(nativeCanvas);
1038    }
1039
1040    // ---- Private delegate/helper methods ----
1041
1042    /**
1043     * Executes a {@link Drawable} with a given canvas and paint.
1044     */
1045    private static void draw(int nCanvas, int nPaint, Drawable runnable) {
1046        // get the delegate from the native int.
1047        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
1048        if (canvasDelegate == null) {
1049            assert false;
1050            return;
1051        }
1052
1053        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
1054        if (paintDelegate == null) {
1055            assert false;
1056            return;
1057        }
1058
1059        // get a Graphics2D object configured with the drawing parameters.
1060        Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
1061
1062        try {
1063            runnable.draw(g, paintDelegate);
1064        } finally {
1065            // dispose Graphics2D object
1066            g.dispose();
1067        }
1068    }
1069
1070    private Canvas_Delegate(BufferedImage image) {
1071        setBitmap(image);
1072    }
1073
1074    private Canvas_Delegate() {
1075    }
1076
1077    /**
1078     * Disposes of the {@link Graphics2D} stack.
1079     */
1080    private void dispose() {
1081        mSnapshot.dispose();
1082    }
1083
1084    private int save(int saveFlags) {
1085        // get the current save count
1086        int count = mSnapshot.size();
1087
1088        // create a new snapshot and add it to the stack
1089        mSnapshot = new GcSnapshot(mSnapshot, saveFlags);
1090
1091        // return the old save count
1092        return count;
1093    }
1094
1095    /**
1096     * Restores the {@link GcSnapshot} to <var>saveCount</var>
1097     * @param saveCount the saveCount
1098     */
1099    private void restoreTo(int saveCount) {
1100        mSnapshot = mSnapshot.restoreTo(saveCount);
1101    }
1102
1103    /**
1104     * Restores the {@link GcSnapshot} to <var>saveCount</var>
1105     * @param saveCount the saveCount
1106     */
1107    private void restore() {
1108        mSnapshot = mSnapshot.restore();
1109    }
1110
1111    private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
1112        return mSnapshot.clipRect(left, top, right, bottom, regionOp);
1113    }
1114
1115    private void setBitmap(BufferedImage image) {
1116        mBufferedImage = image;
1117        assert mSnapshot.size() == 1;
1118        mSnapshot.setGraphics2D(mBufferedImage.createGraphics());
1119    }
1120
1121    /**
1122     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
1123     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
1124     */
1125    /*package*/ Graphics2D createCustomGraphics(Paint_Delegate paint) {
1126        // make new one
1127        Graphics2D g = getGcSnapshot().create();
1128
1129        // configure it
1130
1131        if (paint.isAntiAliased()) {
1132            g.setRenderingHint(
1133                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
1134            g.setRenderingHint(
1135                    RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1136        }
1137
1138        boolean useColorPaint = true;
1139
1140        // get the shader first, as it'll replace the color if it can be used it.
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                        useColorPaint = false;
1152                    }
1153                } else {
1154                    Bridge.getLog().fidelityWarning(null,
1155                            shaderDelegate.getSupportMessage(),
1156                            null);
1157                }
1158            }
1159        }
1160
1161        if (useColorPaint) {
1162            g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
1163        }
1164
1165        int style = paint.getStyle();
1166        if (style == Paint.Style.STROKE.nativeInt ||
1167                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
1168
1169            boolean customStroke = false;
1170
1171            int pathEffect = paint.getPathEffect();
1172            if (pathEffect > 0) {
1173                PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(pathEffect);
1174                assert effectDelegate != null;
1175                if (effectDelegate != null) {
1176                    if (effectDelegate.isSupported()) {
1177                        Stroke stroke = effectDelegate.getStroke(paint);
1178                        assert stroke != null;
1179                        if (stroke != null) {
1180                            g.setStroke(stroke);
1181                            customStroke = true;
1182                        }
1183                    } else {
1184                        Bridge.getLog().fidelityWarning(null,
1185                                effectDelegate.getSupportMessage(),
1186                                null);
1187                    }
1188                }
1189            }
1190
1191            // if no custom stroke as been set, set the default one.
1192            if (customStroke == false) {
1193                g.setStroke(new BasicStroke(
1194                        paint.getStrokeWidth(),
1195                        paint.getJavaCap(),
1196                        paint.getJavaJoin(),
1197                        paint.getStrokeMiter()));
1198            }
1199        }
1200
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();
1208                    assert composite != null;
1209                    if (composite != null) {
1210                        g.setComposite(composite);
1211                    }
1212                } else {
1213                    Bridge.getLog().fidelityWarning(null,
1214                            xfermodeDelegate.getSupportMessage(),
1215                            null);
1216                }
1217            }
1218        }
1219
1220        return g;
1221    }
1222
1223    private static void drawBitmap(
1224            int nativeCanvas,
1225            BufferedImage image,
1226            int nativePaintOrZero,
1227            int sleft, int stop, int sright, int sbottom,
1228            int dleft, int dtop, int dright, int dbottom) {
1229        // get the delegate from the native int.
1230        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
1231        if (canvasDelegate == null) {
1232            assert false;
1233            return;
1234        }
1235
1236        // get the delegate from the native int.
1237        Paint_Delegate paintDelegate = null;
1238        if (nativePaintOrZero > 0) {
1239            paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
1240            if (paintDelegate == null) {
1241                assert false;
1242                return;
1243            }
1244        }
1245
1246        drawBitmap(canvasDelegate, image, paintDelegate,
1247                sleft, stop, sright, sbottom,
1248                dleft, dtop, dright, dbottom);
1249    }
1250
1251    private static void drawBitmap(
1252            Canvas_Delegate canvasDelegate,
1253            BufferedImage image,
1254            Paint_Delegate paintDelegate,
1255            int sleft, int stop, int sright, int sbottom,
1256            int dleft, int dtop, int dright, int dbottom) {
1257        //FIXME add support for canvas, screen and bitmap densities.
1258
1259        Graphics2D g = canvasDelegate.getGcSnapshot().create();
1260        try {
1261            if (paintDelegate != null && paintDelegate.isFilterBitmap()) {
1262                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
1263                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
1264            }
1265
1266            g.drawImage(image, dleft, dtop, dright, dbottom,
1267                    sleft, stop, sright, sbottom, null);
1268        } finally {
1269            g.dispose();
1270        }
1271    }
1272}
1273
1274