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