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