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