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