Paint_Delegate.java revision 3f9b0376c69a5f679661d773ea7b59b1e6787090
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.ide.common.rendering.api.LayoutLog;
20import com.android.layoutlib.bridge.Bridge;
21import com.android.layoutlib.bridge.impl.DelegateManager;
22
23import android.graphics.Paint.FontMetrics;
24import android.graphics.Paint.FontMetricsInt;
25import android.text.TextUtils;
26
27import java.awt.BasicStroke;
28import java.awt.Font;
29import java.awt.Shape;
30import java.awt.Stroke;
31import java.awt.Toolkit;
32import java.awt.font.FontRenderContext;
33import java.awt.geom.AffineTransform;
34import java.util.ArrayList;
35import java.util.Collections;
36import java.util.List;
37
38/**
39 * Delegate implementing the native methods of android.graphics.Paint
40 *
41 * Through the layoutlib_create tool, the original native methods of Paint 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 Paint class.
47 *
48 * @see DelegateManager
49 *
50 */
51public class Paint_Delegate {
52
53    /**
54     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
55     */
56    /*package*/ static final class FontInfo {
57        Font mFont;
58        java.awt.FontMetrics mMetrics;
59    }
60
61    // ---- delegate manager ----
62    private static final DelegateManager<Paint_Delegate> sManager =
63            new DelegateManager<Paint_Delegate>();
64
65    // ---- delegate helper data ----
66    private List<FontInfo> mFonts;
67    private final FontRenderContext mFontContext = new FontRenderContext(
68            new AffineTransform(), true, true);
69
70    // ---- delegate data ----
71    private int mFlags;
72    private int mColor;
73    private int mStyle;
74    private int mCap;
75    private int mJoin;
76    private int mTextAlign;
77    private int mTypeface;
78    private float mStrokeWidth;
79    private float mStrokeMiter;
80    private float mTextSize;
81    private float mTextScaleX;
82    private float mTextSkewX;
83
84    private int mXfermode;
85    private int mColorFilter;
86    private int mShader;
87    private int mPathEffect;
88    private int mMaskFilter;
89    private int mRasterizer;
90
91
92    // ---- Public Helper methods ----
93
94    public static Paint_Delegate getDelegate(int native_paint) {
95        return sManager.getDelegate(native_paint);
96    }
97
98    /**
99     * Returns the list of {@link Font} objects. The first item is the main font, the rest
100     * are fall backs for characters not present in the main font.
101     */
102    public List<FontInfo> getFonts() {
103        return mFonts;
104    }
105
106    public boolean isAntiAliased() {
107        return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0;
108    }
109
110    public boolean isFilterBitmap() {
111        return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0;
112    }
113
114    public int getStyle() {
115        return mStyle;
116    }
117
118    public int getColor() {
119        return mColor;
120    }
121
122    public int getAlpha() {
123        return mColor >>> 24;
124    }
125
126    public void setAlpha(int alpha) {
127        mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
128    }
129
130    public int getTextAlign() {
131        return mTextAlign;
132    }
133
134    public float getStrokeWidth() {
135        return mStrokeWidth;
136    }
137
138    /**
139     * returns the value of stroke miter needed by the java api.
140     */
141    public float getJavaStrokeMiter() {
142        float miter = mStrokeMiter * mStrokeWidth;
143        if (miter < 1.f) {
144            miter = 1.f;
145        }
146        return miter;
147    }
148
149    public int getJavaCap() {
150        switch (Paint.sCapArray[mCap]) {
151            case BUTT:
152                return BasicStroke.CAP_BUTT;
153            case ROUND:
154                return BasicStroke.CAP_ROUND;
155            default:
156            case SQUARE:
157                return BasicStroke.CAP_SQUARE;
158        }
159    }
160
161    public int getJavaJoin() {
162        switch (Paint.sJoinArray[mJoin]) {
163            default:
164            case MITER:
165                return BasicStroke.JOIN_MITER;
166            case ROUND:
167                return BasicStroke.JOIN_ROUND;
168            case BEVEL:
169                return BasicStroke.JOIN_BEVEL;
170        }
171    }
172
173    public Stroke getJavaStroke() {
174        PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(mPathEffect);
175        if (effectDelegate != null) {
176            if (effectDelegate.isSupported()) {
177                Stroke stroke = effectDelegate.getStroke(this);
178                assert stroke != null;
179                if (stroke != null) {
180                    return stroke;
181                }
182            } else {
183                Bridge.getLog().fidelityWarning(LayoutLog.TAG_PATHEFFECT,
184                        effectDelegate.getSupportMessage(),
185                        null);
186            }
187        }
188
189        // if no custom stroke as been set, set the default one.
190        return new BasicStroke(
191                    getStrokeWidth(),
192                    getJavaCap(),
193                    getJavaJoin(),
194                    getJavaStrokeMiter());
195    }
196
197    /**
198     * Returns the {@link Xfermode} delegate or null if none have been set
199     *
200     * @return the delegate or null.
201     */
202    public Xfermode_Delegate getXfermode() {
203        return  Xfermode_Delegate.getDelegate(mXfermode);
204    }
205
206    /**
207     * Returns the {@link ColorFilter} delegate or null if none have been set
208     *
209     * @return the delegate or null.
210     */
211    public ColorFilter_Delegate getColorFilter() {
212        return ColorFilter_Delegate.getDelegate(mColorFilter);
213    }
214
215    /**
216     * Returns the {@link Shader} delegate or null if none have been set
217     *
218     * @return the delegate or null.
219     */
220    public Shader_Delegate getShader() {
221        return Shader_Delegate.getDelegate(mShader);
222    }
223
224    /**
225     * Returns the {@link MaskFilter} delegate or null if none have been set
226     *
227     * @return the delegate or null.
228     */
229    public MaskFilter_Delegate getMaskFilter() {
230        return MaskFilter_Delegate.getDelegate(mMaskFilter);
231    }
232
233    /**
234     * Returns the {@link Rasterizer} delegate or null if none have been set
235     *
236     * @return the delegate or null.
237     */
238    public Rasterizer_Delegate getRasterizer() {
239        return Rasterizer_Delegate.getDelegate(mRasterizer);
240    }
241
242    // ---- native methods ----
243
244    /*package*/ static int getFlags(Paint thisPaint) {
245        // get the delegate from the native int.
246        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
247        if (delegate == null) {
248            return 0;
249        }
250
251        return delegate.mFlags;
252    }
253
254    /*package*/ static void setFlags(Paint thisPaint, int flags) {
255        // get the delegate from the native int.
256        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
257        if (delegate == null) {
258            return;
259        }
260
261        delegate.mFlags = flags;
262    }
263
264    /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
265        setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter);
266    }
267
268    /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
269        setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
270    }
271
272    /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
273        setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
274    }
275
276    /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
277        setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
278    }
279
280    /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
281        setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
282    }
283
284    /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
285        setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
286    }
287
288    /*package*/ static void setDither(Paint thisPaint, boolean dither) {
289        setFlag(thisPaint, Paint.DITHER_FLAG, dither);
290    }
291
292    /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
293        setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
294    }
295
296    /*package*/ static int getColor(Paint thisPaint) {
297        // get the delegate from the native int.
298        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
299        if (delegate == null) {
300            return 0;
301        }
302
303        return delegate.mColor;
304    }
305
306    /*package*/ static void setColor(Paint thisPaint, int color) {
307        // get the delegate from the native int.
308        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
309        if (delegate == null) {
310            return;
311        }
312
313        delegate.mColor = color;
314    }
315
316    /*package*/ static int getAlpha(Paint thisPaint) {
317        // get the delegate from the native int.
318        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
319        if (delegate == null) {
320            return 0;
321        }
322
323        return delegate.getAlpha();
324    }
325
326    /*package*/ static void setAlpha(Paint thisPaint, int a) {
327        // get the delegate from the native int.
328        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
329        if (delegate == null) {
330            return;
331        }
332
333        delegate.setAlpha(a);
334    }
335
336    /*package*/ static float getStrokeWidth(Paint thisPaint) {
337        // get the delegate from the native int.
338        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
339        if (delegate == null) {
340            return 1.f;
341        }
342
343        return delegate.mStrokeWidth;
344    }
345
346    /*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
347        // get the delegate from the native int.
348        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
349        if (delegate == null) {
350            return;
351        }
352
353        delegate.mStrokeWidth = width;
354    }
355
356    /*package*/ static float getStrokeMiter(Paint thisPaint) {
357        // get the delegate from the native int.
358        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
359        if (delegate == null) {
360            return 1.f;
361        }
362
363        return delegate.mStrokeMiter;
364    }
365
366    /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
367        // get the delegate from the native int.
368        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
369        if (delegate == null) {
370            return;
371        }
372
373        delegate.mStrokeMiter = miter;
374    }
375
376    /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy,
377            int color) {
378        // FIXME
379        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
380                "Paint.setShadowLayer is not supported.", null);
381    }
382
383    /*package*/ static float getTextSize(Paint thisPaint) {
384        // get the delegate from the native int.
385        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
386        if (delegate == null) {
387            return 1.f;
388        }
389
390        return delegate.mTextSize;
391    }
392
393    /*package*/ static void setTextSize(Paint thisPaint, float textSize) {
394        // get the delegate from the native int.
395        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
396        if (delegate == null) {
397            return;
398        }
399
400        delegate.mTextSize = textSize;
401        delegate.updateFontObject();
402    }
403
404    /*package*/ static float getTextScaleX(Paint thisPaint) {
405        // get the delegate from the native int.
406        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
407        if (delegate == null) {
408            return 1.f;
409        }
410
411        return delegate.mTextScaleX;
412    }
413
414    /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
415        // get the delegate from the native int.
416        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
417        if (delegate == null) {
418            return;
419        }
420
421        delegate.mTextScaleX = scaleX;
422        delegate.updateFontObject();
423    }
424
425    /*package*/ static float getTextSkewX(Paint thisPaint) {
426        // get the delegate from the native int.
427        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
428        if (delegate == null) {
429            return 1.f;
430        }
431
432        return delegate.mTextSkewX;
433    }
434
435    /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
436        // get the delegate from the native int.
437        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
438        if (delegate == null) {
439            return;
440        }
441
442        delegate.mTextSkewX = skewX;
443        delegate.updateFontObject();
444    }
445
446    /*package*/ static float ascent(Paint thisPaint) {
447        // get the delegate
448        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
449        if (delegate == null) {
450            return 0;
451        }
452
453        if (delegate.mFonts.size() > 0) {
454            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
455            // Android expects negative ascent so we invert the value from Java.
456            return - javaMetrics.getAscent();
457        }
458
459        return 0;
460    }
461
462    /*package*/ static float descent(Paint thisPaint) {
463        // get the delegate
464        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
465        if (delegate == null) {
466            return 0;
467        }
468
469        if (delegate.mFonts.size() > 0) {
470            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
471            return javaMetrics.getDescent();
472        }
473
474        return 0;
475
476    }
477
478    /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
479        // get the delegate
480        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
481        if (delegate == null) {
482            return 0;
483        }
484
485        return delegate.getFontMetrics(metrics);
486    }
487
488    /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
489        // get the delegate
490        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
491        if (delegate == null) {
492            return 0;
493        }
494
495        if (delegate.mFonts.size() > 0) {
496            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
497            if (fmi != null) {
498                // Android expects negative ascent so we invert the value from Java.
499                fmi.top = - javaMetrics.getMaxAscent();
500                fmi.ascent = - javaMetrics.getAscent();
501                fmi.descent = javaMetrics.getDescent();
502                fmi.bottom = javaMetrics.getMaxDescent();
503                fmi.leading = javaMetrics.getLeading();
504            }
505
506            return javaMetrics.getHeight();
507        }
508
509        return 0;
510    }
511
512    /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
513            int count) {
514        // WARNING: the logic in this method is similar to Canvas.drawText.
515        // Any change to this method should be reflected in Canvas.drawText
516
517        // get the delegate
518        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
519        if (delegate == null) {
520            return 0;
521        }
522
523        return delegate.measureText(text, index, count);
524    }
525
526    /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
527        return native_measureText(thisPaint, text.toCharArray(), start, end - start);
528    }
529
530    /*package*/ static float native_measureText(Paint thisPaint, String text) {
531        return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
532    }
533
534    /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
535            float maxWidth, float[] measuredWidth) {
536        // FIXME
537        throw new UnsupportedOperationException();
538    }
539
540    /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
541            float maxWidth, float[] measuredWidth) {
542        // FIXME
543        throw new UnsupportedOperationException();
544    }
545
546
547    /*package*/ static int native_init() {
548        Paint_Delegate newDelegate = new Paint_Delegate();
549        return sManager.addDelegate(newDelegate);
550    }
551
552    /*package*/ static int native_initWithPaint(int paint) {
553        // get the delegate from the native int.
554        Paint_Delegate delegate = sManager.getDelegate(paint);
555        if (delegate == null) {
556            return 0;
557        }
558
559        Paint_Delegate newDelegate = new Paint_Delegate(delegate);
560        return sManager.addDelegate(newDelegate);
561    }
562
563    /*package*/ static void native_reset(int native_object) {
564        // get the delegate from the native int.
565        Paint_Delegate delegate = sManager.getDelegate(native_object);
566        if (delegate == null) {
567            return;
568        }
569
570        delegate.reset();
571    }
572
573    /*package*/ static void native_set(int native_dst, int native_src) {
574        // get the delegate from the native int.
575        Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
576        if (delegate_dst == null) {
577            return;
578        }
579
580        // get the delegate from the native int.
581        Paint_Delegate delegate_src = sManager.getDelegate(native_src);
582        if (delegate_src == null) {
583            return;
584        }
585
586        delegate_dst.set(delegate_src);
587    }
588
589    /*package*/ static int native_getStyle(int native_object) {
590        // get the delegate from the native int.
591        Paint_Delegate delegate = sManager.getDelegate(native_object);
592        if (delegate == null) {
593            return 0;
594        }
595
596        return delegate.mStyle;
597    }
598
599    /*package*/ static void native_setStyle(int native_object, int style) {
600        // get the delegate from the native int.
601        Paint_Delegate delegate = sManager.getDelegate(native_object);
602        if (delegate == null) {
603            return;
604        }
605
606        delegate.mStyle = style;
607    }
608
609    /*package*/ static int native_getStrokeCap(int native_object) {
610        // get the delegate from the native int.
611        Paint_Delegate delegate = sManager.getDelegate(native_object);
612        if (delegate == null) {
613            return 0;
614        }
615
616        return delegate.mCap;
617    }
618
619    /*package*/ static void native_setStrokeCap(int native_object, int cap) {
620        // get the delegate from the native int.
621        Paint_Delegate delegate = sManager.getDelegate(native_object);
622        if (delegate == null) {
623            return;
624        }
625
626        delegate.mCap = cap;
627    }
628
629    /*package*/ static int native_getStrokeJoin(int native_object) {
630        // get the delegate from the native int.
631        Paint_Delegate delegate = sManager.getDelegate(native_object);
632        if (delegate == null) {
633            return 0;
634        }
635
636        return delegate.mJoin;
637    }
638
639    /*package*/ static void native_setStrokeJoin(int native_object, int join) {
640        // get the delegate from the native int.
641        Paint_Delegate delegate = sManager.getDelegate(native_object);
642        if (delegate == null) {
643            return;
644        }
645
646        delegate.mJoin = join;
647    }
648
649    /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) {
650        Paint_Delegate paint = sManager.getDelegate(native_object);
651        if (paint == null) {
652            return false;
653        }
654
655        Path_Delegate srcPath = Path_Delegate.getDelegate(src);
656        if (srcPath == null) {
657            return true;
658        }
659
660        Path_Delegate dstPath = Path_Delegate.getDelegate(dst);
661        if (dstPath == null) {
662            return true;
663        }
664
665        Stroke stroke = paint.getJavaStroke();
666        Shape strokeShape = stroke.createStrokedShape(srcPath.getJavaShape());
667
668        dstPath.setJavaShape(strokeShape);
669
670        // FIXME figure out the return value?
671        return true;
672    }
673
674    /*package*/ static int native_setShader(int native_object, int shader) {
675        // get the delegate from the native int.
676        Paint_Delegate delegate = sManager.getDelegate(native_object);
677        if (delegate == null) {
678            return shader;
679        }
680
681        return delegate.mShader = shader;
682    }
683
684    /*package*/ static int native_setColorFilter(int native_object, int filter) {
685        // get the delegate from the native int.
686        Paint_Delegate delegate = sManager.getDelegate(native_object);
687        if (delegate == null) {
688            return filter;
689        }
690
691        delegate.mColorFilter = filter;
692
693        // since none of those are supported, display a fidelity warning right away
694        ColorFilter_Delegate filterDelegate = delegate.getColorFilter();
695        if (filterDelegate != null && filterDelegate.isSupported() == false) {
696            Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
697                    filterDelegate.getSupportMessage(), null);
698        }
699
700        return filter;
701    }
702
703    /*package*/ static int native_setXfermode(int native_object, int xfermode) {
704        // get the delegate from the native int.
705        Paint_Delegate delegate = sManager.getDelegate(native_object);
706        if (delegate == null) {
707            return xfermode;
708        }
709
710        return delegate.mXfermode = xfermode;
711    }
712
713    /*package*/ static int native_setPathEffect(int native_object, int effect) {
714        // get the delegate from the native int.
715        Paint_Delegate delegate = sManager.getDelegate(native_object);
716        if (delegate == null) {
717            return effect;
718        }
719
720        return delegate.mPathEffect = effect;
721    }
722
723    /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) {
724        // get the delegate from the native int.
725        Paint_Delegate delegate = sManager.getDelegate(native_object);
726        if (delegate == null) {
727            return maskfilter;
728        }
729
730        delegate.mMaskFilter = maskfilter;
731
732        // since none of those are supported, display a fidelity warning right away
733        MaskFilter_Delegate filterDelegate = delegate.getMaskFilter();
734        if (filterDelegate != null && filterDelegate.isSupported() == false) {
735            Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
736                    filterDelegate.getSupportMessage(), null);
737        }
738
739        return maskfilter;
740    }
741
742    /*package*/ static int native_setTypeface(int native_object, int typeface) {
743        // get the delegate from the native int.
744        Paint_Delegate delegate = sManager.getDelegate(native_object);
745        if (delegate == null) {
746            return 0;
747        }
748
749        delegate.mTypeface = typeface;
750        delegate.updateFontObject();
751        return delegate.mTypeface;
752    }
753
754    /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
755        // get the delegate from the native int.
756        Paint_Delegate delegate = sManager.getDelegate(native_object);
757        if (delegate == null) {
758            return rasterizer;
759        }
760
761        delegate.mRasterizer = rasterizer;
762
763        // since none of those are supported, display a fidelity warning right away
764        Rasterizer_Delegate rasterizerDelegate = delegate.getRasterizer();
765        if (rasterizerDelegate != null && rasterizerDelegate.isSupported() == false) {
766            Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
767                    rasterizerDelegate.getSupportMessage(), null);
768        }
769
770        return rasterizer;
771    }
772
773    /*package*/ static int native_getTextAlign(int native_object) {
774        // get the delegate from the native int.
775        Paint_Delegate delegate = sManager.getDelegate(native_object);
776        if (delegate == null) {
777            return 0;
778        }
779
780        return delegate.mTextAlign;
781    }
782
783    /*package*/ static void native_setTextAlign(int native_object, int align) {
784        // get the delegate from the native int.
785        Paint_Delegate delegate = sManager.getDelegate(native_object);
786        if (delegate == null) {
787            return;
788        }
789
790        delegate.mTextAlign = align;
791    }
792
793    /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) {
794        // get the delegate from the native int.
795        Paint_Delegate delegate = sManager.getDelegate(native_paint);
796        if (delegate == null) {
797            return 0.f;
798        }
799
800        return delegate.getFontMetrics(metrics);
801    }
802
803    /*package*/ static int native_getTextWidths(int native_object, char[] text, int index,
804            int count, float[] widths) {
805        // FIXME
806        throw new UnsupportedOperationException();
807    }
808
809    /*package*/ static int native_getTextWidths(int native_object, String text, int start,
810            int end, float[] widths) {
811        // FIXME
812        throw new UnsupportedOperationException();
813    }
814
815    /*package*/ static float native_getTextRunAdvances(int native_object,
816            char[] text, int index, int count, int contextIndex, int contextCount,
817            int flags, float[] advances, int advancesIndex) {
818        // get the delegate from the native int.
819        Paint_Delegate delegate = sManager.getDelegate(native_object);
820        if (delegate == null) {
821            return 0.f;
822        }
823
824        if (delegate.mFonts.size() > 0) {
825            // FIXME: handle multi-char characters (see measureText)
826            float totalAdvance = 0;
827            for (int i = 0; i < count; i++) {
828                char c = text[i + index];
829                boolean found = false;
830                for (FontInfo info : delegate.mFonts) {
831                    if (info.mFont.canDisplay(c)) {
832                        float adv = info.mMetrics.charWidth(c);
833                        totalAdvance += adv;
834                        if (advances != null) {
835                            advances[i] = adv;
836                        }
837
838                        found = true;
839                        break;
840                    }
841                }
842
843                if (found == false) {
844                    // no advance for this char.
845                    if (advances != null) {
846                        advances[i] = 0.f;
847                    }
848                }
849            }
850
851            return totalAdvance;
852        }
853
854        return 0;
855
856    }
857
858    /*package*/ static float native_getTextRunAdvances(int native_object,
859            String text, int start, int end, int contextStart, int contextEnd,
860            int flags, float[] advances, int advancesIndex) {
861        // FIXME: support contextStart, contextEnd and direction flag
862        int count = end - start;
863        char[] buffer = TemporaryBuffer.obtain(count);
864        TextUtils.getChars(text, start, end, buffer, 0);
865
866        return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
867                contextEnd - contextStart, flags, advances, advancesIndex);
868    }
869
870    /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text,
871            int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
872        // FIXME
873        throw new UnsupportedOperationException();
874    }
875
876    /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text,
877            int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
878        // FIXME
879        throw new UnsupportedOperationException();
880    }
881
882    /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
883                char[] text, int index, int count, float x, float y, int path) {
884        // FIXME
885        throw new UnsupportedOperationException();
886    }
887
888    /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
889            String text, int start, int end, float x, float y, int path) {
890        // FIXME
891        throw new UnsupportedOperationException();
892    }
893
894    /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start,
895            int end, Rect bounds) {
896        // FIXME
897        throw new UnsupportedOperationException();
898    }
899
900    /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index,
901            int count, Rect bounds) {
902        // FIXME
903        throw new UnsupportedOperationException();
904    }
905
906    /*package*/ static void finalizer(int nativePaint) {
907        sManager.removeDelegate(nativePaint);
908    }
909
910    // ---- Private delegate/helper methods ----
911
912    /*package*/ Paint_Delegate() {
913        reset();
914    }
915
916    private Paint_Delegate(Paint_Delegate paint) {
917        set(paint);
918    }
919
920    private void set(Paint_Delegate paint) {
921        mFlags = paint.mFlags;
922        mColor = paint.mColor;
923        mStyle = paint.mStyle;
924        mCap = paint.mCap;
925        mJoin = paint.mJoin;
926        mTextAlign = paint.mTextAlign;
927        mTypeface = paint.mTypeface;
928        mStrokeWidth = paint.mStrokeWidth;
929        mStrokeMiter = paint.mStrokeMiter;
930        mTextSize = paint.mTextSize;
931        mTextScaleX = paint.mTextScaleX;
932        mTextSkewX = paint.mTextSkewX;
933        mXfermode = paint.mXfermode;
934        mColorFilter = paint.mColorFilter;
935        mShader = paint.mShader;
936        mPathEffect = paint.mPathEffect;
937        mMaskFilter = paint.mMaskFilter;
938        mRasterizer = paint.mRasterizer;
939        updateFontObject();
940    }
941
942    private void reset() {
943        mFlags = Paint.DEFAULT_PAINT_FLAGS;
944        mColor = 0xFF000000;
945        mStyle = Paint.Style.FILL.nativeInt;
946        mCap = Paint.Cap.BUTT.nativeInt;
947        mJoin = Paint.Join.MITER.nativeInt;
948        mTextAlign = 0;
949        mTypeface = Typeface.sDefaults[0].native_instance;
950        mStrokeWidth = 1.f;
951        mStrokeMiter = 4.f;
952        mTextSize = 20.f;
953        mTextScaleX = 1.f;
954        mTextSkewX = 0.f;
955        mXfermode = 0;
956        mColorFilter = 0;
957        mShader = 0;
958        mPathEffect = 0;
959        mMaskFilter = 0;
960        mRasterizer = 0;
961        updateFontObject();
962    }
963
964    /**
965     * Update the {@link Font} object from the typeface, text size and scaling
966     */
967    @SuppressWarnings("deprecation")
968    private void updateFontObject() {
969        if (mTypeface != 0) {
970            // Get the fonts from the TypeFace object.
971            List<Font> fonts = Typeface_Delegate.getFonts(mTypeface);
972
973            // create new font objects as well as FontMetrics, based on the current text size
974            // and skew info.
975            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
976            for (Font font : fonts) {
977                FontInfo info = new FontInfo();
978                info.mFont = font.deriveFont(mTextSize);
979                if (mTextScaleX != 1.0 || mTextSkewX != 0) {
980                    // TODO: support skew
981                    info.mFont = info.mFont.deriveFont(new AffineTransform(
982                            mTextScaleX, mTextSkewX, 0, 0, 1, 0));
983                }
984                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
985
986                infoList.add(info);
987            }
988
989            mFonts = Collections.unmodifiableList(infoList);
990        }
991    }
992
993    /*package*/ float measureText(char[] text, int index, int count) {
994        if (mFonts.size() > 0) {
995            FontInfo mainFont = mFonts.get(0);
996            int i = index;
997            int lastIndex = index + count;
998            float total = 0f;
999            while (i < lastIndex) {
1000                // always start with the main font.
1001                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
1002                if (upTo == -1) {
1003                    // shortcut to exit
1004                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
1005                } else if (upTo > 0) {
1006                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
1007                    i = upTo;
1008                    // don't call continue at this point. Since it is certain the main font
1009                    // cannot display the font a index upTo (now ==i), we move on to the
1010                    // fallback fonts directly.
1011                }
1012
1013                // no char supported, attempt to read the next char(s) with the
1014                // fallback font. In this case we only test the first character
1015                // and then go back to test with the main font.
1016                // Special test for 2-char characters.
1017                boolean foundFont = false;
1018                for (int f = 1 ; f < mFonts.size() ; f++) {
1019                    FontInfo fontInfo = mFonts.get(f);
1020
1021                    // need to check that the font can display the character. We test
1022                    // differently if the char is a high surrogate.
1023                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
1024                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
1025                    if (upTo == -1) {
1026                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
1027                        i += charCount;
1028                        foundFont = true;
1029                        break;
1030
1031                    }
1032                }
1033
1034                // in case no font can display the char, measure it with the main font.
1035                if (foundFont == false) {
1036                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
1037                    total += mainFont.mMetrics.charsWidth(text, i, size);
1038                    i += size;
1039                }
1040            }
1041        }
1042
1043        return 0;
1044    }
1045
1046    private float getFontMetrics(FontMetrics metrics) {
1047        if (mFonts.size() > 0) {
1048            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
1049            if (metrics != null) {
1050                // Android expects negative ascent so we invert the value from Java.
1051                metrics.top = - javaMetrics.getMaxAscent();
1052                metrics.ascent = - javaMetrics.getAscent();
1053                metrics.descent = javaMetrics.getDescent();
1054                metrics.bottom = javaMetrics.getMaxDescent();
1055                metrics.leading = javaMetrics.getLeading();
1056            }
1057
1058            return javaMetrics.getHeight();
1059        }
1060
1061        return 0;
1062    }
1063
1064
1065
1066    private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
1067        // get the delegate from the native int.
1068        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
1069        if (delegate == null) {
1070            return;
1071        }
1072
1073        if (flagValue) {
1074            delegate.mFlags |= flagMask;
1075        } else {
1076            delegate.mFlags &= ~flagMask;
1077        }
1078    }
1079}
1080