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