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