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