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