Canvas_Delegate.java revision c2e9651bf386a1f7bf7fc706cf5424950570470c
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;
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    /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
890            int start, int end, int contextStart, int contextEnd,
891            float x, float y, int flags, int paint) {
892        int count = end - start;
893        char[] buffer = TemporaryBuffer.obtain(count);
894        TextUtils.getChars(text, start, end, buffer, 0);
895
896        native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint);
897    }
898
899    /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
900            int start, int count, int contextStart, int contextCount,
901            float x, float y, int flags, int paint) {
902        native_drawText(nativeCanvas, text, 0, count, x, y, flags, paint);
903    }
904
905    /*package*/ static void native_drawPosText(int nativeCanvas,
906                                                  char[] text, int index,
907                                                  int count, float[] pos,
908                                                  int paint) {
909        // FIXME
910        throw new UnsupportedOperationException();
911    }
912
913    /*package*/ static void native_drawPosText(int nativeCanvas,
914                                                  String text, float[] pos,
915                                                  int paint) {
916        // FIXME
917        throw new UnsupportedOperationException();
918    }
919
920    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
921                                                     char[] text, int index,
922                                                     int count, int path,
923                                                     float hOffset,
924                                                     float vOffset, int bidiFlags,
925                                                     int paint) {
926        // FIXME
927        throw new UnsupportedOperationException();
928    }
929
930    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
931                                                     String text, int path,
932                                                     float hOffset,
933                                                     float vOffset,
934                                                     int flags, int paint) {
935        // FIXME
936        throw new UnsupportedOperationException();
937    }
938
939    /*package*/ static void native_drawPicture(int nativeCanvas,
940                                                  int nativePicture) {
941        // FIXME
942        throw new UnsupportedOperationException();
943    }
944
945    /*package*/ static void finalizer(int nativeCanvas) {
946        sManager.removeDelegate(nativeCanvas);
947    }
948
949    // ---- Private delegate/helper methods ----
950
951    private Canvas_Delegate(BufferedImage image) {
952        setBitmap(image);
953    }
954
955    private Canvas_Delegate() {
956    }
957
958    private void setBitmap(BufferedImage image) {
959        mBufferedImage = image;
960        mGraphicsStack.push(mBufferedImage.createGraphics());
961    }
962
963    /**
964     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
965     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
966     */
967    private Graphics2D getCustomGraphics(Paint_Delegate paint) {
968        // make new one
969        Graphics2D g = getGraphics2d();
970        g = (Graphics2D)g.create();
971
972        // configure it
973
974        if (paint.isAntiAliased()) {
975            g.setRenderingHint(
976                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
977            g.setRenderingHint(
978                    RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
979        }
980
981        boolean useColorPaint = true;
982
983        // get the shader first, as it'll replace the color if it can be used it.
984        Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(paint.getShader());
985        if (shaderDelegate != null) {
986            java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
987            if (shaderPaint != null) {
988                g.setPaint(shaderPaint);
989                useColorPaint = false;
990            } else {
991                if (mLogger != null) {
992                    mLogger.warning(String.format(
993                            "Shader '%1$s' is not supported in the Layout Editor.",
994                            shaderDelegate.getClass().getCanonicalName()));
995                }
996            }
997        }
998
999        // need to get the alpha to set it in the composite.
1000        float falpha = 1.f;
1001
1002        if (useColorPaint) {
1003            g.setColor(new Color(paint.getColor()));
1004
1005            // the alpha is taken from the alpha channel of the color
1006            int alpha = paint.getAlpha();
1007            falpha = alpha / 255.f;
1008        }
1009
1010        int style = paint.getStyle();
1011        if (style == Paint.Style.STROKE.nativeInt ||
1012                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
1013
1014            PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(
1015                    paint.getPathEffect());
1016
1017            if (effectDelegate instanceof DashPathEffect_Delegate) {
1018                DashPathEffect_Delegate dpe = (DashPathEffect_Delegate)effectDelegate;
1019                g.setStroke(new BasicStroke(
1020                        paint.getStrokeWidth(),
1021                        paint.getJavaCap(),
1022                        paint.getJavaJoin(),
1023                        paint.getStrokeMiter(),
1024                        dpe.getIntervals(),
1025                        dpe.getPhase()));
1026            } else {
1027                g.setStroke(new BasicStroke(
1028                        paint.getStrokeWidth(),
1029                        paint.getJavaCap(),
1030                        paint.getJavaJoin(),
1031                        paint.getStrokeMiter()));
1032            }
1033        }
1034
1035        Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode());
1036        if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
1037            int mode = ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
1038
1039            setModeInGraphics(g, mode, falpha);
1040        } else {
1041            // default mode is src_over
1042            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
1043
1044            // if xfermode wasn't null, then it's something we don't support. log it.
1045            if (mLogger != null && xfermodeDelegate != null) {
1046                mLogger.warning(String.format(
1047                        "Xfermode '%1$s' is not supported in the Layout Editor.",
1048                        xfermodeDelegate.getClass().getCanonicalName()));
1049            }
1050        }
1051
1052        return g;
1053    }
1054
1055    private static void setModeInGraphics(Graphics2D g, int mode, float falpha) {
1056        for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
1057            if (m.nativeInt == mode) {
1058                setModeInGraphics(g, m, falpha);
1059                return;
1060            }
1061        }
1062    }
1063
1064    private static void setModeInGraphics(Graphics2D g, PorterDuff.Mode mode, float falpha) {
1065        switch (mode) {
1066            case CLEAR:
1067                g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha));
1068                break;
1069            case DARKEN:
1070                break;
1071            case DST:
1072                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, falpha));
1073                break;
1074            case DST_ATOP:
1075                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha));
1076                break;
1077            case DST_IN:
1078                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha));
1079                break;
1080            case DST_OUT:
1081                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha));
1082                break;
1083            case DST_OVER:
1084                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha));
1085                break;
1086            case LIGHTEN:
1087                break;
1088            case MULTIPLY:
1089                break;
1090            case SCREEN:
1091                break;
1092            case SRC:
1093                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, falpha));
1094                break;
1095            case SRC_ATOP:
1096                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha));
1097                break;
1098            case SRC_IN:
1099                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha));
1100                break;
1101            case SRC_OUT:
1102                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha));
1103                break;
1104            case SRC_OVER:
1105                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
1106                break;
1107            case XOR:
1108                g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, falpha));
1109                break;
1110        }
1111    }
1112
1113
1114    private static void drawBitmap(
1115            int nativeCanvas,
1116            BufferedImage image,
1117            int nativePaintOrZero,
1118            int sleft, int stop, int sright, int sbottom,
1119            int dleft, int dtop, int dright, int dbottom) {
1120        // get the delegate from the native int.
1121        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
1122        if (canvasDelegate == null) {
1123            assert false;
1124            return;
1125        }
1126
1127        // get the delegate from the native int.
1128        Paint_Delegate paintDelegate = null;
1129        if (nativePaintOrZero > 0) {
1130            paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
1131            if (paintDelegate == null) {
1132                assert false;
1133                return;
1134            }
1135        }
1136
1137        drawBitmap(canvasDelegate, image, paintDelegate,
1138                sleft, stop, sright, sbottom,
1139                dleft, dtop, dright, dbottom);
1140    }
1141
1142    private static void drawBitmap(
1143            Canvas_Delegate canvasDelegate,
1144            BufferedImage image,
1145            Paint_Delegate paintDelegate,
1146            int sleft, int stop, int sright, int sbottom,
1147            int dleft, int dtop, int dright, int dbottom) {
1148
1149        Graphics2D g = canvasDelegate.getGraphics2d();
1150
1151        Composite c = null;
1152
1153        if (paintDelegate != null) {
1154            if (paintDelegate.isFilterBitmap()) {
1155                g = (Graphics2D)g.create();
1156                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
1157                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
1158            }
1159
1160            if (paintDelegate.getAlpha() != 0xFF) {
1161                c = g.getComposite();
1162                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1163                        paintDelegate.getAlpha()/255.f));
1164            }
1165        }
1166
1167        g.drawImage(image, dleft, dtop, dright, dbottom,
1168                sleft, stop, sright, sbottom, null);
1169
1170        if (paintDelegate != null) {
1171            if (paintDelegate.isFilterBitmap()) {
1172                g.dispose();
1173            }
1174            if (c != null) {
1175                g.setComposite(c);
1176            }
1177        }
1178    }
1179
1180}
1181
1182