Canvas_Delegate.java revision f7c92077f0fa54a75dc03d788a344a8c8b5c6760
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.impl.DelegateManager;
21import com.android.layoutlib.bridge.impl.Stack;
22
23import android.graphics.Paint_Delegate.FontInfo;
24import android.text.TextUtils;
25
26import java.awt.AlphaComposite;
27import java.awt.BasicStroke;
28import java.awt.Color;
29import java.awt.Composite;
30import java.awt.Graphics2D;
31import java.awt.Rectangle;
32import java.awt.RenderingHints;
33import java.awt.geom.AffineTransform;
34import java.awt.image.BufferedImage;
35import java.util.List;
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, true /*alpha*/));
484
485            setModeInGraphics(graphics, mode);
486
487            graphics.fillRect(0, 0, canvasDelegate.mBufferedImage.getWidth(),
488                    canvasDelegate.mBufferedImage.getHeight());
489        } finally {
490            // dispose Graphics2D object
491            graphics.dispose();
492        }
493    }
494
495    /*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
496        // FIXME
497        throw new UnsupportedOperationException();
498    }
499
500    /*package*/ static void native_drawLine(int nativeCanvas, float startX,
501                                               float startY, float stopX,
502                                               float stopY, int paint) {
503        // get the delegate from the native int.
504        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
505        if (canvasDelegate == null) {
506            assert false;
507            return;
508        }
509
510        // get the delegate from the native int.
511        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
512        if (paintDelegate == null) {
513            assert false;
514            return;
515        }
516
517        // get a Graphics2D object configured with the drawing parameters.
518        Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
519
520        g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
521
522        // dispose Graphics2D object
523        g.dispose();
524    }
525
526    /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
527                                               int paint) {
528        native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
529    }
530
531    /*package*/ static void native_drawRect(int nativeCanvas, float left,
532                                               float top, float right,
533                                               float bottom, int paint) {
534        // get the delegate from the native int.
535        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
536        if (canvasDelegate == null) {
537            assert false;
538            return;
539        }
540
541        // get the delegate from the native int.
542        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
543        if (paintDelegate == null) {
544            assert false;
545            return;
546        }
547
548        if (right > left && bottom > top) {
549            // get a Graphics2D object configured with the drawing parameters.
550            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
551
552            int style = paintDelegate.getStyle();
553
554            // draw
555            if (style == Paint.Style.FILL.nativeInt ||
556                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
557                g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
558            }
559
560            if (style == Paint.Style.STROKE.nativeInt ||
561                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
562                g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
563            }
564
565            // dispose Graphics2D object
566            g.dispose();
567        }
568    }
569
570    /*package*/ static void native_drawOval(int nativeCanvas, RectF oval,
571                                               int paint) {
572        // get the delegate from the native int.
573        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
574        if (canvasDelegate == null) {
575            assert false;
576            return;
577        }
578
579        // get the delegate from the native int.
580        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
581        if (paintDelegate == null) {
582            assert false;
583            return;
584        }
585
586        if (oval.right > oval.left && oval.bottom > oval.top) {
587            // get a Graphics2D object configured with the drawing parameters.
588            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
589
590            int style = paintDelegate.getStyle();
591
592            // draw
593            if (style == Paint.Style.FILL.nativeInt ||
594                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
595                g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
596            }
597
598            if (style == Paint.Style.STROKE.nativeInt ||
599                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
600                g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
601            }
602
603            // dispose Graphics2D object
604            g.dispose();
605        }
606    }
607
608    /*package*/ static void native_drawCircle(int nativeCanvas, float cx,
609                                                 float cy, float radius,
610                                                 int paint) {
611        native_drawOval(nativeCanvas,
612                new RectF(cx - radius, cy - radius, radius*2, radius*2),
613                paint);
614    }
615
616    /*package*/ static void native_drawArc(int nativeCanvas, RectF oval,
617                                              float startAngle, float sweep,
618                                              boolean useCenter, int paint) {
619        // FIXME
620        throw new UnsupportedOperationException();
621    }
622
623    /*package*/ static void native_drawRoundRect(int nativeCanvas,
624                                                    RectF rect, float rx,
625                                                    float ry, int paint) {
626        // get the delegate from the native int.
627        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
628        if (canvasDelegate == null) {
629            assert false;
630            return;
631        }
632
633        // get the delegate from the native int.
634        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
635        if (paintDelegate == null) {
636            assert false;
637            return;
638        }
639
640        if (rect.right > rect.left && rect.bottom > rect.top) {
641            // get a Graphics2D object configured with the drawing parameters.
642            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
643
644            int style = paintDelegate.getStyle();
645
646            // draw
647            if (style == Paint.Style.FILL.nativeInt ||
648                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
649                g.fillRoundRect(
650                        (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
651                        (int)rx, (int)ry);
652            }
653
654            if (style == Paint.Style.STROKE.nativeInt ||
655                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
656                g.drawRoundRect(
657                        (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
658                        (int)rx, (int)ry);
659            }
660
661            // dispose Graphics2D object
662            g.dispose();
663        }
664    }
665
666    /*package*/ static void native_drawPath(int nativeCanvas, int path,
667                                               int paint) {
668        // FIXME
669        throw new UnsupportedOperationException();
670    }
671
672    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
673                                                 float left, float top,
674                                                 int nativePaintOrZero,
675                                                 int canvasDensity,
676                                                 int screenDensity,
677                                                 int bitmapDensity) {
678        // FIXME
679        throw new UnsupportedOperationException();
680    }
681
682    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
683                                                 Rect src, RectF dst,
684                                                 int nativePaintOrZero,
685                                                 int screenDensity,
686                                                 int bitmapDensity) {
687        // get the delegate from the native int.
688        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
689        if (bitmapDelegate == null) {
690            assert false;
691            return;
692        }
693
694        BufferedImage image = bitmapDelegate.getImage();
695
696        if (src == null) {
697            drawBitmap(nativeCanvas, image, nativePaintOrZero,
698                    0, 0, image.getWidth(), image.getHeight(),
699                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
700        } else {
701            drawBitmap(nativeCanvas, image, nativePaintOrZero,
702                    src.left, src.top, src.width(), src.height(),
703                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
704        }
705    }
706
707    /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
708                                                 Rect src, Rect dst,
709                                                 int nativePaintOrZero,
710                                                 int screenDensity,
711                                                 int bitmapDensity) {
712        // get the delegate from the native int.
713        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
714        if (bitmapDelegate == null) {
715            assert false;
716            return;
717        }
718
719        BufferedImage image = bitmapDelegate.getImage();
720
721        if (src == null) {
722            drawBitmap(nativeCanvas, image, nativePaintOrZero,
723                    0, 0, image.getWidth(), image.getHeight(),
724                    dst.left, dst.top, dst.right, dst.bottom);
725        } else {
726            drawBitmap(nativeCanvas, image, nativePaintOrZero,
727                    src.left, src.top, src.width(), src.height(),
728                    dst.left, dst.top, dst.right, dst.bottom);
729        }
730    }
731
732    /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
733                                                int offset, int stride, float x,
734                                                 float y, int width, int height,
735                                                 boolean hasAlpha,
736                                                 int nativePaintOrZero) {
737        // FIXME
738        throw new UnsupportedOperationException();
739    }
740
741    /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
742                                                      int nMatrix, int nPaint) {
743        // FIXME
744        throw new UnsupportedOperationException();
745    }
746
747    /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
748                                                    int meshWidth, int meshHeight,
749                                                    float[] verts, int vertOffset,
750                                                    int[] colors, int colorOffset, int nPaint) {
751        // FIXME
752        throw new UnsupportedOperationException();
753    }
754
755    /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n,
756                   float[] verts, int vertOffset, float[] texs, int texOffset,
757                   int[] colors, int colorOffset, short[] indices,
758                   int indexOffset, int indexCount, int nPaint) {
759        // FIXME
760        throw new UnsupportedOperationException();
761    }
762
763    /*package*/ static void native_drawText(int nativeCanvas, char[] text,
764                                               int index, int count, float x,
765                                               float y, int flags, int paint) {
766        // WARNING: the logic in this method is similar to Paint.measureText.
767        // Any change to this method should be reflected in Paint.measureText
768
769        // get the delegate from the native int.
770        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
771        if (canvasDelegate == null) {
772            assert false;
773            return;
774        }
775
776        // get the delegate from the native int.
777        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
778        if (paintDelegate == null) {
779            assert false;
780            return;
781        }
782
783        Graphics2D g = (Graphics2D) canvasDelegate.getCustomGraphics(paintDelegate);
784        try {
785            // Paint.TextAlign indicates how the text is positioned relative to X.
786            // LEFT is the default and there's nothing to do.
787            if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
788                float m = paintDelegate.measureText(text, index, count);
789                if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
790                    x -= m / 2;
791                } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
792                    x -= m;
793                }
794            }
795
796            List<FontInfo> fonts = paintDelegate.getFonts();
797
798            if (fonts.size() > 0) {
799                FontInfo mainFont = fonts.get(0);
800                int i = index;
801                int lastIndex = index + count;
802                while (i < lastIndex) {
803                    // always start with the main font.
804                    int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
805                    if (upTo == -1) {
806                        // draw all the rest and exit.
807                        g.setFont(mainFont.mFont);
808                        g.drawChars(text, i, lastIndex - i, (int)x, (int)y);
809                        return;
810                    } else if (upTo > 0) {
811                        // draw what's possible
812                        g.setFont(mainFont.mFont);
813                        g.drawChars(text, i, upTo - i, (int)x, (int)y);
814
815                        // compute the width that was drawn to increase x
816                        x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
817
818                        // move index to the first non displayed char.
819                        i = upTo;
820
821                        // don't call continue at this point. Since it is certain the main font
822                        // cannot display the font a index upTo (now ==i), we move on to the
823                        // fallback fonts directly.
824                    }
825
826                    // no char supported, attempt to read the next char(s) with the
827                    // fallback font. In this case we only test the first character
828                    // and then go back to test with the main font.
829                    // Special test for 2-char characters.
830                    boolean foundFont = false;
831                    for (int f = 1 ; f < fonts.size() ; f++) {
832                        FontInfo fontInfo = fonts.get(f);
833
834                        // need to check that the font can display the character. We test
835                        // differently if the char is a high surrogate.
836                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
837                        upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
838                        if (upTo == -1) {
839                            // draw that char
840                            g.setFont(fontInfo.mFont);
841                            g.drawChars(text, i, charCount, (int)x, (int)y);
842
843                            // update x
844                            x += fontInfo.mMetrics.charsWidth(text, i, charCount);
845
846                            // update the index in the text, and move on
847                            i += charCount;
848                            foundFont = true;
849                            break;
850
851                        }
852                    }
853
854                    // in case no font can display the char, display it with the main font.
855                    // (it'll put a square probably)
856                    if (foundFont == false) {
857                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
858
859                        g.setFont(mainFont.mFont);
860                        g.drawChars(text, i, charCount, (int)x, (int)y);
861
862                        // measure it to advance x
863                        x += mainFont.mMetrics.charsWidth(text, i, charCount);
864
865                        // and move to the next chars.
866                        i += charCount;
867                    }
868                }
869            }
870        } finally {
871            g.dispose();
872        }
873    }
874
875    /*package*/ static void native_drawText(int nativeCanvas, String text,
876                                               int start, int end, float x,
877                                               float y, int flags, int paint) {
878        int count = end - start;
879        char[] buffer = TemporaryBuffer.obtain(count);
880        TextUtils.getChars(text, start, end, buffer, 0);
881
882        native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
883    }
884
885    /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
886            int start, int end, int contextStart, int contextEnd,
887            float x, float y, int flags, int paint) {
888        int count = end - start;
889        char[] buffer = TemporaryBuffer.obtain(count);
890        TextUtils.getChars(text, start, end, buffer, 0);
891
892        native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint);
893    }
894
895    /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
896            int start, int count, int contextStart, int contextCount,
897            float x, float y, int flags, int paint) {
898        native_drawText(nativeCanvas, text, 0, count, x, y, flags, paint);
899    }
900
901    /*package*/ static void native_drawPosText(int nativeCanvas,
902                                                  char[] text, int index,
903                                                  int count, float[] pos,
904                                                  int paint) {
905        // FIXME
906        throw new UnsupportedOperationException();
907    }
908
909    /*package*/ static void native_drawPosText(int nativeCanvas,
910                                                  String text, float[] pos,
911                                                  int paint) {
912        // FIXME
913        throw new UnsupportedOperationException();
914    }
915
916    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
917                                                     char[] text, int index,
918                                                     int count, int path,
919                                                     float hOffset,
920                                                     float vOffset, int bidiFlags,
921                                                     int paint) {
922        // FIXME
923        throw new UnsupportedOperationException();
924    }
925
926    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
927                                                     String text, int path,
928                                                     float hOffset,
929                                                     float vOffset,
930                                                     int flags, int paint) {
931        // FIXME
932        throw new UnsupportedOperationException();
933    }
934
935    /*package*/ static void native_drawPicture(int nativeCanvas,
936                                                  int nativePicture) {
937        // FIXME
938        throw new UnsupportedOperationException();
939    }
940
941    /*package*/ static void finalizer(int nativeCanvas) {
942        sManager.removeDelegate(nativeCanvas);
943    }
944
945    // ---- Private delegate/helper methods ----
946
947    private Canvas_Delegate(BufferedImage image) {
948        setBitmap(image);
949    }
950
951    private Canvas_Delegate() {
952    }
953
954    private void setBitmap(BufferedImage image) {
955        mBufferedImage = image;
956        mGraphicsStack.push(mBufferedImage.createGraphics());
957    }
958
959    /**
960     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
961     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
962     */
963    private Graphics2D getCustomGraphics(Paint_Delegate paint) {
964        // make new one
965        Graphics2D g = getGraphics2d();
966        g = (Graphics2D)g.create();
967
968        // configure it
969
970        if (paint.isAntiAliased()) {
971            g.setRenderingHint(
972                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
973            g.setRenderingHint(
974                    RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
975        }
976
977        boolean useColorPaint = true;
978
979        // get the shader first, as it'll replace the color if it can be used it.
980        Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(paint.getShader());
981        if (shaderDelegate != null) {
982            java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
983            if (shaderPaint != null) {
984                g.setPaint(shaderPaint);
985                useColorPaint = false;
986            } else {
987                if (mLogger != null) {
988                    mLogger.warning(String.format(
989                            "Shader '%1$s' is not supported in the Layout Editor.",
990                            shaderDelegate.getClass().getCanonicalName()));
991                }
992            }
993        }
994
995        if (useColorPaint) {
996            g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
997        }
998
999        int style = paint.getStyle();
1000        if (style == Paint.Style.STROKE.nativeInt ||
1001                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
1002
1003            PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(
1004                    paint.getPathEffect());
1005
1006            if (effectDelegate instanceof DashPathEffect_Delegate) {
1007                DashPathEffect_Delegate dpe = (DashPathEffect_Delegate)effectDelegate;
1008                g.setStroke(new BasicStroke(
1009                        paint.getStrokeWidth(),
1010                        paint.getJavaCap(),
1011                        paint.getJavaJoin(),
1012                        paint.getStrokeMiter(),
1013                        dpe.getIntervals(),
1014                        dpe.getPhase()));
1015            } else {
1016                g.setStroke(new BasicStroke(
1017                        paint.getStrokeWidth(),
1018                        paint.getJavaCap(),
1019                        paint.getJavaJoin(),
1020                        paint.getStrokeMiter()));
1021            }
1022        }
1023
1024        Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode());
1025        if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
1026            int mode = ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
1027
1028            setModeInGraphics(g, mode);
1029        } else {
1030            // default mode is src_over
1031            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
1032
1033            // if xfermode wasn't null, then it's something we don't support. log it.
1034            if (mLogger != null && xfermodeDelegate != null) {
1035                mLogger.warning(String.format(
1036                        "Xfermode '%1$s' is not supported in the Layout Editor.",
1037                        xfermodeDelegate.getClass().getCanonicalName()));
1038            }
1039        }
1040
1041        return g;
1042    }
1043
1044    private static void setModeInGraphics(Graphics2D g, int mode) {
1045        for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
1046            if (m.nativeInt == mode) {
1047                setModeInGraphics(g, m);
1048                return;
1049            }
1050        }
1051    }
1052
1053    private static void setModeInGraphics(Graphics2D g, PorterDuff.Mode mode) {
1054        switch (mode) {
1055            case CLEAR:
1056                g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 1.0f /*alpha*/));
1057                break;
1058            case DARKEN:
1059                break;
1060            case DST:
1061                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, 1.0f /*alpha*/));
1062                break;
1063            case DST_ATOP:
1064                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1.0f /*alpha*/));
1065                break;
1066            case DST_IN:
1067                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0f /*alpha*/));
1068                break;
1069            case DST_OUT:
1070                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, 1.0f /*alpha*/));
1071                break;
1072            case DST_OVER:
1073                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, 1.0f /*alpha*/));
1074                break;
1075            case LIGHTEN:
1076                break;
1077            case MULTIPLY:
1078                break;
1079            case SCREEN:
1080                break;
1081            case SRC:
1082                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f /*alpha*/));
1083                break;
1084            case SRC_ATOP:
1085                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f /*alpha*/));
1086                break;
1087            case SRC_IN:
1088                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 1.0f /*alpha*/));
1089                break;
1090            case SRC_OUT:
1091                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, 1.0f /*alpha*/));
1092                break;
1093            case SRC_OVER:
1094                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f /*alpha*/));
1095                break;
1096            case XOR:
1097                g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, 1.0f /*alpha*/));
1098                break;
1099        }
1100    }
1101
1102
1103    private static void drawBitmap(
1104            int nativeCanvas,
1105            BufferedImage image,
1106            int nativePaintOrZero,
1107            int sleft, int stop, int sright, int sbottom,
1108            int dleft, int dtop, int dright, int dbottom) {
1109        // get the delegate from the native int.
1110        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
1111        if (canvasDelegate == null) {
1112            assert false;
1113            return;
1114        }
1115
1116        // get the delegate from the native int.
1117        Paint_Delegate paintDelegate = null;
1118        if (nativePaintOrZero > 0) {
1119            paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
1120            if (paintDelegate == null) {
1121                assert false;
1122                return;
1123            }
1124        }
1125
1126        drawBitmap(canvasDelegate, image, paintDelegate,
1127                sleft, stop, sright, sbottom,
1128                dleft, dtop, dright, dbottom);
1129    }
1130
1131    private static void drawBitmap(
1132            Canvas_Delegate canvasDelegate,
1133            BufferedImage image,
1134            Paint_Delegate paintDelegate,
1135            int sleft, int stop, int sright, int sbottom,
1136            int dleft, int dtop, int dright, int dbottom) {
1137
1138        Graphics2D g = canvasDelegate.getGraphics2d();
1139
1140        Composite c = null;
1141
1142        if (paintDelegate != null) {
1143            if (paintDelegate.isFilterBitmap()) {
1144                g = (Graphics2D)g.create();
1145                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
1146                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
1147            }
1148        }
1149
1150        g.drawImage(image, dleft, dtop, dright, dbottom,
1151                sleft, stop, sright, sbottom, null);
1152
1153        if (paintDelegate != null) {
1154            if (paintDelegate.isFilterBitmap()) {
1155                g.dispose();
1156            }
1157            if (c != null) {
1158                g.setComposite(c);
1159            }
1160        }
1161    }
1162
1163}
1164
1165