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