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