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