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