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