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