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