1 /*
2  * Copyright (C) 2006 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 android.annotation.ColorInt;
20 import android.annotation.ColorLong;
21 import android.annotation.IntDef;
22 import android.annotation.IntRange;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.Px;
26 import android.annotation.Size;
27 import android.compat.annotation.UnsupportedAppUsage;
28 import android.graphics.fonts.FontVariationAxis;
29 import android.os.Build;
30 import android.os.LocaleList;
31 import android.text.GraphicsOperations;
32 import android.text.SpannableString;
33 import android.text.SpannedString;
34 import android.text.TextUtils;
35 
36 import com.android.internal.annotations.GuardedBy;
37 
38 import dalvik.annotation.optimization.CriticalNative;
39 import dalvik.annotation.optimization.FastNative;
40 
41 import libcore.util.NativeAllocationRegistry;
42 
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.RetentionPolicy;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.HashMap;
48 import java.util.Locale;
49 import java.util.Objects;
50 
51 /**
52  * The Paint class holds the style and color information about how to draw
53  * geometries, text and bitmaps.
54  */
55 public class Paint {
56 
57     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
58     private long mNativePaint;
59     private long mNativeShader;
60     private long mNativeColorFilter;
61 
62     // Use a Holder to allow static initialization of Paint in the boot image.
63     private static class NoImagePreloadHolder {
64         public static final NativeAllocationRegistry sRegistry =
65                 NativeAllocationRegistry.createMalloced(
66                 Paint.class.getClassLoader(), nGetNativeFinalizer());
67     }
68 
69     @ColorLong private long mColor;
70     private ColorFilter     mColorFilter;
71     private MaskFilter      mMaskFilter;
72     private PathEffect      mPathEffect;
73     private Shader          mShader;
74     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
75     private Typeface        mTypeface;
76     private Xfermode        mXfermode;
77 
78     private boolean         mHasCompatScaling;
79     private float           mCompatScaling;
80     private float           mInvCompatScaling;
81 
82     private LocaleList      mLocales;
83     private String          mFontFeatureSettings;
84     private String          mFontVariationSettings;
85 
86     private float           mShadowLayerRadius;
87     private float           mShadowLayerDx;
88     private float           mShadowLayerDy;
89     @ColorLong private long mShadowLayerColor;
90 
91     private static final Object sCacheLock = new Object();
92 
93     /**
94      * Cache for the Minikin language list ID.
95      *
96      * A map from a string representation of the LocaleList to Minikin's language list ID.
97      */
98     @GuardedBy("sCacheLock")
99     private static final HashMap<String, Integer> sMinikinLocaleListIdCache = new HashMap<>();
100 
101     /**
102      * @hide
103      */
104     public  int         mBidiFlags = BIDI_DEFAULT_LTR;
105 
106     static final Style[] sStyleArray = {
107         Style.FILL, Style.STROKE, Style.FILL_AND_STROKE
108     };
109     static final Cap[] sCapArray = {
110         Cap.BUTT, Cap.ROUND, Cap.SQUARE
111     };
112     static final Join[] sJoinArray = {
113         Join.MITER, Join.ROUND, Join.BEVEL
114     };
115     static final Align[] sAlignArray = {
116         Align.LEFT, Align.CENTER, Align.RIGHT
117     };
118 
119     /** @hide */
120     @Retention(RetentionPolicy.SOURCE)
121     @IntDef(flag = true, value = {
122             ANTI_ALIAS_FLAG,
123             FILTER_BITMAP_FLAG,
124             DITHER_FLAG,
125             UNDERLINE_TEXT_FLAG,
126             STRIKE_THRU_TEXT_FLAG,
127             FAKE_BOLD_TEXT_FLAG,
128             LINEAR_TEXT_FLAG,
129             SUBPIXEL_TEXT_FLAG,
130             EMBEDDED_BITMAP_TEXT_FLAG
131     })
132     public @interface PaintFlag{}
133 
134     /**
135      * Paint flag that enables antialiasing when drawing.
136      *
137      * <p>Enabling this flag will cause all draw operations that support
138      * antialiasing to use it.</p>
139      *
140      * <p>Notable draw operations that do <b>not</b> support antialiasing include:</p>
141      * <ul>
142      *      <li>{@link android.graphics.Canvas#drawBitmapMesh}</li>
143      *      <li>{@link android.graphics.Canvas#drawPatch}</li>
144      *      <li>{@link android.graphics.Canvas#drawVertices}</li>
145      * </ul>
146      *
147      * @see #Paint(int)
148      * @see #setFlags(int)
149      */
150     public static final int ANTI_ALIAS_FLAG     = 0x01;
151     /**
152      * Paint flag that enables bilinear sampling on scaled bitmaps.
153      *
154      * <p>If cleared, scaled bitmaps will be drawn with nearest neighbor
155      * sampling, likely resulting in artifacts. This should generally be on
156      * when drawing bitmaps, unless performance-bound (rendering to software
157      * canvas) or preferring pixelation artifacts to blurriness when scaling
158      * significantly.</p>
159      *
160      * <p>If bitmaps are scaled for device density at creation time (as
161      * resource bitmaps often are) the filtering will already have been
162      * done.</p>
163      *
164      * <p>On devices running {@link Build.VERSION_CODES#O} and below, hardware
165      * accelerated drawing always uses bilinear sampling on scaled bitmaps,
166      * regardless of this flag. On devices running {@link Build.VERSION_CODES#Q}
167      * and above, this flag defaults to being set on a new {@code Paint}. It can
168      * be cleared with {@link #setFlags} or {@link #setFilterBitmap}.</p>
169      *
170      * @see #Paint()
171      * @see #Paint(int)
172      * @see #setFlags(int)
173      */
174     public static final int FILTER_BITMAP_FLAG  = 0x02;
175     /**
176      * Paint flag that enables dithering when blitting.
177      *
178      * <p>Enabling this flag applies a dither to any blit operation where the
179      * target's colour space is more constrained than the source.
180      *
181      * @see #Paint(int)
182      * @see #setFlags(int)
183      */
184     public static final int DITHER_FLAG         = 0x04;
185     /**
186      * Paint flag that applies an underline decoration to drawn text.
187      *
188      * @see #Paint(int)
189      * @see #setFlags(int)
190      */
191     public static final int UNDERLINE_TEXT_FLAG = 0x08;
192     /**
193      * Paint flag that applies a strike-through decoration to drawn text.
194      *
195      * @see #Paint(int)
196      * @see #setFlags(int)
197      */
198     public static final int STRIKE_THRU_TEXT_FLAG = 0x10;
199     /**
200      * Paint flag that applies a synthetic bolding effect to drawn text.
201      *
202      * <p>Enabling this flag will cause text draw operations to apply a
203      * simulated bold effect when drawing a {@link Typeface} that is not
204      * already bold.</p>
205      *
206      * @see #Paint(int)
207      * @see #setFlags(int)
208      */
209     public static final int FAKE_BOLD_TEXT_FLAG = 0x20;
210     /**
211      * Paint flag that enables smooth linear scaling of text.
212      *
213      * <p>Enabling this flag does not actually scale text, but rather adjusts
214      * text draw operations to deal gracefully with smooth adjustment of scale.
215      * When this flag is enabled, font hinting is disabled to prevent shape
216      * deformation between scale factors, and glyph caching is disabled due to
217      * the large number of glyph images that will be generated.</p>
218      *
219      * <p>{@link #SUBPIXEL_TEXT_FLAG} should be used in conjunction with this
220      * flag to prevent glyph positions from snapping to whole pixel values as
221      * scale factor is adjusted.</p>
222      *
223      * @see #Paint(int)
224      * @see #setFlags(int)
225      */
226     public static final int LINEAR_TEXT_FLAG    = 0x40;
227     /**
228      * Paint flag that enables subpixel positioning of text.
229      *
230      * <p>Enabling this flag causes glyph advances to be computed with subpixel
231      * accuracy.</p>
232      *
233      * <p>This can be used with {@link #LINEAR_TEXT_FLAG} to prevent text from
234      * jittering during smooth scale transitions.</p>
235      *
236      * @see #Paint(int)
237      * @see #setFlags(int)
238      */
239     public static final int SUBPIXEL_TEXT_FLAG  = 0x80;
240     /** Legacy Paint flag, no longer used. */
241     public static final int DEV_KERN_TEXT_FLAG  = 0x100;
242     /** @hide bit mask for the flag enabling subpixel glyph rendering for text */
243     public static final int LCD_RENDER_TEXT_FLAG = 0x200;
244     /**
245      * Paint flag that enables the use of bitmap fonts when drawing text.
246      *
247      * <p>Disabling this flag will prevent text draw operations from using
248      * embedded bitmap strikes in fonts, causing fonts with both scalable
249      * outlines and bitmap strikes to draw only the scalable outlines, and
250      * fonts with only bitmap strikes to not draw at all.</p>
251      *
252      * @see #Paint(int)
253      * @see #setFlags(int)
254      */
255     public static final int EMBEDDED_BITMAP_TEXT_FLAG = 0x400;
256     /** @hide bit mask for the flag forcing freetype's autohinter on for text */
257     public static final int AUTO_HINTING_TEXT_FLAG = 0x800;
258     /** @hide bit mask for the flag enabling vertical rendering for text */
259     public static final int VERTICAL_TEXT_FLAG = 0x1000;
260 
261     // These flags are always set on a new/reset paint, even if flags 0 is passed.
262     static final int HIDDEN_DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG
263             | FILTER_BITMAP_FLAG;
264 
265     /**
266      * Font hinter option that disables font hinting.
267      *
268      * @see #setHinting(int)
269      */
270     public static final int HINTING_OFF = 0x0;
271 
272     /**
273      * Font hinter option that enables font hinting.
274      *
275      * @see #setHinting(int)
276      */
277     public static final int HINTING_ON = 0x1;
278 
279     /**
280      * Bidi flag to set LTR paragraph direction.
281      *
282      * @hide
283      */
284     public static final int BIDI_LTR = 0x0;
285 
286     /**
287      * Bidi flag to set RTL paragraph direction.
288      *
289      * @hide
290      */
291     public static final int BIDI_RTL = 0x1;
292 
293     /**
294      * Bidi flag to detect paragraph direction via heuristics, defaulting to
295      * LTR.
296      *
297      * @hide
298      */
299     public static final int BIDI_DEFAULT_LTR = 0x2;
300 
301     /**
302      * Bidi flag to detect paragraph direction via heuristics, defaulting to
303      * RTL.
304      *
305      * @hide
306      */
307     public static final int BIDI_DEFAULT_RTL = 0x3;
308 
309     /**
310      * Bidi flag to override direction to all LTR (ignore bidi).
311      *
312      * @hide
313      */
314     public static final int BIDI_FORCE_LTR = 0x4;
315 
316     /**
317      * Bidi flag to override direction to all RTL (ignore bidi).
318      *
319      * @hide
320      */
321     public static final int BIDI_FORCE_RTL = 0x5;
322 
323     /**
324      * Maximum Bidi flag value.
325      * @hide
326      */
327     private static final int BIDI_MAX_FLAG_VALUE = BIDI_FORCE_RTL;
328 
329     /**
330      * Mask for bidi flags.
331      * @hide
332      */
333     private static final int BIDI_FLAG_MASK = 0x7;
334 
335     /**
336      * Flag for getTextRunAdvances indicating left-to-right run direction.
337      * @hide
338      */
339     public static final int DIRECTION_LTR = 0;
340 
341     /**
342      * Flag for getTextRunAdvances indicating right-to-left run direction.
343      * @hide
344      */
345     public static final int DIRECTION_RTL = 1;
346 
347     /** @hide */
348     @IntDef(prefix = { "CURSOR_" }, value = {
349         CURSOR_AFTER, CURSOR_AT_OR_AFTER, CURSOR_BEFORE, CURSOR_AT_OR_BEFORE
350     })
351     @Retention(RetentionPolicy.SOURCE)
352     public @interface CursorOption {}
353 
354     /**
355      * Option for getTextRunCursor.
356      *
357      * Compute the valid cursor after offset or the limit of the context, whichever is less.
358      */
359     public static final int CURSOR_AFTER = 0;
360 
361     /**
362      * Option for getTextRunCursor.
363      *
364      * Compute the valid cursor at or after the offset or the limit of the context, whichever is
365      * less.
366      */
367     public static final int CURSOR_AT_OR_AFTER = 1;
368 
369      /**
370      * Option for getTextRunCursor.
371      *
372      * Compute the valid cursor before offset or the start of the context, whichever is greater.
373      */
374     public static final int CURSOR_BEFORE = 2;
375 
376    /**
377      * Option for getTextRunCursor.
378      *
379      * Compute the valid cursor at or before offset or the start of the context, whichever is
380      * greater.
381      */
382     public static final int CURSOR_AT_OR_BEFORE = 3;
383 
384     /**
385      * Option for getTextRunCursor.
386      *
387      * Return offset if the cursor at offset is valid, or -1 if it isn't.
388      */
389     public static final int CURSOR_AT = 4;
390 
391     /**
392      * Maximum cursor option value.
393      */
394     private static final int CURSOR_OPT_MAX_VALUE = CURSOR_AT;
395 
396     /** @hide */
397     @IntDef(prefix = { "START_HYPHEN_EDIT_" }, value = {
398         START_HYPHEN_EDIT_NO_EDIT,
399         START_HYPHEN_EDIT_INSERT_HYPHEN,
400         START_HYPHEN_EDIT_INSERT_ZWJ
401     })
402     @Retention(RetentionPolicy.SOURCE)
403     public @interface StartHyphenEdit {}
404 
405     /**
406      * An integer representing the starting of the line has no modification for hyphenation.
407      */
408     public static final int START_HYPHEN_EDIT_NO_EDIT = 0x00;
409 
410     /**
411      * An integer representing the starting of the line has normal hyphen character (U+002D).
412      */
413     public static final int START_HYPHEN_EDIT_INSERT_HYPHEN = 0x01;
414 
415     /**
416      * An integer representing the starting of the line has Zero-Width-Joiner (U+200D).
417      */
418     public static final int START_HYPHEN_EDIT_INSERT_ZWJ = 0x02;
419 
420     /** @hide */
421     @IntDef(prefix = { "END_HYPHEN_EDIT_" }, value = {
422         END_HYPHEN_EDIT_NO_EDIT,
423         END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN,
424         END_HYPHEN_EDIT_INSERT_HYPHEN,
425         END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN,
426         END_HYPHEN_EDIT_INSERT_MAQAF,
427         END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN,
428         END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN
429     })
430     @Retention(RetentionPolicy.SOURCE)
431     public @interface EndHyphenEdit {}
432 
433     /**
434      * An integer representing the end of the line has no modification for hyphenation.
435      */
436     public static final int END_HYPHEN_EDIT_NO_EDIT = 0x00;
437 
438     /**
439      * An integer representing the character at the end of the line is replaced with hyphen
440      * character (U+002D).
441      */
442     public static final int END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN = 0x01;
443 
444     /**
445      * An integer representing the end of the line has normal hyphen character (U+002D).
446      */
447     public static final int END_HYPHEN_EDIT_INSERT_HYPHEN = 0x02;
448 
449     /**
450      * An integer representing the end of the line has Armentian hyphen (U+058A).
451      */
452     public static final int END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN = 0x03;
453 
454     /**
455      * An integer representing the end of the line has maqaf (Hebrew hyphen, U+05BE).
456      */
457     public static final int END_HYPHEN_EDIT_INSERT_MAQAF = 0x04;
458 
459     /**
460      * An integer representing the end of the line has Canadian Syllabics hyphen (U+1400).
461      */
462     public static final int END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN = 0x05;
463 
464     /**
465      * An integer representing the end of the line has Zero-Width-Joiner (U+200D) followed by normal
466      * hyphen character (U+002D).
467      */
468     public static final int END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN = 0x06;
469 
470     /**
471      * The Style specifies if the primitive being drawn is filled, stroked, or
472      * both (in the same color). The default is FILL.
473      */
474     public enum Style {
475         /**
476          * Geometry and text drawn with this style will be filled, ignoring all
477          * stroke-related settings in the paint.
478          */
479         FILL            (0),
480         /**
481          * Geometry and text drawn with this style will be stroked, respecting
482          * the stroke-related fields on the paint.
483          */
484         STROKE          (1),
485         /**
486          * Geometry and text drawn with this style will be both filled and
487          * stroked at the same time, respecting the stroke-related fields on
488          * the paint. This mode can give unexpected results if the geometry
489          * is oriented counter-clockwise. This restriction does not apply to
490          * either FILL or STROKE.
491          */
492         FILL_AND_STROKE (2);
493 
Style(int nativeInt)494         Style(int nativeInt) {
495             this.nativeInt = nativeInt;
496         }
497         final int nativeInt;
498     }
499 
500     /**
501      * The Cap specifies the treatment for the beginning and ending of
502      * stroked lines and paths. The default is BUTT.
503      */
504     public enum Cap {
505         /**
506          * The stroke ends with the path, and does not project beyond it.
507          */
508         BUTT    (0),
509         /**
510          * The stroke projects out as a semicircle, with the center at the
511          * end of the path.
512          */
513         ROUND   (1),
514         /**
515          * The stroke projects out as a square, with the center at the end
516          * of the path.
517          */
518         SQUARE  (2);
519 
Cap(int nativeInt)520         private Cap(int nativeInt) {
521             this.nativeInt = nativeInt;
522         }
523         final int nativeInt;
524     }
525 
526     /**
527      * The Join specifies the treatment where lines and curve segments
528      * join on a stroked path. The default is MITER.
529      */
530     public enum Join {
531         /**
532          * The outer edges of a join meet at a sharp angle
533          */
534         MITER   (0),
535         /**
536          * The outer edges of a join meet in a circular arc.
537          */
538         ROUND   (1),
539         /**
540          * The outer edges of a join meet with a straight line
541          */
542         BEVEL   (2);
543 
Join(int nativeInt)544         private Join(int nativeInt) {
545             this.nativeInt = nativeInt;
546         }
547         final int nativeInt;
548     }
549 
550     /**
551      * Align specifies how drawText aligns its text relative to the
552      * [x,y] coordinates. The default is LEFT.
553      */
554     public enum Align {
555         /**
556          * The text is drawn to the right of the x,y origin
557          */
558         LEFT    (0),
559         /**
560          * The text is drawn centered horizontally on the x,y origin
561          */
562         CENTER  (1),
563         /**
564          * The text is drawn to the left of the x,y origin
565          */
566         RIGHT   (2);
567 
Align(int nativeInt)568         private Align(int nativeInt) {
569             this.nativeInt = nativeInt;
570         }
571         final int nativeInt;
572     }
573 
574     /**
575      * Create a new paint with default settings.
576      *
577      * <p>On devices running {@link Build.VERSION_CODES#O} and below, hardware
578      * accelerated drawing always acts as if {@link #FILTER_BITMAP_FLAG} is set.
579      * On devices running {@link Build.VERSION_CODES#Q} and above,
580      * {@code FILTER_BITMAP_FLAG} is set by this constructor, and it can be
581      * cleared with {@link #setFlags} or {@link #setFilterBitmap}.
582      * On devices running {@link Build.VERSION_CODES#S} and above, {@code ANTI_ALIAS_FLAG}
583      * is set by this constructor, and it can be cleared with {@link #setFlags} or
584      * {@link #setAntiAlias}.</p>
585      */
Paint()586     public Paint() {
587         this(ANTI_ALIAS_FLAG);
588     }
589 
590     /**
591      * Create a new paint with the specified flags. Use setFlags() to change
592      * these after the paint is created.
593      *
594      * <p>On devices running {@link Build.VERSION_CODES#O} and below, hardware
595      * accelerated drawing always acts as if {@link #FILTER_BITMAP_FLAG} is set.
596      * On devices running {@link Build.VERSION_CODES#Q} and above,
597      * {@code FILTER_BITMAP_FLAG} is always set by this constructor, regardless
598      * of the value of {@code flags}. It can be cleared with {@link #setFlags} or
599      * {@link #setFilterBitmap}.</p>
600      *
601      * @param flags initial flag bits, as if they were passed via setFlags().
602      */
Paint(int flags)603     public Paint(int flags) {
604         mNativePaint = nInit();
605         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePaint);
606         setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
607         // TODO: Turning off hinting has undesirable side effects, we need to
608         //       revisit hinting once we add support for subpixel positioning
609         // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
610         //        ? HINTING_OFF : HINTING_ON);
611         mCompatScaling = mInvCompatScaling = 1;
612         setTextLocales(LocaleList.getAdjustedDefault());
613         mColor = Color.pack(Color.BLACK);
614     }
615 
616     /**
617      * Create a new paint, initialized with the attributes in the specified
618      * paint parameter.
619      *
620      * @param paint Existing paint used to initialized the attributes of the
621      *              new paint.
622      */
Paint(Paint paint)623     public Paint(Paint paint) {
624         mNativePaint = nInitWithPaint(paint.getNativeInstance());
625         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePaint);
626         setClassVariablesFrom(paint);
627     }
628 
629     /** Restores the paint to its default settings. */
reset()630     public void reset() {
631         nReset(mNativePaint);
632         setFlags(HIDDEN_DEFAULT_PAINT_FLAGS | ANTI_ALIAS_FLAG);
633 
634         // TODO: Turning off hinting has undesirable side effects, we need to
635         //       revisit hinting once we add support for subpixel positioning
636         // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
637         //        ? HINTING_OFF : HINTING_ON);
638 
639         mColor = Color.pack(Color.BLACK);
640         mColorFilter = null;
641         mMaskFilter = null;
642         mPathEffect = null;
643         mShader = null;
644         mNativeShader = 0;
645         mTypeface = null;
646         mXfermode = null;
647 
648         mHasCompatScaling = false;
649         mCompatScaling = 1;
650         mInvCompatScaling = 1;
651 
652         mBidiFlags = BIDI_DEFAULT_LTR;
653         setTextLocales(LocaleList.getAdjustedDefault());
654         setElegantTextHeight(false);
655         mFontFeatureSettings = null;
656         mFontVariationSettings = null;
657 
658         mShadowLayerRadius = 0.0f;
659         mShadowLayerDx = 0.0f;
660         mShadowLayerDy = 0.0f;
661         mShadowLayerColor = Color.pack(0);
662     }
663 
664     /**
665      * Copy the fields from src into this paint. This is equivalent to calling
666      * get() on all of the src fields, and calling the corresponding set()
667      * methods on this.
668      */
set(Paint src)669     public void set(Paint src) {
670         if (this != src) {
671             // copy over the native settings
672             nSet(mNativePaint, src.mNativePaint);
673             setClassVariablesFrom(src);
674         }
675     }
676 
677     /**
678      * Set all class variables using current values from the given
679      * {@link Paint}.
680      */
setClassVariablesFrom(Paint paint)681     private void setClassVariablesFrom(Paint paint) {
682         mColor = paint.mColor;
683         mColorFilter = paint.mColorFilter;
684         mMaskFilter = paint.mMaskFilter;
685         mPathEffect = paint.mPathEffect;
686         mShader = paint.mShader;
687         mNativeShader = paint.mNativeShader;
688         mTypeface = paint.mTypeface;
689         mXfermode = paint.mXfermode;
690 
691         mHasCompatScaling = paint.mHasCompatScaling;
692         mCompatScaling = paint.mCompatScaling;
693         mInvCompatScaling = paint.mInvCompatScaling;
694 
695         mBidiFlags = paint.mBidiFlags;
696         mLocales = paint.mLocales;
697         mFontFeatureSettings = paint.mFontFeatureSettings;
698         mFontVariationSettings = paint.mFontVariationSettings;
699 
700         mShadowLayerRadius = paint.mShadowLayerRadius;
701         mShadowLayerDx = paint.mShadowLayerDx;
702         mShadowLayerDy = paint.mShadowLayerDy;
703         mShadowLayerColor = paint.mShadowLayerColor;
704     }
705 
706     /** @hide */
707     @UnsupportedAppUsage
setCompatibilityScaling(float factor)708     public void setCompatibilityScaling(float factor) {
709         if (factor == 1.0) {
710             mHasCompatScaling = false;
711             mCompatScaling = mInvCompatScaling = 1.0f;
712         } else {
713             mHasCompatScaling = true;
714             mCompatScaling = factor;
715             mInvCompatScaling = 1.0f/factor;
716         }
717     }
718 
719     /**
720      * Return the pointer to the native object while ensuring that any
721      * mutable objects that are attached to the paint are also up-to-date.
722      *
723      * Note: Although this method is |synchronized|, this is simply so it
724      * is not thread-hostile to multiple threads calling this method. It
725      * is still unsafe to attempt to change the Shader/ColorFilter while
726      * another thread attempts to access the native object.
727      *
728      * @hide
729      */
730     @UnsupportedAppUsage
getNativeInstance()731     public synchronized long getNativeInstance() {
732         boolean filter = isFilterBitmap();
733         long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance(filter);
734         if (newNativeShader != mNativeShader) {
735             mNativeShader = newNativeShader;
736             nSetShader(mNativePaint, mNativeShader);
737         }
738         long newNativeColorFilter = mColorFilter == null ? 0 : mColorFilter.getNativeInstance();
739         if (newNativeColorFilter != mNativeColorFilter) {
740             mNativeColorFilter = newNativeColorFilter;
741             nSetColorFilter(mNativePaint, mNativeColorFilter);
742         }
743         return mNativePaint;
744     }
745 
746     /**
747      * Return the bidi flags on the paint.
748      *
749      * @return the bidi flags on the paint
750      * @hide
751      */
getBidiFlags()752     public int getBidiFlags() {
753         return mBidiFlags;
754     }
755 
756     /**
757      * Set the bidi flags on the paint.
758      * @hide
759      */
setBidiFlags(int flags)760     public void setBidiFlags(int flags) {
761         // only flag value is the 3-bit BIDI control setting
762         flags &= BIDI_FLAG_MASK;
763         if (flags > BIDI_MAX_FLAG_VALUE) {
764             throw new IllegalArgumentException("unknown bidi flag: " + flags);
765         }
766         mBidiFlags = flags;
767     }
768 
769     /**
770      * Return the paint's flags. Use the Flag enum to test flag values.
771      *
772      * @return the paint's flags (see enums ending in _Flag for bit masks)
773      */
getFlags()774     public @PaintFlag int getFlags() {
775         return nGetFlags(mNativePaint);
776     }
777 
778     /**
779      * Set the paint's flags. Use the Flag enum to specific flag values.
780      *
781      * @param flags The new flag bits for the paint
782      */
setFlags(@aintFlag int flags)783     public void setFlags(@PaintFlag int flags) {
784         nSetFlags(mNativePaint, flags);
785     }
786 
787     /**
788      * Return the paint's hinting mode.  Returns either
789      * {@link #HINTING_OFF} or {@link #HINTING_ON}.
790      */
getHinting()791     public int getHinting() {
792         return nGetHinting(mNativePaint);
793     }
794 
795     /**
796      * Set the paint's hinting mode.  May be either
797      * {@link #HINTING_OFF} or {@link #HINTING_ON}.
798      */
setHinting(int mode)799     public void setHinting(int mode) {
800         nSetHinting(mNativePaint, mode);
801     }
802 
803     /**
804      * Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set
805      * AntiAliasing smooths out the edges of what is being drawn, but is has
806      * no impact on the interior of the shape. See setDither() and
807      * setFilterBitmap() to affect how colors are treated.
808      *
809      * @return true if the antialias bit is set in the paint's flags.
810      */
isAntiAlias()811     public final boolean isAntiAlias() {
812         return (getFlags() & ANTI_ALIAS_FLAG) != 0;
813     }
814 
815     /**
816      * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit
817      * AntiAliasing smooths out the edges of what is being drawn, but is has
818      * no impact on the interior of the shape. See setDither() and
819      * setFilterBitmap() to affect how colors are treated.
820      *
821      * @param aa true to set the antialias bit in the flags, false to clear it
822      */
setAntiAlias(boolean aa)823     public void setAntiAlias(boolean aa) {
824         nSetAntiAlias(mNativePaint, aa);
825     }
826 
827     /**
828      * Helper for getFlags(), returning true if DITHER_FLAG bit is set
829      * Dithering affects how colors that are higher precision than the device
830      * are down-sampled. No dithering is generally faster, but higher precision
831      * colors are just truncated down (e.g. 8888 -> 565). Dithering tries to
832      * distribute the error inherent in this process, to reduce the visual
833      * artifacts.
834      *
835      * @return true if the dithering bit is set in the paint's flags.
836      */
isDither()837     public final boolean isDither() {
838         return (getFlags() & DITHER_FLAG) != 0;
839     }
840 
841     /**
842      * Helper for setFlags(), setting or clearing the DITHER_FLAG bit
843      * Dithering affects how colors that are higher precision than the device
844      * are down-sampled. No dithering is generally faster, but higher precision
845      * colors are just truncated down (e.g. 8888 -> 565). Dithering tries to
846      * distribute the error inherent in this process, to reduce the visual
847      * artifacts.
848      *
849      * @param dither true to set the dithering bit in flags, false to clear it
850      */
setDither(boolean dither)851     public void setDither(boolean dither) {
852         nSetDither(mNativePaint, dither);
853     }
854 
855     /**
856      * Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set
857      *
858      * @return true if the lineartext bit is set in the paint's flags
859      */
isLinearText()860     public final boolean isLinearText() {
861         return (getFlags() & LINEAR_TEXT_FLAG) != 0;
862     }
863 
864     /**
865      * Helper for setFlags(), setting or clearing the LINEAR_TEXT_FLAG bit
866      *
867      * @param linearText true to set the linearText bit in the paint's flags,
868      *                   false to clear it.
869      */
setLinearText(boolean linearText)870     public void setLinearText(boolean linearText) {
871         nSetLinearText(mNativePaint, linearText);
872     }
873 
874     /**
875      * Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set
876      *
877      * @return true if the subpixel bit is set in the paint's flags
878      */
isSubpixelText()879     public final boolean isSubpixelText() {
880         return (getFlags() & SUBPIXEL_TEXT_FLAG) != 0;
881     }
882 
883     /**
884      * Helper for setFlags(), setting or clearing the SUBPIXEL_TEXT_FLAG bit
885      *
886      * @param subpixelText true to set the subpixelText bit in the paint's
887      *                     flags, false to clear it.
888      */
setSubpixelText(boolean subpixelText)889     public void setSubpixelText(boolean subpixelText) {
890         nSetSubpixelText(mNativePaint, subpixelText);
891     }
892 
893     /**
894      * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
895      *
896      * @return true if the underlineText bit is set in the paint's flags.
897      * @see #getUnderlinePosition()
898      * @see #getUnderlineThickness()
899      * @see #setUnderlineText(boolean)
900      */
isUnderlineText()901     public final boolean isUnderlineText() {
902         return (getFlags() & UNDERLINE_TEXT_FLAG) != 0;
903     }
904 
905     /**
906      * Returns the distance from top of the underline to the baseline in pixels.
907      *
908      * The result is positive for positions that are below the baseline.
909      * This method returns where the underline should be drawn independent of if the {@link
910      * #UNDERLINE_TEXT_FLAG} bit is set.
911      *
912      * @return the position of the underline in pixels
913      * @see #isUnderlineText()
914      * @see #getUnderlineThickness()
915      * @see #setUnderlineText(boolean)
916      */
getUnderlinePosition()917     public @Px float getUnderlinePosition() {
918         return nGetUnderlinePosition(mNativePaint);
919     }
920 
921     /**
922      * Returns the thickness of the underline in pixels.
923      *
924      * @return the thickness of the underline in pixels
925      * @see #isUnderlineText()
926      * @see #getUnderlinePosition()
927      * @see #setUnderlineText(boolean)
928      */
getUnderlineThickness()929     public @Px float getUnderlineThickness() {
930         return nGetUnderlineThickness(mNativePaint);
931     }
932 
933     /**
934      * Helper for setFlags(), setting or clearing the UNDERLINE_TEXT_FLAG bit
935      *
936      * @param underlineText true to set the underlineText bit in the paint's
937      *                      flags, false to clear it.
938      * @see #isUnderlineText()
939      * @see #getUnderlinePosition()
940      * @see #getUnderlineThickness()
941      */
setUnderlineText(boolean underlineText)942     public void setUnderlineText(boolean underlineText) {
943         nSetUnderlineText(mNativePaint, underlineText);
944     }
945 
946     /**
947      * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
948      *
949      * @return true if the {@link #STRIKE_THRU_TEXT_FLAG} bit is set in the paint's flags.
950      * @see #getStrikeThruPosition()
951      * @see #getStrikeThruThickness()
952      * @see #setStrikeThruText(boolean)
953      */
isStrikeThruText()954     public final boolean isStrikeThruText() {
955         return (getFlags() & STRIKE_THRU_TEXT_FLAG) != 0;
956     }
957 
958     /**
959      * Distance from top of the strike-through line to the baseline in pixels.
960      *
961      * The result is negative for positions that are above the baseline.
962      * This method returns where the strike-through line should be drawn independent of if the
963      * {@link #STRIKE_THRU_TEXT_FLAG} bit is set.
964      *
965      * @return the position of the strike-through line in pixels
966      * @see #getStrikeThruThickness()
967      * @see #setStrikeThruText(boolean)
968      * @see #isStrikeThruText()
969      */
getStrikeThruPosition()970     public @Px float getStrikeThruPosition() {
971         return nGetStrikeThruPosition(mNativePaint);
972     }
973 
974     /**
975      * Returns the thickness of the strike-through line in pixels.
976      *
977      * @return the position of the strike-through line in pixels
978      * @see #getStrikeThruPosition()
979      * @see #setStrikeThruText(boolean)
980      * @see #isStrikeThruText()
981      */
getStrikeThruThickness()982     public @Px float getStrikeThruThickness() {
983         return nGetStrikeThruThickness(mNativePaint);
984     }
985 
986     /**
987      * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit
988      *
989      * @param strikeThruText true to set the strikeThruText bit in the paint's
990      *                       flags, false to clear it.
991      * @see #getStrikeThruPosition()
992      * @see #getStrikeThruThickness()
993      * @see #isStrikeThruText()
994      */
setStrikeThruText(boolean strikeThruText)995     public void setStrikeThruText(boolean strikeThruText) {
996         nSetStrikeThruText(mNativePaint, strikeThruText);
997     }
998 
999     /**
1000      * Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set
1001      *
1002      * @return true if the fakeBoldText bit is set in the paint's flags.
1003      */
isFakeBoldText()1004     public final boolean isFakeBoldText() {
1005         return (getFlags() & FAKE_BOLD_TEXT_FLAG) != 0;
1006     }
1007 
1008     /**
1009      * Helper for setFlags(), setting or clearing the FAKE_BOLD_TEXT_FLAG bit
1010      *
1011      * @param fakeBoldText true to set the fakeBoldText bit in the paint's
1012      *                     flags, false to clear it.
1013      */
setFakeBoldText(boolean fakeBoldText)1014     public void setFakeBoldText(boolean fakeBoldText) {
1015         nSetFakeBoldText(mNativePaint, fakeBoldText);
1016     }
1017 
1018     /**
1019      * Whether or not the bitmap filter is activated.
1020      * Filtering affects the sampling of bitmaps when they are transformed.
1021      * Filtering does not affect how the colors in the bitmap are converted into
1022      * device pixels. That is dependent on dithering and xfermodes.
1023      *
1024      * @see #setFilterBitmap(boolean) setFilterBitmap()
1025      * @see #FILTER_BITMAP_FLAG
1026      */
isFilterBitmap()1027     public final boolean isFilterBitmap() {
1028         return (getFlags() & FILTER_BITMAP_FLAG) != 0;
1029     }
1030 
1031     /**
1032      * Helper for setFlags(), setting or clearing the FILTER_BITMAP_FLAG bit.
1033      * Filtering affects the sampling of bitmaps when they are transformed.
1034      * Filtering does not affect how the colors in the bitmap are converted into
1035      * device pixels. That is dependent on dithering and xfermodes.
1036      *
1037      * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's
1038      *               flags, false to clear it.
1039      * @see #FILTER_BITMAP_FLAG
1040      */
setFilterBitmap(boolean filter)1041     public void setFilterBitmap(boolean filter) {
1042         nSetFilterBitmap(mNativePaint, filter);
1043     }
1044 
1045     /**
1046      * Return the paint's style, used for controlling how primitives'
1047      * geometries are interpreted (except for drawBitmap, which always assumes
1048      * FILL_STYLE).
1049      *
1050      * @return the paint's style setting (Fill, Stroke, StrokeAndFill)
1051      */
getStyle()1052     public Style getStyle() {
1053         return sStyleArray[nGetStyle(mNativePaint)];
1054     }
1055 
1056     /**
1057      * Set the paint's style, used for controlling how primitives'
1058      * geometries are interpreted (except for drawBitmap, which always assumes
1059      * Fill).
1060      *
1061      * @param style The new style to set in the paint
1062      */
setStyle(Style style)1063     public void setStyle(Style style) {
1064         nSetStyle(mNativePaint, style.nativeInt);
1065     }
1066 
1067     /**
1068      * Return the paint's color in sRGB. Note that the color is a 32bit value
1069      * containing alpha as well as r,g,b. This 32bit value is not premultiplied,
1070      * meaning that its alpha can be any value, regardless of the values of
1071      * r,g,b. See the Color class for more details.
1072      *
1073      * @return the paint's color (and alpha).
1074      */
1075     @ColorInt
getColor()1076     public int getColor() {
1077         return Color.toArgb(mColor);
1078     }
1079 
1080     /**
1081      * Return the paint's color. Note that the color is a long with an encoded
1082      * {@link ColorSpace} as well as alpha and r,g,b. These values are not
1083      * premultiplied, meaning that alpha can be any value, regardless of the
1084      * values of r,g,b. See the {@link Color} class for more details.
1085      *
1086      * @return the paint's color, alpha, and {@code ColorSpace} encoded as a
1087      *      {@code ColorLong}
1088      */
1089     @ColorLong
getColorLong()1090     public long getColorLong() {
1091         return mColor;
1092     }
1093 
1094     /**
1095      * Set the paint's color. Note that the color is an int containing alpha
1096      * as well as r,g,b. This 32bit value is not premultiplied, meaning that
1097      * its alpha can be any value, regardless of the values of r,g,b.
1098      * See the Color class for more details.
1099      *
1100      * @param color The new color (including alpha) to set in the paint.
1101      */
setColor(@olorInt int color)1102     public void setColor(@ColorInt int color) {
1103         nSetColor(mNativePaint, color);
1104         mColor = Color.pack(color);
1105     }
1106 
1107     /**
1108      * Set the paint's color with a {@code ColorLong}. Note that the color is
1109      * a long with an encoded {@link ColorSpace} as well as alpha and r,g,b.
1110      * These values are not premultiplied, meaning that alpha can be any value,
1111      * regardless of the values of r,g,b. See the {@link Color} class for more
1112      * details.
1113      *
1114      * @param color The new color (including alpha and {@link ColorSpace})
1115      *      to set in the paint.
1116      * @throws IllegalArgumentException if the color space encoded in the
1117      *      {@code ColorLong} is invalid or unknown.
1118      */
setColor(@olorLong long color)1119     public void setColor(@ColorLong long color) {
1120         ColorSpace cs = Color.colorSpace(color);
1121 
1122         nSetColor(mNativePaint, cs.getNativeInstance(), color);
1123         mColor = color;
1124     }
1125 
1126     /**
1127      * Helper to getColor() that just returns the color's alpha value. This is
1128      * the same as calling getColor() >>> 24. It always returns a value between
1129      * 0 (completely transparent) and 255 (completely opaque).
1130      *
1131      * @return the alpha component of the paint's color.
1132      */
getAlpha()1133     public int getAlpha() {
1134         return Math.round(Color.alpha(mColor) * 255.0f);
1135     }
1136 
1137     /**
1138      * Helper to setColor(), that only assigns the color's alpha value,
1139      * leaving its r,g,b values unchanged. Results are undefined if the alpha
1140      * value is outside of the range [0..255]
1141      *
1142      * @param a set the alpha component [0..255] of the paint's color.
1143      */
setAlpha(int a)1144     public void setAlpha(int a) {
1145         // FIXME: No need to unpack this. Instead, just update the alpha bits.
1146         // b/122959599
1147         ColorSpace cs = Color.colorSpace(mColor);
1148         float r = Color.red(mColor);
1149         float g = Color.green(mColor);
1150         float b = Color.blue(mColor);
1151         mColor = Color.pack(r, g, b, a * (1.0f / 255), cs);
1152         nSetAlpha(mNativePaint, a);
1153     }
1154 
1155     /**
1156      * Helper to setColor(), that takes a,r,g,b and constructs the color int
1157      *
1158      * @param a The new alpha component (0..255) of the paint's color.
1159      * @param r The new red component (0..255) of the paint's color.
1160      * @param g The new green component (0..255) of the paint's color.
1161      * @param b The new blue component (0..255) of the paint's color.
1162      */
setARGB(int a, int r, int g, int b)1163     public void setARGB(int a, int r, int g, int b) {
1164         setColor((a << 24) | (r << 16) | (g << 8) | b);
1165     }
1166 
1167     /**
1168      * Return the width for stroking.
1169      * <p />
1170      * A value of 0 strokes in hairline mode.
1171      * Hairlines always draws a single pixel independent of the canvas's matrix.
1172      *
1173      * @return the paint's stroke width, used whenever the paint's style is
1174      *         Stroke or StrokeAndFill.
1175      */
getStrokeWidth()1176     public float getStrokeWidth() {
1177         return nGetStrokeWidth(mNativePaint);
1178     }
1179 
1180     /**
1181      * Set the width for stroking.
1182      * Pass 0 to stroke in hairline mode.
1183      * Hairlines always draws a single pixel independent of the canvas's matrix.
1184      *
1185      * @param width set the paint's stroke width, used whenever the paint's
1186      *              style is Stroke or StrokeAndFill.
1187      */
setStrokeWidth(float width)1188     public void setStrokeWidth(float width) {
1189         nSetStrokeWidth(mNativePaint, width);
1190     }
1191 
1192     /**
1193      * Return the paint's stroke miter value. Used to control the behavior
1194      * of miter joins when the joins angle is sharp.
1195      *
1196      * @return the paint's miter limit, used whenever the paint's style is
1197      *         Stroke or StrokeAndFill.
1198      */
getStrokeMiter()1199     public float getStrokeMiter() {
1200         return nGetStrokeMiter(mNativePaint);
1201     }
1202 
1203     /**
1204      * Set the paint's stroke miter value. This is used to control the behavior
1205      * of miter joins when the joins angle is sharp. This value must be >= 0.
1206      *
1207      * @param miter set the miter limit on the paint, used whenever the paint's
1208      *              style is Stroke or StrokeAndFill.
1209      */
setStrokeMiter(float miter)1210     public void setStrokeMiter(float miter) {
1211         nSetStrokeMiter(mNativePaint, miter);
1212     }
1213 
1214     /**
1215      * Return the paint's Cap, controlling how the start and end of stroked
1216      * lines and paths are treated.
1217      *
1218      * @return the line cap style for the paint, used whenever the paint's
1219      *         style is Stroke or StrokeAndFill.
1220      */
getStrokeCap()1221     public Cap getStrokeCap() {
1222         return sCapArray[nGetStrokeCap(mNativePaint)];
1223     }
1224 
1225     /**
1226      * Set the paint's Cap.
1227      *
1228      * @param cap set the paint's line cap style, used whenever the paint's
1229      *            style is Stroke or StrokeAndFill.
1230      */
setStrokeCap(Cap cap)1231     public void setStrokeCap(Cap cap) {
1232         nSetStrokeCap(mNativePaint, cap.nativeInt);
1233     }
1234 
1235     /**
1236      * Return the paint's stroke join type.
1237      *
1238      * @return the paint's Join.
1239      */
getStrokeJoin()1240     public Join getStrokeJoin() {
1241         return sJoinArray[nGetStrokeJoin(mNativePaint)];
1242     }
1243 
1244     /**
1245      * Set the paint's Join.
1246      *
1247      * @param join set the paint's Join, used whenever the paint's style is
1248      *             Stroke or StrokeAndFill.
1249      */
setStrokeJoin(Join join)1250     public void setStrokeJoin(Join join) {
1251         nSetStrokeJoin(mNativePaint, join.nativeInt);
1252     }
1253 
1254     /**
1255      * Applies any/all effects (patheffect, stroking) to src, returning the
1256      * result in dst. The result is that drawing src with this paint will be
1257      * the same as drawing dst with a default paint (at least from the
1258      * geometric perspective).
1259      *
1260      * @param src input path
1261      * @param dst output path (may be the same as src)
1262      * @return    true if the path should be filled, or false if it should be
1263      *                 drawn with a hairline (width == 0)
1264      */
getFillPath(Path src, Path dst)1265     public boolean getFillPath(Path src, Path dst) {
1266         return nGetFillPath(mNativePaint, src.readOnlyNI(), dst.mutateNI());
1267     }
1268 
1269     /**
1270      * Get the paint's shader object.
1271      *
1272      * @return the paint's shader (or null)
1273      */
getShader()1274     public Shader getShader() {
1275         return mShader;
1276     }
1277 
1278     /**
1279      * Set or clear the shader object.
1280      * <p />
1281      * Pass null to clear any previous shader.
1282      * As a convenience, the parameter passed is also returned.
1283      *
1284      * @param shader May be null. the new shader to be installed in the paint
1285      * @return       shader
1286      */
setShader(Shader shader)1287     public Shader setShader(Shader shader) {
1288         // If mShader changes, cached value of native shader aren't valid, since
1289         // old shader's pointer may be reused by another shader allocation later
1290         if (mShader != shader) {
1291             mNativeShader = -1;
1292             // Release any native references to the old shader content
1293             nSetShader(mNativePaint, 0);
1294         }
1295         // Defer setting the shader natively until getNativeInstance() is called
1296         mShader = shader;
1297         return shader;
1298     }
1299 
1300     /**
1301      * Get the paint's colorfilter (maybe be null).
1302      *
1303      * @return the paint's colorfilter (maybe be null)
1304      */
getColorFilter()1305     public ColorFilter getColorFilter() {
1306         return mColorFilter;
1307     }
1308 
1309     /**
1310      * Set or clear the paint's colorfilter, returning the parameter.
1311      *
1312      * @param filter May be null. The new filter to be installed in the paint
1313      * @return       filter
1314      */
setColorFilter(ColorFilter filter)1315     public ColorFilter setColorFilter(ColorFilter filter) {
1316         // If mColorFilter changes, cached value of native shader aren't valid, since
1317         // old shader's pointer may be reused by another shader allocation later
1318         if (mColorFilter != filter) {
1319             mNativeColorFilter = -1;
1320         }
1321 
1322         // Defer setting the filter natively until getNativeInstance() is called
1323         mColorFilter = filter;
1324         return filter;
1325     }
1326 
1327     /**
1328      * Get the paint's transfer mode object.
1329      *
1330      * @return the paint's transfer mode (or null)
1331      */
getXfermode()1332     public Xfermode getXfermode() {
1333         return mXfermode;
1334     }
1335 
1336     /**
1337      * Get the paint's blend mode object.
1338      *
1339      * @return the paint's blend mode (or null)
1340      */
1341     @Nullable
getBlendMode()1342     public BlendMode getBlendMode() {
1343         if (mXfermode == null) {
1344             return null;
1345         } else {
1346             return BlendMode.fromValue(mXfermode.porterDuffMode);
1347         }
1348     }
1349 
1350     /**
1351      * Set or clear the transfer mode object. A transfer mode defines how
1352      * source pixels (generate by a drawing command) are composited with
1353      * the destination pixels (content of the render target).
1354      * <p />
1355      * Pass null to clear any previous transfer mode.
1356      * As a convenience, the parameter passed is also returned.
1357      * <p />
1358      * {@link PorterDuffXfermode} is the most common transfer mode.
1359      *
1360      * @param xfermode May be null. The xfermode to be installed in the paint
1361      * @return         xfermode
1362      */
setXfermode(Xfermode xfermode)1363     public Xfermode setXfermode(Xfermode xfermode) {
1364         return installXfermode(xfermode);
1365     }
1366 
1367     @Nullable
installXfermode(Xfermode xfermode)1368     private Xfermode installXfermode(Xfermode xfermode) {
1369         int newMode = xfermode != null ? xfermode.porterDuffMode : Xfermode.DEFAULT;
1370         int curMode = mXfermode != null ? mXfermode.porterDuffMode : Xfermode.DEFAULT;
1371         if (newMode != curMode) {
1372             nSetXfermode(mNativePaint, newMode);
1373         }
1374         mXfermode = xfermode;
1375         return xfermode;
1376     }
1377 
1378     /**
1379      * Set or clear the blend mode. A blend mode defines how source pixels
1380      * (generated by a drawing command) are composited with the destination pixels
1381      * (content of the render target).
1382      * <p />
1383      * Pass null to clear any previous blend mode.
1384      * <p />
1385      *
1386      * @see BlendMode
1387      *
1388      * @param blendmode May be null. The blend mode to be installed in the paint
1389      */
setBlendMode(@ullable BlendMode blendmode)1390     public void setBlendMode(@Nullable BlendMode blendmode) {
1391         installXfermode(blendmode != null ? blendmode.getXfermode() : null);
1392     }
1393 
1394     /**
1395      * Get the paint's patheffect object.
1396      *
1397      * @return the paint's patheffect (or null)
1398      */
getPathEffect()1399     public PathEffect getPathEffect() {
1400         return mPathEffect;
1401     }
1402 
1403     /**
1404      * Set or clear the patheffect object.
1405      * <p />
1406      * Pass null to clear any previous patheffect.
1407      * As a convenience, the parameter passed is also returned.
1408      *
1409      * @param effect May be null. The patheffect to be installed in the paint
1410      * @return       effect
1411      */
setPathEffect(PathEffect effect)1412     public PathEffect setPathEffect(PathEffect effect) {
1413         long effectNative = 0;
1414         if (effect != null) {
1415             effectNative = effect.native_instance;
1416         }
1417         nSetPathEffect(mNativePaint, effectNative);
1418         mPathEffect = effect;
1419         return effect;
1420     }
1421 
1422     /**
1423      * Get the paint's maskfilter object.
1424      *
1425      * @return the paint's maskfilter (or null)
1426      */
getMaskFilter()1427     public MaskFilter getMaskFilter() {
1428         return mMaskFilter;
1429     }
1430 
1431     /**
1432      * Set or clear the maskfilter object.
1433      * <p />
1434      * Pass null to clear any previous maskfilter.
1435      * As a convenience, the parameter passed is also returned.
1436      *
1437      * @param maskfilter May be null. The maskfilter to be installed in the
1438      *                   paint
1439      * @return           maskfilter
1440      */
setMaskFilter(MaskFilter maskfilter)1441     public MaskFilter setMaskFilter(MaskFilter maskfilter) {
1442         long maskfilterNative = 0;
1443         if (maskfilter != null) {
1444             maskfilterNative = maskfilter.native_instance;
1445         }
1446         nSetMaskFilter(mNativePaint, maskfilterNative);
1447         mMaskFilter = maskfilter;
1448         return maskfilter;
1449     }
1450 
1451     /**
1452      * Get the paint's typeface object.
1453      * <p />
1454      * The typeface object identifies which font to use when drawing or
1455      * measuring text.
1456      *
1457      * @return the paint's typeface (or null)
1458      */
getTypeface()1459     public Typeface getTypeface() {
1460         return mTypeface;
1461     }
1462 
1463     /**
1464      * Set or clear the typeface object.
1465      * <p />
1466      * Pass null to clear any previous typeface.
1467      * As a convenience, the parameter passed is also returned.
1468      *
1469      * @param typeface May be null. The typeface to be installed in the paint
1470      * @return         typeface
1471      */
setTypeface(Typeface typeface)1472     public Typeface setTypeface(Typeface typeface) {
1473         final long typefaceNative = typeface == null ? 0 : typeface.native_instance;
1474         nSetTypeface(mNativePaint, typefaceNative);
1475         mTypeface = typeface;
1476         return typeface;
1477     }
1478 
1479     /**
1480      * Get the paint's rasterizer (or null).
1481      * <p />
1482      * The raster controls/modifies how paths/text are turned into alpha masks.
1483      *
1484      * @return         the paint's rasterizer (or null)
1485      *
1486      * @deprecated Rasterizer is not supported by either the HW or PDF backends.
1487      * @removed
1488      */
1489     @Deprecated
getRasterizer()1490     public Rasterizer getRasterizer() {
1491         return null;
1492     }
1493 
1494     /**
1495      * Set or clear the rasterizer object.
1496      * <p />
1497      * Pass null to clear any previous rasterizer.
1498      * As a convenience, the parameter passed is also returned.
1499      *
1500      * @param rasterizer May be null. The new rasterizer to be installed in
1501      *                   the paint.
1502      * @return           rasterizer
1503      *
1504      * @deprecated Rasterizer is not supported by either the HW or PDF backends.
1505      * @removed
1506      */
1507     @Deprecated
setRasterizer(Rasterizer rasterizer)1508     public Rasterizer setRasterizer(Rasterizer rasterizer) {
1509         return rasterizer;
1510     }
1511 
1512     /**
1513      * This draws a shadow layer below the main layer, with the specified
1514      * offset and color, and blur radius. If radius is 0, then the shadow
1515      * layer is removed.
1516      * <p>
1517      * Can be used to create a blurred shadow underneath text. Support for use
1518      * with other drawing operations is constrained to the software rendering
1519      * pipeline.
1520      * <p>
1521      * The alpha of the shadow will be the paint's alpha if the shadow color is
1522      * opaque, or the alpha from the shadow color if not.
1523      */
setShadowLayer(float radius, float dx, float dy, @ColorInt int shadowColor)1524     public void setShadowLayer(float radius, float dx, float dy, @ColorInt int shadowColor) {
1525         setShadowLayer(radius, dx, dy, Color.pack(shadowColor));
1526     }
1527 
1528     /**
1529      * This draws a shadow layer below the main layer, with the specified
1530      * offset and color, and blur radius. If radius is 0, then the shadow
1531      * layer is removed.
1532      * <p>
1533      * Can be used to create a blurred shadow underneath text. Support for use
1534      * with other drawing operations is constrained to the software rendering
1535      * pipeline.
1536      * <p>
1537      * The alpha of the shadow will be the paint's alpha if the shadow color is
1538      * opaque, or the alpha from the shadow color if not.
1539      *
1540      * @throws IllegalArgumentException if the color space encoded in the
1541      *      {@code ColorLong} is invalid or unknown.
1542      */
setShadowLayer(float radius, float dx, float dy, @ColorLong long shadowColor)1543     public void setShadowLayer(float radius, float dx, float dy, @ColorLong long shadowColor) {
1544         ColorSpace cs = Color.colorSpace(shadowColor);
1545         nSetShadowLayer(mNativePaint, radius, dx, dy, cs.getNativeInstance(), shadowColor);
1546 
1547         mShadowLayerRadius = radius;
1548         mShadowLayerDx = dx;
1549         mShadowLayerDy = dy;
1550         mShadowLayerColor = shadowColor;
1551     }
1552 
1553     /**
1554      * Clear the shadow layer.
1555      */
clearShadowLayer()1556     public void clearShadowLayer() {
1557         setShadowLayer(0, 0, 0, 0);
1558     }
1559 
1560     /**
1561      * Checks if the paint has a shadow layer attached
1562      *
1563      * @return true if the paint has a shadow layer attached and false otherwise
1564      * @hide
1565      */
hasShadowLayer()1566     public boolean hasShadowLayer() {
1567         return nHasShadowLayer(mNativePaint);
1568     }
1569 
1570     /**
1571      * Returns the blur radius of the shadow layer.
1572      * @see #setShadowLayer(float,float,float,int)
1573      * @see #setShadowLayer(float,float,float,long)
1574      */
getShadowLayerRadius()1575     public float getShadowLayerRadius() {
1576         return mShadowLayerRadius;
1577     }
1578 
1579     /**
1580      * Returns the x offset of the shadow layer.
1581      * @see #setShadowLayer(float,float,float,int)
1582      * @see #setShadowLayer(float,float,float,long)
1583      */
getShadowLayerDx()1584     public float getShadowLayerDx() {
1585         return mShadowLayerDx;
1586     }
1587 
1588     /**
1589      * Returns the y offset of the shadow layer.
1590      * @see #setShadowLayer(float,float,float,int)
1591      * @see #setShadowLayer(float,float,float,long)
1592      */
getShadowLayerDy()1593     public float getShadowLayerDy() {
1594         return mShadowLayerDy;
1595     }
1596 
1597     /**
1598      * Returns the color of the shadow layer.
1599      * @see #setShadowLayer(float,float,float,int)
1600      * @see #setShadowLayer(float,float,float,long)
1601      */
getShadowLayerColor()1602     public @ColorInt int getShadowLayerColor() {
1603         return Color.toArgb(mShadowLayerColor);
1604     }
1605 
1606     /**
1607      * Returns the color of the shadow layer.
1608      *
1609      * @return the shadow layer's color encoded as a {@link ColorLong}.
1610      * @see #setShadowLayer(float,float,float,int)
1611      * @see #setShadowLayer(float,float,float,long)
1612      * @see Color
1613      */
getShadowLayerColorLong()1614     public @ColorLong long getShadowLayerColorLong() {
1615         return mShadowLayerColor;
1616     }
1617 
1618     /**
1619      * Return the paint's Align value for drawing text. This controls how the
1620      * text is positioned relative to its origin. LEFT align means that all of
1621      * the text will be drawn to the right of its origin (i.e. the origin
1622      * specifies the LEFT edge of the text) and so on.
1623      *
1624      * @return the paint's Align value for drawing text.
1625      */
getTextAlign()1626     public Align getTextAlign() {
1627         return sAlignArray[nGetTextAlign(mNativePaint)];
1628     }
1629 
1630     /**
1631      * Set the paint's text alignment. This controls how the
1632      * text is positioned relative to its origin. LEFT align means that all of
1633      * the text will be drawn to the right of its origin (i.e. the origin
1634      * specifies the LEFT edge of the text) and so on.
1635      *
1636      * @param align set the paint's Align value for drawing text.
1637      */
setTextAlign(Align align)1638     public void setTextAlign(Align align) {
1639         nSetTextAlign(mNativePaint, align.nativeInt);
1640     }
1641 
1642     /**
1643      * Get the text's primary Locale. Note that this is not all of the locale-related information
1644      * Paint has. Use {@link #getTextLocales()} to get the complete list.
1645      *
1646      * @return the paint's primary Locale used for drawing text, never null.
1647      */
1648     @NonNull
getTextLocale()1649     public Locale getTextLocale() {
1650         return mLocales.get(0);
1651     }
1652 
1653     /**
1654      * Get the text locale list.
1655      *
1656      * @return the paint's LocaleList used for drawing text, never null or empty.
1657      */
1658     @NonNull @Size(min=1)
getTextLocales()1659     public LocaleList getTextLocales() {
1660         return mLocales;
1661     }
1662 
1663     /**
1664      * Set the text locale list to a one-member list consisting of just the locale.
1665      *
1666      * See {@link #setTextLocales(LocaleList)} for how the locale list affects
1667      * the way the text is drawn for some languages.
1668      *
1669      * @param locale the paint's locale value for drawing text, must not be null.
1670      */
setTextLocale(@onNull Locale locale)1671     public void setTextLocale(@NonNull Locale locale) {
1672         if (locale == null) {
1673             throw new IllegalArgumentException("locale cannot be null");
1674         }
1675         if (mLocales != null && mLocales.size() == 1 && locale.equals(mLocales.get(0))) {
1676             return;
1677         }
1678         mLocales = new LocaleList(locale);
1679         syncTextLocalesWithMinikin();
1680     }
1681 
1682     /**
1683      * Set the text locale list.
1684      *
1685      * The text locale list affects how the text is drawn for some languages.
1686      *
1687      * For example, if the locale list contains {@link Locale#CHINESE} or {@link Locale#CHINA},
1688      * then the text renderer will prefer to draw text using a Chinese font. Likewise,
1689      * if the locale list contains {@link Locale#JAPANESE} or {@link Locale#JAPAN}, then the text
1690      * renderer will prefer to draw text using a Japanese font. If the locale list contains both,
1691      * the order those locales appear in the list is considered for deciding the font.
1692      *
1693      * This distinction is important because Chinese and Japanese text both use many
1694      * of the same Unicode code points but their appearance is subtly different for
1695      * each language.
1696      *
1697      * By default, the text locale list is initialized to a one-member list just containing the
1698      * system locales. This assumes that the text to be rendered will most likely be in the user's
1699      * preferred language.
1700      *
1701      * If the actual language or languages of the text is/are known, then they can be provided to
1702      * the text renderer using this method. The text renderer may attempt to guess the
1703      * language script based on the contents of the text to be drawn independent of
1704      * the text locale here. Specifying the text locales just helps it do a better
1705      * job in certain ambiguous cases.
1706      *
1707      * @param locales the paint's locale list for drawing text, must not be null or empty.
1708      */
setTextLocales(@onNull @izemin=1) LocaleList locales)1709     public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
1710         if (locales == null || locales.isEmpty()) {
1711             throw new IllegalArgumentException("locales cannot be null or empty");
1712         }
1713         if (locales.equals(mLocales)) return;
1714         mLocales = locales;
1715         syncTextLocalesWithMinikin();
1716     }
1717 
syncTextLocalesWithMinikin()1718     private void syncTextLocalesWithMinikin() {
1719         final String languageTags = mLocales.toLanguageTags();
1720         final Integer minikinLocaleListId;
1721         synchronized (sCacheLock) {
1722             minikinLocaleListId = sMinikinLocaleListIdCache.get(languageTags);
1723             if (minikinLocaleListId == null) {
1724                 final int newID = nSetTextLocales(mNativePaint, languageTags);
1725                 sMinikinLocaleListIdCache.put(languageTags, newID);
1726                 return;
1727             }
1728         }
1729         nSetTextLocalesByMinikinLocaleListId(mNativePaint, minikinLocaleListId.intValue());
1730     }
1731 
1732     /**
1733      * Get the elegant metrics flag.
1734      *
1735      * @return true if elegant metrics are enabled for text drawing.
1736      */
isElegantTextHeight()1737     public boolean isElegantTextHeight() {
1738         return nIsElegantTextHeight(mNativePaint);
1739     }
1740 
1741     /**
1742      * Set the paint's elegant height metrics flag. This setting selects font
1743      * variants that have not been compacted to fit Latin-based vertical
1744      * metrics, and also increases top and bottom bounds to provide more space.
1745      *
1746      * @param elegant set the paint's elegant metrics flag for drawing text.
1747      */
setElegantTextHeight(boolean elegant)1748     public void setElegantTextHeight(boolean elegant) {
1749         nSetElegantTextHeight(mNativePaint, elegant);
1750     }
1751 
1752     /**
1753      * Return the paint's text size.
1754      *
1755      * @return the paint's text size in pixel units.
1756      */
getTextSize()1757     public float getTextSize() {
1758         return nGetTextSize(mNativePaint);
1759     }
1760 
1761     /**
1762      * Set the paint's text size. This value must be > 0
1763      *
1764      * @param textSize set the paint's text size in pixel units.
1765      */
setTextSize(float textSize)1766     public void setTextSize(float textSize) {
1767         nSetTextSize(mNativePaint, textSize);
1768     }
1769 
1770     /**
1771      * Return the paint's horizontal scale factor for text. The default value
1772      * is 1.0.
1773      *
1774      * @return the paint's scale factor in X for drawing/measuring text
1775      */
getTextScaleX()1776     public float getTextScaleX() {
1777         return nGetTextScaleX(mNativePaint);
1778     }
1779 
1780     /**
1781      * Set the paint's horizontal scale factor for text. The default value
1782      * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
1783      * stretch the text narrower.
1784      *
1785      * @param scaleX set the paint's scale in X for drawing/measuring text.
1786      */
setTextScaleX(float scaleX)1787     public void setTextScaleX(float scaleX) {
1788         nSetTextScaleX(mNativePaint, scaleX);
1789     }
1790 
1791     /**
1792      * Return the paint's horizontal skew factor for text. The default value
1793      * is 0.
1794      *
1795      * @return         the paint's skew factor in X for drawing text.
1796      */
getTextSkewX()1797     public float getTextSkewX() {
1798         return nGetTextSkewX(mNativePaint);
1799     }
1800 
1801     /**
1802      * Set the paint's horizontal skew factor for text. The default value
1803      * is 0. For approximating oblique text, use values around -0.25.
1804      *
1805      * @param skewX set the paint's skew factor in X for drawing text.
1806      */
setTextSkewX(float skewX)1807     public void setTextSkewX(float skewX) {
1808         nSetTextSkewX(mNativePaint, skewX);
1809     }
1810 
1811     /**
1812      * Return the paint's letter-spacing for text. The default value
1813      * is 0.
1814      *
1815      * @return         the paint's letter-spacing for drawing text.
1816      */
getLetterSpacing()1817     public float getLetterSpacing() {
1818         return nGetLetterSpacing(mNativePaint);
1819     }
1820 
1821     /**
1822      * Set the paint's letter-spacing for text. The default value
1823      * is 0.  The value is in 'EM' units.  Typical values for slight
1824      * expansion will be around 0.05.  Negative values tighten text.
1825      *
1826      * @param letterSpacing set the paint's letter-spacing for drawing text.
1827      */
setLetterSpacing(float letterSpacing)1828     public void setLetterSpacing(float letterSpacing) {
1829         nSetLetterSpacing(mNativePaint, letterSpacing);
1830     }
1831 
1832     /**
1833      * Return the paint's extra word-spacing for text.
1834      *
1835      * The default value is 0.
1836      *
1837      * @return the paint's extra word-spacing for drawing text in pixels.
1838      * @see #setWordSpacing(float)
1839      */
getWordSpacing()1840     public @Px float getWordSpacing() {
1841         return nGetWordSpacing(mNativePaint);
1842     }
1843 
1844     /**
1845      * Set the paint's extra word-spacing for text.
1846      *
1847      * Increases the white space width between words with the given amount of pixels.
1848      * The default value is 0.
1849      *
1850      * @param wordSpacing set the paint's extra word-spacing for drawing text in pixels.
1851      * @see #getWordSpacing()
1852      */
setWordSpacing(@x float wordSpacing)1853     public void setWordSpacing(@Px float wordSpacing) {
1854         nSetWordSpacing(mNativePaint, wordSpacing);
1855     }
1856 
1857     /**
1858      * Returns the font feature settings. The format is the same as the CSS
1859      * font-feature-settings attribute:
1860      * <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
1861      *     https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop</a>
1862      *
1863      * @return the paint's currently set font feature settings. Default is null.
1864      *
1865      * @see #setFontFeatureSettings(String)
1866      */
getFontFeatureSettings()1867     public String getFontFeatureSettings() {
1868         return mFontFeatureSettings;
1869     }
1870 
1871     /**
1872      * Set font feature settings.
1873      *
1874      * The format is the same as the CSS font-feature-settings attribute:
1875      * <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
1876      *     https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop</a>
1877      *
1878      * @see #getFontFeatureSettings()
1879      *
1880      * @param settings the font feature settings string to use, may be null.
1881      */
setFontFeatureSettings(String settings)1882     public void setFontFeatureSettings(String settings) {
1883         if (settings != null && settings.equals("")) {
1884             settings = null;
1885         }
1886         if ((settings == null && mFontFeatureSettings == null)
1887                 || (settings != null && settings.equals(mFontFeatureSettings))) {
1888             return;
1889         }
1890         mFontFeatureSettings = settings;
1891         nSetFontFeatureSettings(mNativePaint, settings);
1892     }
1893 
1894     /**
1895      * Returns the font variation settings.
1896      *
1897      * @return the paint's currently set font variation settings. Default is null.
1898      *
1899      * @see #setFontVariationSettings(String)
1900      */
getFontVariationSettings()1901     public String getFontVariationSettings() {
1902         return mFontVariationSettings;
1903     }
1904 
1905     /**
1906      * Sets TrueType or OpenType font variation settings. The settings string is constructed from
1907      * multiple pairs of axis tag and style values. The axis tag must contain four ASCII characters
1908      * and must be wrapped with single quotes (U+0027) or double quotes (U+0022). Axis strings that
1909      * are longer or shorter than four characters, or contain characters outside of U+0020..U+007E
1910      * are invalid. If a specified axis name is not defined in the font, the settings will be
1911      * ignored.
1912      *
1913      * Examples,
1914      * <ul>
1915      * <li>Set font width to 150.
1916      * <pre>
1917      * <code>
1918      *   Paint paint = new Paint();
1919      *   paint.setFontVariationSettings("'wdth' 150");
1920      * </code>
1921      * </pre>
1922      * </li>
1923      *
1924      * <li>Set the font slant to 20 degrees and ask for italic style.
1925      * <pre>
1926      * <code>
1927      *   Paint paint = new Paint();
1928      *   paint.setFontVariationSettings("'slnt' 20, 'ital' 1");
1929      * </code>
1930      * </pre>
1931      * </li>
1932      * </ul>
1933      *
1934      * @param fontVariationSettings font variation settings. You can pass null or empty string as
1935      *                              no variation settings.
1936      *
1937      * @return true if the given settings is effective to at least one font file underlying this
1938      *         typeface. This function also returns true for empty settings string. Otherwise
1939      *         returns false
1940      *
1941      * @throws IllegalArgumentException If given string is not a valid font variation settings
1942      *                                  format
1943      *
1944      * @see #getFontVariationSettings()
1945      * @see FontVariationAxis
1946      */
setFontVariationSettings(String fontVariationSettings)1947     public boolean setFontVariationSettings(String fontVariationSettings) {
1948         final String settings = TextUtils.nullIfEmpty(fontVariationSettings);
1949         if (settings == mFontVariationSettings
1950                 || (settings != null && settings.equals(mFontVariationSettings))) {
1951             return true;
1952         }
1953 
1954         if (settings == null || settings.length() == 0) {
1955             mFontVariationSettings = null;
1956             setTypeface(Typeface.createFromTypefaceWithVariation(mTypeface,
1957                       Collections.emptyList()));
1958             return true;
1959         }
1960 
1961         // The null typeface is valid and it is equivalent to Typeface.DEFAULT.
1962         // To call isSupportedAxes method, use Typeface.DEFAULT instance.
1963         Typeface targetTypeface = mTypeface == null ? Typeface.DEFAULT : mTypeface;
1964         FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(settings);
1965         final ArrayList<FontVariationAxis> filteredAxes = new ArrayList<FontVariationAxis>();
1966         for (final FontVariationAxis axis : axes) {
1967             if (targetTypeface.isSupportedAxes(axis.getOpenTypeTagValue())) {
1968                 filteredAxes.add(axis);
1969             }
1970         }
1971         if (filteredAxes.isEmpty()) {
1972             return false;
1973         }
1974         mFontVariationSettings = settings;
1975         setTypeface(Typeface.createFromTypefaceWithVariation(targetTypeface, filteredAxes));
1976         return true;
1977     }
1978 
1979     /**
1980      * Get the current value of start hyphen edit.
1981      *
1982      * The default value is 0 which is equivalent to {@link #START_HYPHEN_EDIT_NO_EDIT}.
1983      *
1984      * @return the current starting hyphen edit value
1985      * @see #setStartHyphenEdit(int)
1986      */
getStartHyphenEdit()1987     public @StartHyphenEdit int getStartHyphenEdit() {
1988         return nGetStartHyphenEdit(mNativePaint);
1989     }
1990 
1991     /**
1992      * Get the current value of end hyphen edit.
1993      *
1994      * The default value is 0 which is equivalent to {@link #END_HYPHEN_EDIT_NO_EDIT}.
1995      *
1996      * @return the current starting hyphen edit value
1997      * @see #setStartHyphenEdit(int)
1998      */
getEndHyphenEdit()1999     public @EndHyphenEdit int getEndHyphenEdit() {
2000         return nGetEndHyphenEdit(mNativePaint);
2001     }
2002 
2003     /**
2004      * Set a start hyphen edit on the paint.
2005      *
2006      * By setting start hyphen edit, the measurement and drawing is performed with modifying
2007      * hyphenation at the start of line. For example, by passing
2008      * {@link #START_HYPHEN_EDIT_INSERT_HYPHEN} like as follows, HYPHEN(U+2010)
2009      * character is appended at the start of line.
2010      *
2011      * <pre>
2012      * <code>
2013      *   Paint paint = new Paint();
2014      *   paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_INSERT_HYPHEN);
2015      *   paint.measureText("abc", 0, 3);  // Returns the width of "-abc"
2016      *   Canvas.drawText("abc", 0, 3, 0f, 0f, paint);  // Draws "-abc"
2017      * </code>
2018      * </pre>
2019      *
2020      * The default value is 0 which is equivalent to
2021      * {@link #START_HYPHEN_EDIT_NO_EDIT}.
2022      *
2023      * @param startHyphen a start hyphen edit value.
2024      * @see #getStartHyphenEdit()
2025      */
setStartHyphenEdit(@tartHyphenEdit int startHyphen)2026     public void setStartHyphenEdit(@StartHyphenEdit int startHyphen) {
2027         nSetStartHyphenEdit(mNativePaint, startHyphen);
2028     }
2029 
2030     /**
2031      * Set a end hyphen edit on the paint.
2032      *
2033      * By setting end hyphen edit, the measurement and drawing is performed with modifying
2034      * hyphenation at the end of line. For example, by passing
2035      * {@link #END_HYPHEN_EDIT_INSERT_HYPHEN} like as follows, HYPHEN(U+2010)
2036      * character is appended at the end of line.
2037      *
2038      * <pre>
2039      * <code>
2040      *   Paint paint = new Paint();
2041      *   paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN);
2042      *   paint.measureText("abc", 0, 3);  // Returns the width of "abc-"
2043      *   Canvas.drawText("abc", 0, 3, 0f, 0f, paint);  // Draws "abc-"
2044      * </code>
2045      * </pre>
2046      *
2047      * The default value is 0 which is equivalent to {@link #END_HYPHEN_EDIT_NO_EDIT}.
2048      *
2049      * @param endHyphen a end hyphen edit value.
2050      * @see #getEndHyphenEdit()
2051      */
setEndHyphenEdit(@ndHyphenEdit int endHyphen)2052     public void setEndHyphenEdit(@EndHyphenEdit int endHyphen) {
2053         nSetEndHyphenEdit(mNativePaint, endHyphen);
2054     }
2055 
2056     /**
2057      * Return the distance above (negative) the baseline (ascent) based on the
2058      * current typeface and text size.
2059      *
2060      * <p>Note that this is the ascent of the main typeface, and actual text rendered may need a
2061      * larger ascent because fallback fonts may get used in rendering the text.
2062      *
2063      * @return the distance above (negative) the baseline (ascent) based on the
2064      *         current typeface and text size.
2065      */
ascent()2066     public float ascent() {
2067         return nAscent(mNativePaint);
2068     }
2069 
2070     /**
2071      * Return the distance below (positive) the baseline (descent) based on the
2072      * current typeface and text size.
2073      *
2074      * <p>Note that this is the descent of the main typeface, and actual text rendered may need a
2075      * larger descent because fallback fonts may get used in rendering the text.
2076      *
2077      * @return the distance below (positive) the baseline (descent) based on
2078      *         the current typeface and text size.
2079      */
descent()2080     public float descent() {
2081         return nDescent(mNativePaint);
2082     }
2083 
2084     /**
2085      * Class that describes the various metrics for a font at a given text size.
2086      * Remember, Y values increase going down, so those values will be positive,
2087      * and values that measure distances going up will be negative. This class
2088      * is returned by getFontMetrics().
2089      */
2090     public static class FontMetrics {
2091         /**
2092          * The maximum distance above the baseline for the tallest glyph in
2093          * the font at a given text size.
2094          */
2095         public float   top;
2096         /**
2097          * The recommended distance above the baseline for singled spaced text.
2098          */
2099         public float   ascent;
2100         /**
2101          * The recommended distance below the baseline for singled spaced text.
2102          */
2103         public float   descent;
2104         /**
2105          * The maximum distance below the baseline for the lowest glyph in
2106          * the font at a given text size.
2107          */
2108         public float   bottom;
2109         /**
2110          * The recommended additional space to add between lines of text.
2111          */
2112         public float   leading;
2113     }
2114 
2115     /**
2116      * Return the font's recommended interline spacing, given the Paint's
2117      * settings for typeface, textSize, etc. If metrics is not null, return the
2118      * fontmetric values in it.
2119      *
2120      * <p>Note that these are the values for the main typeface, and actual text rendered may need a
2121      * larger set of values because fallback fonts may get used in rendering the text.
2122      *
2123      * @param metrics If this object is not null, its fields are filled with
2124      *                the appropriate values given the paint's text attributes.
2125      * @return the font's recommended interline spacing.
2126      */
getFontMetrics(FontMetrics metrics)2127     public float getFontMetrics(FontMetrics metrics) {
2128         return nGetFontMetrics(mNativePaint, metrics);
2129     }
2130 
2131     /**
2132      * Allocates a new FontMetrics object, and then calls getFontMetrics(fm)
2133      * with it, returning the object.
2134      */
getFontMetrics()2135     public FontMetrics getFontMetrics() {
2136         FontMetrics fm = new FontMetrics();
2137         getFontMetrics(fm);
2138         return fm;
2139     }
2140 
2141     /**
2142      * Returns the font metrics value for the given text.
2143      *
2144      * If the text is rendered with multiple font files, this function returns the large ascent and
2145      * descent that are enough for drawing all font files.
2146      *
2147      * The context range is used for shaping context. Some script, e.g. Arabic or Devanagari,
2148      * changes letter shape based on its location or surrounding characters.
2149      *
2150      * @param text a text to be measured.
2151      * @param start a starting offset in the text.
2152      * @param count a length of the text to be measured.
2153      * @param contextStart a context starting offset in the text.
2154      * @param contextCount a length of the context to be used.
2155      * @param isRtl true if measuring on RTL context, otherwise false.
2156      * @param outMetrics the output font metrics.
2157      */
getFontMetricsInt( @onNull CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int count, @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextCount, boolean isRtl, @NonNull FontMetricsInt outMetrics)2158     public void getFontMetricsInt(
2159             @NonNull CharSequence text,
2160             @IntRange(from = 0) int start, @IntRange(from = 0) int count,
2161             @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextCount,
2162             boolean isRtl,
2163             @NonNull FontMetricsInt outMetrics) {
2164 
2165         if (text == null) {
2166             throw new IllegalArgumentException("text must not be null");
2167         }
2168         if (start < 0 || start >= text.length()) {
2169             throw new IllegalArgumentException("start argument is out of bounds.");
2170         }
2171         if (count < 0 || start + count > text.length()) {
2172             throw new IllegalArgumentException("count argument is out of bounds.");
2173         }
2174         if (contextStart < 0 || contextStart >= text.length()) {
2175             throw new IllegalArgumentException("ctxStart argument is out of bounds.");
2176         }
2177         if (contextCount < 0 || contextStart + contextCount > text.length()) {
2178             throw new IllegalArgumentException("ctxCount argument is out of bounds.");
2179         }
2180         if (outMetrics == null) {
2181             throw new IllegalArgumentException("outMetrics must not be null.");
2182         }
2183 
2184         if (count == 0) {
2185             getFontMetricsInt(outMetrics);
2186             return;
2187         }
2188 
2189         if (text instanceof String) {
2190             nGetFontMetricsIntForText(mNativePaint, (String) text, start, count, contextStart,
2191                     contextCount, isRtl, outMetrics);
2192         } else {
2193             char[] buf = TemporaryBuffer.obtain(contextCount);
2194             try {
2195                 TextUtils.getChars(text, contextStart, contextStart + contextCount, buf, 0);
2196                 nGetFontMetricsIntForText(mNativePaint, buf, start - contextStart, count, 0,
2197                         contextCount, isRtl, outMetrics);
2198             } finally {
2199                 TemporaryBuffer.recycle(buf);
2200             }
2201         }
2202 
2203     }
2204 
2205     /**
2206      * Returns the font metrics value for the given text.
2207      *
2208      * If the text is rendered with multiple font files, this function returns the large ascent and
2209      * descent that are enough for drawing all font files.
2210      *
2211      * The context range is used for shaping context. Some script, e.g. Arabic or Devanagari,
2212      * changes letter shape based on its location or surrounding characters.
2213      *
2214      * @param text a text to be measured.
2215      * @param start a starting offset in the text.
2216      * @param count a length of the text to be measured.
2217      * @param contextStart a context starting offset in the text.
2218      * @param contextCount a length of the context to be used.
2219      * @param isRtl true if measuring on RTL context, otherwise false.
2220      * @param outMetrics the output font metrics.
2221      */
getFontMetricsInt(@onNull char[] text, @IntRange(from = 0) int start, @IntRange(from = 0) int count, @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextCount, boolean isRtl, @NonNull FontMetricsInt outMetrics)2222     public void getFontMetricsInt(@NonNull char[] text,
2223             @IntRange(from = 0) int start, @IntRange(from = 0) int count,
2224             @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextCount,
2225             boolean isRtl,
2226             @NonNull FontMetricsInt outMetrics) {
2227         if (text == null) {
2228             throw new IllegalArgumentException("text must not be null");
2229         }
2230         if (start < 0 || start >= text.length) {
2231             throw new IllegalArgumentException("start argument is out of bounds.");
2232         }
2233         if (count < 0 || start + count > text.length) {
2234             throw new IllegalArgumentException("count argument is out of bounds.");
2235         }
2236         if (contextStart < 0 || contextStart >= text.length) {
2237             throw new IllegalArgumentException("ctxStart argument is out of bounds.");
2238         }
2239         if (contextCount < 0 || contextStart + contextCount > text.length) {
2240             throw new IllegalArgumentException("ctxCount argument is out of bounds.");
2241         }
2242         if (outMetrics == null) {
2243             throw new IllegalArgumentException("outMetrics must not be null.");
2244         }
2245 
2246         if (count == 0) {
2247             getFontMetricsInt(outMetrics);
2248             return;
2249         }
2250 
2251         nGetFontMetricsIntForText(mNativePaint, text, start, count, contextStart, contextCount,
2252                 isRtl, outMetrics);
2253     }
2254 
2255     /**
2256      * Convenience method for callers that want to have FontMetrics values as
2257      * integers.
2258      */
2259     public static class FontMetricsInt {
2260         /**
2261          * The maximum distance above the baseline for the tallest glyph in
2262          * the font at a given text size.
2263          */
2264         public int   top;
2265         /**
2266          * The recommended distance above the baseline for singled spaced text.
2267          */
2268         public int   ascent;
2269         /**
2270          * The recommended distance below the baseline for singled spaced text.
2271          */
2272         public int   descent;
2273         /**
2274          * The maximum distance below the baseline for the lowest glyph in
2275          * the font at a given text size.
2276          */
2277         public int   bottom;
2278         /**
2279          * The recommended additional space to add between lines of text.
2280          */
2281         public int   leading;
2282 
toString()2283         @Override public String toString() {
2284             return "FontMetricsInt: top=" + top + " ascent=" + ascent +
2285                     " descent=" + descent + " bottom=" + bottom +
2286                     " leading=" + leading;
2287         }
2288 
2289         @Override
equals(Object o)2290         public boolean equals(Object o) {
2291             if (this == o) return true;
2292             if (!(o instanceof FontMetricsInt)) return false;
2293             FontMetricsInt that = (FontMetricsInt) o;
2294             return top == that.top
2295                     && ascent == that.ascent
2296                     && descent == that.descent
2297                     && bottom == that.bottom
2298                     && leading == that.leading;
2299         }
2300 
2301         @Override
hashCode()2302         public int hashCode() {
2303             return Objects.hash(top, ascent, descent, bottom, leading);
2304         }
2305     }
2306 
2307     /**
2308      * Return the font's interline spacing, given the Paint's settings for
2309      * typeface, textSize, etc. If metrics is not null, return the fontmetric
2310      * values in it. Note: all values have been converted to integers from
2311      * floats, in such a way has to make the answers useful for both spacing
2312      * and clipping. If you want more control over the rounding, call
2313      * getFontMetrics().
2314      *
2315      * <p>Note that these are the values for the main typeface, and actual text rendered may need a
2316      * larger set of values because fallback fonts may get used in rendering the text.
2317      *
2318      * @return the font's interline spacing.
2319      */
getFontMetricsInt(FontMetricsInt fmi)2320     public int getFontMetricsInt(FontMetricsInt fmi) {
2321         return nGetFontMetricsInt(mNativePaint, fmi);
2322     }
2323 
getFontMetricsInt()2324     public FontMetricsInt getFontMetricsInt() {
2325         FontMetricsInt fm = new FontMetricsInt();
2326         getFontMetricsInt(fm);
2327         return fm;
2328     }
2329 
2330     /**
2331      * Return the recommend line spacing based on the current typeface and
2332      * text size.
2333      *
2334      * <p>Note that this is the value for the main typeface, and actual text rendered may need a
2335      * larger value because fallback fonts may get used in rendering the text.
2336      *
2337      * @return  recommend line spacing based on the current typeface and
2338      *          text size.
2339      */
getFontSpacing()2340     public float getFontSpacing() {
2341         return getFontMetrics(null);
2342     }
2343 
2344     /**
2345      * Return the width of the text.
2346      *
2347      * @param text  The text to measure. Cannot be null.
2348      * @param index The index of the first character to start measuring
2349      * @param count THe number of characters to measure, beginning with start
2350      * @return      The width of the text
2351      */
measureText(char[] text, int index, int count)2352     public float measureText(char[] text, int index, int count) {
2353         if (text == null) {
2354             throw new IllegalArgumentException("text cannot be null");
2355         }
2356         if ((index | count) < 0 || index + count > text.length) {
2357             throw new ArrayIndexOutOfBoundsException();
2358         }
2359 
2360         if (text.length == 0 || count == 0) {
2361             return 0f;
2362         }
2363         if (!mHasCompatScaling) {
2364             return (float) Math.ceil(nGetTextAdvances(mNativePaint, text,
2365                     index, count, index, count, mBidiFlags, null, 0));
2366         }
2367 
2368         final float oldSize = getTextSize();
2369         setTextSize(oldSize * mCompatScaling);
2370         final float w = nGetTextAdvances(mNativePaint, text, index, count, index, count,
2371                 mBidiFlags, null, 0);
2372         setTextSize(oldSize);
2373         return (float) Math.ceil(w*mInvCompatScaling);
2374     }
2375 
2376     /**
2377      * Return the width of the text.
2378      *
2379      * @param text  The text to measure. Cannot be null.
2380      * @param start The index of the first character to start measuring
2381      * @param end   1 beyond the index of the last character to measure
2382      * @return      The width of the text
2383      */
measureText(String text, int start, int end)2384     public float measureText(String text, int start, int end) {
2385         if (text == null) {
2386             throw new IllegalArgumentException("text cannot be null");
2387         }
2388         if ((start | end | (end - start) | (text.length() - end)) < 0) {
2389             throw new IndexOutOfBoundsException();
2390         }
2391 
2392         if (text.length() == 0 || start == end) {
2393             return 0f;
2394         }
2395         if (!mHasCompatScaling) {
2396             return (float) Math.ceil(nGetTextAdvances(mNativePaint, text,
2397                     start, end, start, end, mBidiFlags, null, 0));
2398         }
2399         final float oldSize = getTextSize();
2400         setTextSize(oldSize * mCompatScaling);
2401         final float w = nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags,
2402                 null, 0);
2403         setTextSize(oldSize);
2404         return (float) Math.ceil(w * mInvCompatScaling);
2405     }
2406 
2407     /**
2408      * Return the width of the text.
2409      *
2410      * @param text  The text to measure. Cannot be null.
2411      * @return      The width of the text
2412      */
measureText(String text)2413     public float measureText(String text) {
2414         if (text == null) {
2415             throw new IllegalArgumentException("text cannot be null");
2416         }
2417         return measureText(text, 0, text.length());
2418     }
2419 
2420     /**
2421      * Return the width of the text.
2422      *
2423      * @param text  The text to measure
2424      * @param start The index of the first character to start measuring
2425      * @param end   1 beyond the index of the last character to measure
2426      * @return      The width of the text
2427      */
measureText(CharSequence text, int start, int end)2428     public float measureText(CharSequence text, int start, int end) {
2429         if (text == null) {
2430             throw new IllegalArgumentException("text cannot be null");
2431         }
2432         if ((start | end | (end - start) | (text.length() - end)) < 0) {
2433             throw new IndexOutOfBoundsException();
2434         }
2435 
2436         if (text.length() == 0 || start == end) {
2437             return 0f;
2438         }
2439         if (text instanceof String) {
2440             return measureText((String)text, start, end);
2441         }
2442         if (text instanceof SpannedString ||
2443             text instanceof SpannableString) {
2444             return measureText(text.toString(), start, end);
2445         }
2446         if (text instanceof GraphicsOperations) {
2447             return ((GraphicsOperations)text).measureText(start, end, this);
2448         }
2449 
2450         char[] buf = TemporaryBuffer.obtain(end - start);
2451         TextUtils.getChars(text, start, end, buf, 0);
2452         float result = measureText(buf, 0, end - start);
2453         TemporaryBuffer.recycle(buf);
2454         return result;
2455     }
2456 
2457     /**
2458      * Measure the text, stopping early if the measured width exceeds maxWidth.
2459      * Return the number of chars that were measured, and if measuredWidth is
2460      * not null, return in it the actual width measured.
2461      *
2462      * @param text  The text to measure. Cannot be null.
2463      * @param index The offset into text to begin measuring at
2464      * @param count The number of maximum number of entries to measure. If count
2465      *              is negative, then the characters are measured in reverse order.
2466      * @param maxWidth The maximum width to accumulate.
2467      * @param measuredWidth Optional. If not null, returns the actual width
2468      *                     measured.
2469      * @return The number of chars that were measured. Will always be <=
2470      *         abs(count).
2471      */
breakText(char[] text, int index, int count, float maxWidth, float[] measuredWidth)2472     public int breakText(char[] text, int index, int count,
2473                                 float maxWidth, float[] measuredWidth) {
2474         if (text == null) {
2475             throw new IllegalArgumentException("text cannot be null");
2476         }
2477         if (index < 0 || text.length - index < Math.abs(count)) {
2478             throw new ArrayIndexOutOfBoundsException();
2479         }
2480 
2481         if (text.length == 0 || count == 0) {
2482             return 0;
2483         }
2484         if (!mHasCompatScaling) {
2485             return nBreakText(mNativePaint, text, index, count, maxWidth, mBidiFlags,
2486                     measuredWidth);
2487         }
2488 
2489         final float oldSize = getTextSize();
2490         setTextSize(oldSize * mCompatScaling);
2491         final int res = nBreakText(mNativePaint, text, index, count, maxWidth * mCompatScaling,
2492                 mBidiFlags, measuredWidth);
2493         setTextSize(oldSize);
2494         if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
2495         return res;
2496     }
2497 
2498     /**
2499      * Measure the text, stopping early if the measured width exceeds maxWidth.
2500      * Return the number of chars that were measured, and if measuredWidth is
2501      * not null, return in it the actual width measured.
2502      *
2503      * @param text  The text to measure. Cannot be null.
2504      * @param start The offset into text to begin measuring at
2505      * @param end   The end of the text slice to measure.
2506      * @param measureForwards If true, measure forwards, starting at start.
2507      *                        Otherwise, measure backwards, starting with end.
2508      * @param maxWidth The maximum width to accumulate.
2509      * @param measuredWidth Optional. If not null, returns the actual width
2510      *                     measured.
2511      * @return The number of chars that were measured. Will always be <=
2512      *         abs(end - start).
2513      */
breakText(CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measuredWidth)2514     public int breakText(CharSequence text, int start, int end,
2515                          boolean measureForwards,
2516                          float maxWidth, float[] measuredWidth) {
2517         if (text == null) {
2518             throw new IllegalArgumentException("text cannot be null");
2519         }
2520         if ((start | end | (end - start) | (text.length() - end)) < 0) {
2521             throw new IndexOutOfBoundsException();
2522         }
2523 
2524         if (text.length() == 0 || start == end) {
2525             return 0;
2526         }
2527         if (start == 0 && text instanceof String && end == text.length()) {
2528             return breakText((String) text, measureForwards, maxWidth,
2529                              measuredWidth);
2530         }
2531 
2532         char[] buf = TemporaryBuffer.obtain(end - start);
2533         int result;
2534 
2535         TextUtils.getChars(text, start, end, buf, 0);
2536 
2537         if (measureForwards) {
2538             result = breakText(buf, 0, end - start, maxWidth, measuredWidth);
2539         } else {
2540             result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth);
2541         }
2542 
2543         TemporaryBuffer.recycle(buf);
2544         return result;
2545     }
2546 
2547     /**
2548      * Measure the text, stopping early if the measured width exceeds maxWidth.
2549      * Return the number of chars that were measured, and if measuredWidth is
2550      * not null, return in it the actual width measured.
2551      *
2552      * @param text  The text to measure. Cannot be null.
2553      * @param measureForwards If true, measure forwards, starting with the
2554      *                        first character in the string. Otherwise,
2555      *                        measure backwards, starting with the
2556      *                        last character in the string.
2557      * @param maxWidth The maximum width to accumulate.
2558      * @param measuredWidth Optional. If not null, returns the actual width
2559      *                     measured.
2560      * @return The number of chars that were measured. Will always be <=
2561      *         abs(count).
2562      */
breakText(String text, boolean measureForwards, float maxWidth, float[] measuredWidth)2563     public int breakText(String text, boolean measureForwards,
2564                                 float maxWidth, float[] measuredWidth) {
2565         if (text == null) {
2566             throw new IllegalArgumentException("text cannot be null");
2567         }
2568 
2569         if (text.length() == 0) {
2570             return 0;
2571         }
2572         if (!mHasCompatScaling) {
2573             return nBreakText(mNativePaint, text, measureForwards,
2574                     maxWidth, mBidiFlags, measuredWidth);
2575         }
2576 
2577         final float oldSize = getTextSize();
2578         setTextSize(oldSize*mCompatScaling);
2579         final int res = nBreakText(mNativePaint, text, measureForwards, maxWidth*mCompatScaling,
2580                 mBidiFlags, measuredWidth);
2581         setTextSize(oldSize);
2582         if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
2583         return res;
2584     }
2585 
2586     /**
2587      * Return the advance widths for the characters in the string.
2588      *
2589      * @param text     The text to measure. Cannot be null.
2590      * @param index    The index of the first char to to measure
2591      * @param count    The number of chars starting with index to measure
2592      * @param widths   array to receive the advance widths of the characters.
2593      *                 Must be at least a large as count.
2594      * @return         the actual number of widths returned.
2595      */
getTextWidths(char[] text, int index, int count, float[] widths)2596     public int getTextWidths(char[] text, int index, int count,
2597                              float[] widths) {
2598         if (text == null) {
2599             throw new IllegalArgumentException("text cannot be null");
2600         }
2601         if ((index | count) < 0 || index + count > text.length
2602                 || count > widths.length) {
2603             throw new ArrayIndexOutOfBoundsException();
2604         }
2605 
2606         if (text.length == 0 || count == 0) {
2607             return 0;
2608         }
2609         if (!mHasCompatScaling) {
2610             nGetTextAdvances(mNativePaint, text, index, count, index, count, mBidiFlags, widths, 0);
2611             return count;
2612         }
2613 
2614         final float oldSize = getTextSize();
2615         setTextSize(oldSize * mCompatScaling);
2616         nGetTextAdvances(mNativePaint, text, index, count, index, count, mBidiFlags, widths, 0);
2617         setTextSize(oldSize);
2618         for (int i = 0; i < count; i++) {
2619             widths[i] *= mInvCompatScaling;
2620         }
2621         return count;
2622     }
2623 
2624     /**
2625      * Return the advance widths for the characters in the string.
2626      *
2627      * @param text     The text to measure. Cannot be null.
2628      * @param start    The index of the first char to to measure
2629      * @param end      The end of the text slice to measure
2630      * @param widths   array to receive the advance widths of the characters.
2631      *                 Must be at least a large as (end - start).
2632      * @return         the actual number of widths returned.
2633      */
getTextWidths(CharSequence text, int start, int end, float[] widths)2634     public int getTextWidths(CharSequence text, int start, int end,
2635                              float[] widths) {
2636         if (text == null) {
2637             throw new IllegalArgumentException("text cannot be null");
2638         }
2639         if ((start | end | (end - start) | (text.length() - end)) < 0) {
2640             throw new IndexOutOfBoundsException();
2641         }
2642         if (end - start > widths.length) {
2643             throw new ArrayIndexOutOfBoundsException();
2644         }
2645 
2646         if (text.length() == 0 || start == end) {
2647             return 0;
2648         }
2649         if (text instanceof String) {
2650             return getTextWidths((String) text, start, end, widths);
2651         }
2652         if (text instanceof SpannedString ||
2653             text instanceof SpannableString) {
2654             return getTextWidths(text.toString(), start, end, widths);
2655         }
2656         if (text instanceof GraphicsOperations) {
2657             return ((GraphicsOperations) text).getTextWidths(start, end,
2658                                                                  widths, this);
2659         }
2660 
2661         char[] buf = TemporaryBuffer.obtain(end - start);
2662         TextUtils.getChars(text, start, end, buf, 0);
2663         int result = getTextWidths(buf, 0, end - start, widths);
2664         TemporaryBuffer.recycle(buf);
2665         return result;
2666     }
2667 
2668     /**
2669      * Return the advance widths for the characters in the string.
2670      *
2671      * @param text   The text to measure. Cannot be null.
2672      * @param start  The index of the first char to to measure
2673      * @param end    The end of the text slice to measure
2674      * @param widths array to receive the advance widths of the characters.
2675      *               Must be at least a large as the text.
2676      * @return       the number of code units in the specified text.
2677      */
getTextWidths(String text, int start, int end, float[] widths)2678     public int getTextWidths(String text, int start, int end, float[] widths) {
2679         if (text == null) {
2680             throw new IllegalArgumentException("text cannot be null");
2681         }
2682         if ((start | end | (end - start) | (text.length() - end)) < 0) {
2683             throw new IndexOutOfBoundsException();
2684         }
2685         if (end - start > widths.length) {
2686             throw new ArrayIndexOutOfBoundsException();
2687         }
2688 
2689         if (text.length() == 0 || start == end) {
2690             return 0;
2691         }
2692         if (!mHasCompatScaling) {
2693             nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags, widths, 0);
2694             return end - start;
2695         }
2696 
2697         final float oldSize = getTextSize();
2698         setTextSize(oldSize * mCompatScaling);
2699         nGetTextAdvances(mNativePaint, text, start, end, start, end, mBidiFlags, widths, 0);
2700         setTextSize(oldSize);
2701         for (int i = 0; i < end - start; i++) {
2702             widths[i] *= mInvCompatScaling;
2703         }
2704         return end - start;
2705     }
2706 
2707     /**
2708      * Return the advance widths for the characters in the string.
2709      *
2710      * @param text   The text to measure
2711      * @param widths array to receive the advance widths of the characters.
2712      *               Must be at least a large as the text.
2713      * @return       the number of code units in the specified text.
2714      */
getTextWidths(String text, float[] widths)2715     public int getTextWidths(String text, float[] widths) {
2716         return getTextWidths(text, 0, text.length(), widths);
2717     }
2718 
2719     /**
2720      * Retrieve the character advances of the text.
2721      *
2722      * Returns the total advance width for the characters in the run from {@code index} for
2723      * {@code count} of chars, and if {@code advances} is not null, the advance assigned to each of
2724      * these characters (java chars).
2725      *
2726      * <p>
2727      * The trailing surrogate in a valid surrogate pair is assigned an advance of 0.  Thus the
2728      * number of returned advances is always equal to count, not to the number of unicode codepoints
2729      * represented by the run.
2730      * </p>
2731      *
2732      * <p>
2733      * In the case of conjuncts or combining marks, the total advance is assigned to the first
2734      * logical character, and the following characters are assigned an advance of 0.
2735      * </p>
2736      *
2737      * <p>
2738      * This generates the sum of the advances of glyphs for characters in a reordered cluster as the
2739      * width of the first logical character in the cluster, and 0 for the widths of all other
2740      * characters in the cluster.  In effect, such clusters are treated like conjuncts.
2741      * </p>
2742      *
2743      * <p>
2744      * The shaping bounds limit the amount of context available outside start and end that can be
2745      * used for shaping analysis.  These bounds typically reflect changes in bidi level or font
2746      * metrics across which shaping does not occur.
2747      * </p>
2748      *
2749      * @param chars the text to measure.
2750      * @param index the index of the first character to measure
2751      * @param count the number of characters to measure
2752      * @param contextIndex the index of the first character to use for shaping context.
2753      *                     Context must cover the measureing target.
2754      * @param contextCount the number of character to use for shaping context.
2755      *                     Context must cover the measureing target.
2756      * @param isRtl whether the run is in RTL direction
2757      * @param advances array to receive the advances, must have room for all advances.
2758      *                 This can be null if only total advance is needed
2759      * @param advancesIndex the position in advances at which to put the advance corresponding to
2760      *                      the character at start
2761      * @return the total advance in pixels
2762      */
getTextRunAdvances(@onNull char[] chars, @IntRange(from = 0) int index, @IntRange(from = 0) int count, @IntRange(from = 0) int contextIndex, @IntRange(from = 0) int contextCount, boolean isRtl, @Nullable float[] advances, @IntRange(from = 0) int advancesIndex)2763     public float getTextRunAdvances(@NonNull char[] chars, @IntRange(from = 0) int index,
2764             @IntRange(from = 0) int count, @IntRange(from = 0) int contextIndex,
2765             @IntRange(from = 0) int contextCount, boolean isRtl, @Nullable float[] advances,
2766             @IntRange(from = 0) int advancesIndex) {
2767         if (chars == null) {
2768             throw new IllegalArgumentException("text cannot be null");
2769         }
2770         if ((index | count | contextIndex | contextCount | advancesIndex
2771                 | (index - contextIndex) | (contextCount - count)
2772                 | ((contextIndex + contextCount) - (index + count))
2773                 | (chars.length - (contextIndex + contextCount))
2774                 | (advances == null ? 0 :
2775                     (advances.length - (advancesIndex + count)))) < 0) {
2776             throw new IndexOutOfBoundsException();
2777         }
2778 
2779         if (chars.length == 0 || count == 0){
2780             return 0f;
2781         }
2782         if (!mHasCompatScaling) {
2783             return nGetTextAdvances(mNativePaint, chars, index, count, contextIndex, contextCount,
2784                     isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
2785                     advancesIndex);
2786         }
2787 
2788         final float oldSize = getTextSize();
2789         setTextSize(oldSize * mCompatScaling);
2790         final float res = nGetTextAdvances(mNativePaint, chars, index, count, contextIndex,
2791                 contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex);
2792         setTextSize(oldSize);
2793 
2794         if (advances != null) {
2795             for (int i = advancesIndex, e = i + count; i < e; i++) {
2796                 advances[i] *= mInvCompatScaling;
2797             }
2798         }
2799         return res * mInvCompatScaling; // assume errors are not significant
2800     }
2801 
2802     /**
2803      * Returns the next cursor position in the run.
2804      *
2805      * This avoids placing the cursor between surrogates, between characters that form conjuncts,
2806      * between base characters and combining marks, or within a reordering cluster.
2807      *
2808      * <p>
2809      * ContextStart and offset are relative to the start of text.
2810      * The context is the shaping context for cursor movement, generally the bounds of the metric
2811      * span enclosing the cursor in the direction of movement.
2812      *
2813      * <p>
2814      * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
2815      * returns -1.  Otherwise this will never return a value before contextStart or after
2816      * contextStart + contextLength.
2817      *
2818      * @param text the text
2819      * @param contextStart the start of the context
2820      * @param contextLength the length of the context
2821      * @param isRtl true if the paragraph context is RTL, otherwise false
2822      * @param offset the cursor position to move from
2823      * @param cursorOpt how to move the cursor
2824      * @return the offset of the next position, or -1
2825      */
getTextRunCursor(@onNull char[] text, @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextLength, boolean isRtl, @IntRange(from = 0) int offset, @CursorOption int cursorOpt)2826     public int getTextRunCursor(@NonNull char[] text, @IntRange(from = 0) int contextStart,
2827             @IntRange(from = 0) int contextLength, boolean isRtl, @IntRange(from = 0) int offset,
2828             @CursorOption int cursorOpt) {
2829         int contextEnd = contextStart + contextLength;
2830         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
2831                 | (offset - contextStart) | (contextEnd - offset)
2832                 | (text.length - contextEnd) | cursorOpt) < 0)
2833                 || cursorOpt > CURSOR_OPT_MAX_VALUE) {
2834             throw new IndexOutOfBoundsException();
2835         }
2836 
2837         return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength,
2838                 isRtl ? DIRECTION_RTL : DIRECTION_LTR, offset, cursorOpt);
2839     }
2840 
2841     /**
2842      * Returns the next cursor position in the run.
2843      *
2844      * This avoids placing the cursor between surrogates, between characters that form conjuncts,
2845      * between base characters and combining marks, or within a reordering cluster.
2846      *
2847      * <p>
2848      * ContextStart, contextEnd, and offset are relative to the start of
2849      * text.  The context is the shaping context for cursor movement, generally
2850      * the bounds of the metric span enclosing the cursor in the direction of
2851      * movement.
2852      *
2853      * <p>
2854      * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
2855      * returns -1.  Otherwise this will never return a value before contextStart or after
2856      * contextEnd.
2857      *
2858      * @param text the text
2859      * @param contextStart the start of the context
2860      * @param contextEnd the end of the context
2861      * @param isRtl true if the paragraph context is RTL, otherwise false
2862      * @param offset the cursor position to move from
2863      * @param cursorOpt how to move the cursor
2864      * @return the offset of the next position, or -1
2865      */
getTextRunCursor(@onNull CharSequence text, @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset, @CursorOption int cursorOpt)2866     public int getTextRunCursor(@NonNull CharSequence text, @IntRange(from = 0) int contextStart,
2867             @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset,
2868             @CursorOption int cursorOpt) {
2869 
2870         if (text instanceof String || text instanceof SpannedString ||
2871                 text instanceof SpannableString) {
2872             return getTextRunCursor(text.toString(), contextStart, contextEnd,
2873                     isRtl, offset, cursorOpt);
2874         }
2875         if (text instanceof GraphicsOperations) {
2876             return ((GraphicsOperations) text).getTextRunCursor(
2877                     contextStart, contextEnd, isRtl, offset, cursorOpt, this);
2878         }
2879 
2880         int contextLen = contextEnd - contextStart;
2881         char[] buf = TemporaryBuffer.obtain(contextLen);
2882         TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
2883         int relPos = getTextRunCursor(buf, 0, contextLen, isRtl, offset - contextStart, cursorOpt);
2884         TemporaryBuffer.recycle(buf);
2885         return (relPos == -1) ? -1 : relPos + contextStart;
2886     }
2887 
2888     /**
2889      * Returns the next cursor position in the run.
2890      *
2891      * This avoids placing the cursor between surrogates, between characters that form conjuncts,
2892      * between base characters and combining marks, or within a reordering cluster.
2893      *
2894      * <p>
2895      * ContextStart, contextEnd, and offset are relative to the start of text.  The context is the
2896      * shaping context for cursor movement, generally the bounds of the metric span enclosing the
2897      * cursor in the direction of movement.
2898      * </p>
2899      *
2900      * <p>
2901      * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
2902      * returns -1.  Otherwise this will never return a value before contextStart or after
2903      * contextEnd.
2904      * </p>
2905      *
2906      * @param text the text
2907      * @param contextStart the start of the context
2908      * @param contextEnd the end of the context
2909      * @param isRtl true if the paragraph context is RTL, otherwise false.
2910      * @param offset the cursor position to move from
2911      * @param cursorOpt how to move the cursor
2912      * @return the offset of the next position, or -1
2913      * @hide
2914      */
getTextRunCursor(@onNull String text, @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset, @CursorOption int cursorOpt)2915     public int getTextRunCursor(@NonNull String text, @IntRange(from = 0) int contextStart,
2916             @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset,
2917             @CursorOption int cursorOpt) {
2918         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
2919                 | (offset - contextStart) | (contextEnd - offset)
2920                 | (text.length() - contextEnd) | cursorOpt) < 0)
2921                 || cursorOpt > CURSOR_OPT_MAX_VALUE) {
2922             throw new IndexOutOfBoundsException();
2923         }
2924 
2925         return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd,
2926                 isRtl ? DIRECTION_RTL : DIRECTION_LTR, offset, cursorOpt);
2927     }
2928 
2929     /**
2930      * Return the path (outline) for the specified text.
2931      * Note: just like Canvas.drawText, this will respect the Align setting in
2932      * the paint.
2933      *
2934      * @param text the text to retrieve the path from
2935      * @param index the index of the first character in text
2936      * @param count the number of characters starting with index
2937      * @param x the x coordinate of the text's origin
2938      * @param y the y coordinate of the text's origin
2939      * @param path the path to receive the data describing the text. Must be allocated by the caller
2940      */
getTextPath(char[] text, int index, int count, float x, float y, Path path)2941     public void getTextPath(char[] text, int index, int count,
2942                             float x, float y, Path path) {
2943         if ((index | count) < 0 || index + count > text.length) {
2944             throw new ArrayIndexOutOfBoundsException();
2945         }
2946         nGetTextPath(mNativePaint, mBidiFlags, text, index, count, x, y, path.mutateNI());
2947     }
2948 
2949     /**
2950      * Return the path (outline) for the specified text.
2951      * Note: just like Canvas.drawText, this will respect the Align setting
2952      * in the paint.
2953      *
2954      * @param text the text to retrieve the path from
2955      * @param start the first character in the text
2956      * @param end 1 past the last character in the text
2957      * @param x the x coordinate of the text's origin
2958      * @param y the y coordinate of the text's origin
2959      * @param path the path to receive the data describing the text. Must be allocated by the caller
2960      */
getTextPath(String text, int start, int end, float x, float y, Path path)2961     public void getTextPath(String text, int start, int end,
2962                             float x, float y, Path path) {
2963         if ((start | end | (end - start) | (text.length() - end)) < 0) {
2964             throw new IndexOutOfBoundsException();
2965         }
2966         nGetTextPath(mNativePaint, mBidiFlags, text, start, end, x, y, path.mutateNI());
2967     }
2968 
2969     /**
2970      * Retrieve the text boundary box and store to bounds.
2971      *
2972      * Return in bounds (allocated by the caller) the smallest rectangle that
2973      * encloses all of the characters, with an implied origin at (0,0).
2974      *
2975      * @param text string to measure and return its bounds
2976      * @param start index of the first char in the string to measure
2977      * @param end 1 past the last char in the string to measure
2978      * @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
2979      */
getTextBounds(String text, int start, int end, Rect bounds)2980     public void getTextBounds(String text, int start, int end, Rect bounds) {
2981         if ((start | end | (end - start) | (text.length() - end)) < 0) {
2982             throw new IndexOutOfBoundsException();
2983         }
2984         if (bounds == null) {
2985             throw new NullPointerException("need bounds Rect");
2986         }
2987         nGetStringBounds(mNativePaint, text, start, end, mBidiFlags, bounds);
2988     }
2989 
2990     /**
2991      * Retrieve the text boundary box and store to bounds.
2992      *
2993      * Return in bounds (allocated by the caller) the smallest rectangle that
2994      * encloses all of the characters, with an implied origin at (0,0).
2995      *
2996      * Note that styles are ignored even if you pass {@link android.text.Spanned} instance.
2997      * Use {@link android.text.StaticLayout} for measuring bounds of {@link android.text.Spanned}.
2998      *
2999      * @param text text to measure and return its bounds
3000      * @param start index of the first char in the text to measure
3001      * @param end 1 past the last char in the text to measure
3002      * @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
3003      */
getTextBounds(@onNull CharSequence text, int start, int end, @NonNull Rect bounds)3004     public void getTextBounds(@NonNull CharSequence text, int start, int end,
3005             @NonNull Rect bounds) {
3006         if ((start | end | (end - start) | (text.length() - end)) < 0) {
3007             throw new IndexOutOfBoundsException();
3008         }
3009         if (bounds == null) {
3010             throw new NullPointerException("need bounds Rect");
3011         }
3012         char[] buf = TemporaryBuffer.obtain(end - start);
3013         TextUtils.getChars(text, start, end, buf, 0);
3014         getTextBounds(buf, 0, end - start, bounds);
3015         TemporaryBuffer.recycle(buf);
3016     }
3017 
3018     /**
3019      * Return in bounds (allocated by the caller) the smallest rectangle that
3020      * encloses all of the characters, with an implied origin at (0,0).
3021      *
3022      * @param text  array of chars to measure and return their unioned bounds
3023      * @param index index of the first char in the array to measure
3024      * @param count the number of chars, beginning at index, to measure
3025      * @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
3026      */
getTextBounds(char[] text, int index, int count, Rect bounds)3027     public void getTextBounds(char[] text, int index, int count, Rect bounds) {
3028         if ((index | count) < 0 || index + count > text.length) {
3029             throw new ArrayIndexOutOfBoundsException();
3030         }
3031         if (bounds == null) {
3032             throw new NullPointerException("need bounds Rect");
3033         }
3034         nGetCharArrayBounds(mNativePaint, text, index, count, mBidiFlags,
3035             bounds);
3036     }
3037 
3038     /**
3039      * Determine whether the typeface set on the paint has a glyph supporting the string. The
3040      * simplest case is when the string contains a single character, in which this method
3041      * determines whether the font has the character. In the case of multiple characters, the
3042      * method returns true if there is a single glyph representing the ligature. For example, if
3043      * the input is a pair of regional indicator symbols, determine whether there is an emoji flag
3044      * for the pair.
3045      *
3046      * <p>Finally, if the string contains a variation selector, the method only returns true if
3047      * the fonts contains a glyph specific to that variation.
3048      *
3049      * <p>Checking is done on the entire fallback chain, not just the immediate font referenced.
3050      *
3051      * @param string the string to test whether there is glyph support
3052      * @return true if the typeface has a glyph for the string
3053      */
hasGlyph(String string)3054     public boolean hasGlyph(String string) {
3055         return nHasGlyph(mNativePaint, mBidiFlags, string);
3056     }
3057 
3058     /**
3059      * Measure cursor position within a run of text.
3060      *
3061      * <p>The run of text includes the characters from {@code start} to {@code end} in the text. In
3062      * addition, the range {@code contextStart} to {@code contextEnd} is used as context for the
3063      * purpose of complex text shaping, such as Arabic text potentially shaped differently based on
3064      * the text next to it.
3065      *
3066      * <p>All text outside the range {@code contextStart..contextEnd} is ignored. The text between
3067      * {@code start} and {@code end} will be laid out to be measured.
3068      *
3069      * <p>The returned width measurement is the advance from {@code start} to {@code offset}. It is
3070      * generally a positive value, no matter the direction of the run. If {@code offset == end},
3071      * the return value is simply the width of the whole run from {@code start} to {@code end}.
3072      *
3073      * <p>Ligatures are formed for characters in the range {@code start..end} (but not for
3074      * {@code start..contextStart} or {@code end..contextEnd}). If {@code offset} points to a
3075      * character in the middle of such a formed ligature, but at a grapheme cluster boundary, the
3076      * return value will also reflect an advance in the middle of the ligature. See
3077      * {@link #getOffsetForAdvance} for more discussion of grapheme cluster boundaries.
3078      *
3079      * <p>The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is
3080      * suitable only for runs of a single direction.
3081      *
3082      * <p>All indices are relative to the start of {@code text}. Further, {@code 0 <= contextStart
3083      * <= start <= offset <= end <= contextEnd <= text.length} must hold on entry.
3084      *
3085      * @param text the text to measure. Cannot be null.
3086      * @param start the index of the start of the range to measure
3087      * @param end the index + 1 of the end of the range to measure
3088      * @param contextStart the index of the start of the shaping context
3089      * @param contextEnd the index + 1 of the end of the shaping context
3090      * @param isRtl whether the run is in RTL direction
3091      * @param offset index of caret position
3092      * @return width measurement between start and offset
3093      */
getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)3094     public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd,
3095             boolean isRtl, int offset) {
3096         if (text == null) {
3097             throw new IllegalArgumentException("text cannot be null");
3098         }
3099         if ((contextStart | start | offset | end | contextEnd
3100                 | start - contextStart | offset - start | end - offset
3101                 | contextEnd - end | text.length - contextEnd) < 0) {
3102             throw new IndexOutOfBoundsException();
3103         }
3104         if (end == start) {
3105             return 0.0f;
3106         }
3107         // TODO: take mCompatScaling into account (or eliminate compat scaling)?
3108         return nGetRunAdvance(mNativePaint, text, start, end, contextStart, contextEnd, isRtl,
3109                 offset);
3110     }
3111 
3112     /**
3113      * @see #getRunAdvance(char[], int, int, int, int, boolean, int)
3114      *
3115      * @param text the text to measure. Cannot be null.
3116      * @param start the index of the start of the range to measure
3117      * @param end the index + 1 of the end of the range to measure
3118      * @param contextStart the index of the start of the shaping context
3119      * @param contextEnd the index + 1 of the end of the shaping context
3120      * @param isRtl whether the run is in RTL direction
3121      * @param offset index of caret position
3122      * @return width measurement between start and offset
3123      */
getRunAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)3124     public float getRunAdvance(CharSequence text, int start, int end, int contextStart,
3125             int contextEnd, boolean isRtl, int offset) {
3126         if (text == null) {
3127             throw new IllegalArgumentException("text cannot be null");
3128         }
3129         if ((contextStart | start | offset | end | contextEnd
3130                 | start - contextStart | offset - start | end - offset
3131                 | contextEnd - end | text.length() - contextEnd) < 0) {
3132             throw new IndexOutOfBoundsException();
3133         }
3134         if (end == start) {
3135             return 0.0f;
3136         }
3137         // TODO performance: specialized alternatives to avoid buffer copy, if win is significant
3138         char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart);
3139         TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
3140         float result = getRunAdvance(buf, start - contextStart, end - contextStart, 0,
3141                 contextEnd - contextStart, isRtl, offset - contextStart);
3142         TemporaryBuffer.recycle(buf);
3143         return result;
3144     }
3145 
3146 
3147     /**
3148      * Measure the advance of each character within a run of text and also return the cursor
3149      * position within the run.
3150      *
3151      * @see #getRunAdvance(char[], int, int, int, int, boolean, int) for more details.
3152      *
3153      * @param text the text to measure. Cannot be null.
3154      * @param start the start index of the range to measure, inclusive
3155      * @param end the end index of the range to measure, exclusive
3156      * @param contextStart the start index of the shaping context, inclusive
3157      * @param contextEnd the end index of the shaping context, exclusive
3158      * @param isRtl whether the run is in RTL direction
3159      * @param offset index of caret position
3160      * @param advances the array that receives the computed character advances
3161      * @param advancesIndex the start index from which the advances array is filled
3162      * @return width measurement between start and offset
3163      * @throws IndexOutOfBoundsException if a) contextStart or contextEnd is out of array's range
3164      * or contextStart is larger than contextEnd,
3165      * b) start or end is not within the range [contextStart, contextEnd), or start is larger than
3166      * end,
3167      * c) offset is not within the range [start, end),
3168      * d) advances.length - advanceIndex is smaller than the length of the run, which equals to
3169      * end - start.
3170      *
3171      */
getRunCharacterAdvance(@onNull char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, @Nullable float[] advances, int advancesIndex)3172     public float getRunCharacterAdvance(@NonNull char[] text, int start, int end, int contextStart,
3173             int contextEnd, boolean isRtl, int offset,
3174             @Nullable float[] advances, int advancesIndex) {
3175         if (text == null) {
3176             throw new IllegalArgumentException("text cannot be null");
3177         }
3178         if (contextStart < 0 || contextEnd > text.length) {
3179             throw new IndexOutOfBoundsException("Invalid Context Range: " + contextStart + ", "
3180                     + contextEnd + " must be in 0, " + text.length);
3181         }
3182 
3183         if (start < contextStart || contextEnd < end) {
3184             throw new IndexOutOfBoundsException("Invalid start/end range: " + start + ", " + end
3185                     + " must be in " + contextStart + ", " + contextEnd);
3186         }
3187 
3188         if (offset < start || end < offset) {
3189             throw new IndexOutOfBoundsException("Invalid offset position: " + offset
3190                     + " must be in " + start + ", " + end);
3191         }
3192 
3193         if (advances != null && advances.length < advancesIndex - start + end) {
3194             throw new IndexOutOfBoundsException("Given array doesn't have enough space to receive "
3195                     + "the result, advances.length: " + advances.length + " advanceIndex: "
3196                     + advancesIndex + " needed space: " + (offset - start));
3197         }
3198 
3199         if (end == start) {
3200             return 0.0f;
3201         }
3202 
3203         return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd,
3204                 isRtl, offset, advances, advancesIndex);
3205     }
3206 
3207     /**
3208      * @see #getRunCharacterAdvance(char[], int, int, int, int, boolean, int, float[], int)
3209      *
3210      * @param text the text to measure. Cannot be null.
3211      * @param start the index of the start of the range to measure
3212      * @param end the index + 1 of the end of the range to measure
3213      * @param contextStart the index of the start of the shaping context
3214      * @param contextEnd the index + 1 of the end of the shaping context
3215      * @param isRtl whether the run is in RTL direction
3216      * @param offset index of caret position
3217      * @param advances the array that receives the computed character advances
3218      * @param advancesIndex the start index from which the advances array is filled
3219      * @return width measurement between start and offset
3220      * @throws IndexOutOfBoundsException if a) contextStart or contextEnd is out of array's range
3221      * or contextStart is larger than contextEnd,
3222      * b) start or end is not within the range [contextStart, contextEnd), or end is larger than
3223      * start,
3224      * c) offset is not within the range [start, end),
3225      * d) advances.length - advanceIndex is smaller than the run length, which equals to
3226      * end - start.
3227      */
getRunCharacterAdvance(@onNull CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, @Nullable float[] advances, int advancesIndex)3228     public float getRunCharacterAdvance(@NonNull CharSequence text, int start, int end,
3229             int contextStart, int contextEnd, boolean isRtl, int offset,
3230             @Nullable float[] advances, int advancesIndex) {
3231         if (text == null) {
3232             throw new IllegalArgumentException("text cannot be null");
3233         }
3234         if (contextStart < 0 || contextEnd > text.length()) {
3235             throw new IndexOutOfBoundsException("Invalid Context Range: " + contextStart + ", "
3236                     + contextEnd + " must be in 0, " + text.length());
3237         }
3238 
3239         if (start < contextStart || contextEnd < end) {
3240             throw new IndexOutOfBoundsException("Invalid start/end range: " + start + ", " + end
3241                     + " must be in " + contextStart + ", " + contextEnd);
3242         }
3243 
3244         if (offset < start || end < offset) {
3245             throw new IndexOutOfBoundsException("Invalid offset position: " + offset
3246                     + " must be in " + start + ", " + end);
3247         }
3248 
3249         if (advances != null && advances.length < advancesIndex - start + end) {
3250             throw new IndexOutOfBoundsException("Given array doesn't have enough space to receive "
3251                     + "the result, advances.length: " + advances.length + " advanceIndex: "
3252                     + advancesIndex + " needed space: " + (offset - start));
3253         }
3254 
3255         if (end == start) {
3256             return 0.0f;
3257         }
3258 
3259         char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart);
3260         TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
3261         final float result = getRunCharacterAdvance(buf, start - contextStart, end - contextStart,
3262                 0, contextEnd - contextStart, isRtl, offset - contextStart,
3263                 advances, advancesIndex);
3264         TemporaryBuffer.recycle(buf);
3265         return result;
3266     }
3267 
3268     /**
3269      * Get the character offset within the string whose position is closest to the specified
3270      * horizontal position.
3271      *
3272      * <p>The returned value is generally the value of {@code offset} for which
3273      * {@link #getRunAdvance} yields a result most closely approximating {@code advance},
3274      * and which is also on a grapheme cluster boundary. As such, it is the preferred method
3275      * for positioning a cursor in response to a touch or pointer event. The grapheme cluster
3276      * boundaries are based on
3277      * <a href="http://unicode.org/reports/tr29/">Unicode Standard Annex #29</a> but with some
3278      * tailoring for better user experience.
3279      *
3280      * <p>Note that {@code advance} is a (generally positive) width measurement relative to the start
3281      * of the run. Thus, for RTL runs it the distance from the point to the right edge.
3282      *
3283      * <p>All indices are relative to the start of {@code text}. Further, {@code 0 <= contextStart
3284      * <= start <= end <= contextEnd <= text.length} must hold on entry, and {@code start <= result
3285      * <= end} will hold on return.
3286      *
3287      * @param text the text to measure. Cannot be null.
3288      * @param start the index of the start of the range to measure
3289      * @param end the index + 1 of the end of the range to measure
3290      * @param contextStart the index of the start of the shaping context
3291      * @param contextEnd the index + 1 of the end of the range to measure
3292      * @param isRtl whether the run is in RTL direction
3293      * @param advance width relative to start of run
3294      * @return index of offset
3295      */
getOffsetForAdvance(char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance)3296     public int getOffsetForAdvance(char[] text, int start, int end, int contextStart,
3297             int contextEnd, boolean isRtl, float advance) {
3298         if (text == null) {
3299             throw new IllegalArgumentException("text cannot be null");
3300         }
3301         if ((contextStart | start | end | contextEnd
3302                 | start - contextStart | end - start | contextEnd - end
3303                 | text.length - contextEnd) < 0) {
3304             throw new IndexOutOfBoundsException();
3305         }
3306         // TODO: take mCompatScaling into account (or eliminate compat scaling)?
3307         return nGetOffsetForAdvance(mNativePaint, text, start, end, contextStart, contextEnd,
3308                 isRtl, advance);
3309     }
3310 
3311     /**
3312      * @see #getOffsetForAdvance(char[], int, int, int, int, boolean, float)
3313      *
3314      * @param text the text to measure. Cannot be null.
3315      * @param start the index of the start of the range to measure
3316      * @param end the index + 1 of the end of the range to measure
3317      * @param contextStart the index of the start of the shaping context
3318      * @param contextEnd the index + 1 of the end of the range to measure
3319      * @param isRtl whether the run is in RTL direction
3320      * @param advance width relative to start of run
3321      * @return index of offset
3322      */
getOffsetForAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance)3323     public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart,
3324             int contextEnd, boolean isRtl, float advance) {
3325         if (text == null) {
3326             throw new IllegalArgumentException("text cannot be null");
3327         }
3328         if ((contextStart | start | end | contextEnd
3329                 | start - contextStart | end - start | contextEnd - end
3330                 | text.length() - contextEnd) < 0) {
3331             throw new IndexOutOfBoundsException();
3332         }
3333         // TODO performance: specialized alternatives to avoid buffer copy, if win is significant
3334         char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart);
3335         TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
3336         int result = getOffsetForAdvance(buf, start - contextStart, end - contextStart, 0,
3337                 contextEnd - contextStart, isRtl, advance) + contextStart;
3338         TemporaryBuffer.recycle(buf);
3339         return result;
3340     }
3341 
3342     /**
3343      * Returns true of the passed {@link Paint} will have the same effect on text measurement
3344      *
3345      * @param other A {@link Paint} object.
3346      * @return true if the other {@link Paint} has the same effect on text measurement.
3347      */
equalsForTextMeasurement(@onNull Paint other)3348     public boolean equalsForTextMeasurement(@NonNull Paint other) {
3349         return nEqualsForTextMeasurement(mNativePaint, other.mNativePaint);
3350     }
3351 
3352     // regular JNI
nGetNativeFinalizer()3353     private static native long nGetNativeFinalizer();
nInit()3354     private static native long nInit();
nInitWithPaint(long paint)3355     private static native long nInitWithPaint(long paint);
nBreakText(long nObject, char[] text, int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth)3356     private static native int nBreakText(long nObject, char[] text, int index, int count,
3357             float maxWidth, int bidiFlags, float[] measuredWidth);
nBreakText(long nObject, String text, boolean measureForwards, float maxWidth, int bidiFlags, float[] measuredWidth)3358     private static native int nBreakText(long nObject, String text, boolean measureForwards,
3359             float maxWidth, int bidiFlags, float[] measuredWidth);
nGetTextAdvances(long paintPtr, char[] text, int index, int count, int contextIndex, int contextCount, int bidiFlags, float[] advances, int advancesIndex)3360     private static native float nGetTextAdvances(long paintPtr, char[] text, int index, int count,
3361             int contextIndex, int contextCount, int bidiFlags, float[] advances, int advancesIndex);
nGetTextAdvances(long paintPtr, String text, int start, int end, int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex)3362     private static native float nGetTextAdvances(long paintPtr, String text, int start, int end,
3363             int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex);
nGetTextRunCursor(long paintPtr, char[] text, int contextStart, int contextLength, int dir, int offset, int cursorOpt)3364     private native int nGetTextRunCursor(long paintPtr, char[] text, int contextStart,
3365             int contextLength, int dir, int offset, int cursorOpt);
nGetTextRunCursor(long paintPtr, String text, int contextStart, int contextEnd, int dir, int offset, int cursorOpt)3366     private native int nGetTextRunCursor(long paintPtr, String text, int contextStart,
3367             int contextEnd, int dir, int offset, int cursorOpt);
nGetTextPath(long paintPtr, int bidiFlags, char[] text, int index, int count, float x, float y, long path)3368     private static native void nGetTextPath(long paintPtr, int bidiFlags, char[] text, int index,
3369             int count, float x, float y, long path);
nGetTextPath(long paintPtr, int bidiFlags, String text, int start, int end, float x, float y, long path)3370     private static native void nGetTextPath(long paintPtr, int bidiFlags, String text, int start,
3371             int end, float x, float y, long path);
nGetStringBounds(long nativePaint, String text, int start, int end, int bidiFlags, Rect bounds)3372     private static native void nGetStringBounds(long nativePaint, String text, int start, int end,
3373             int bidiFlags, Rect bounds);
nGetCharArrayBounds(long nativePaint, char[] text, int index, int count, int bidiFlags, Rect bounds)3374     private static native void nGetCharArrayBounds(long nativePaint, char[] text, int index,
3375             int count, int bidiFlags, Rect bounds);
nHasGlyph(long paintPtr, int bidiFlags, String string)3376     private static native boolean nHasGlyph(long paintPtr, int bidiFlags, String string);
nGetRunAdvance(long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)3377     private static native float nGetRunAdvance(long paintPtr, char[] text, int start, int end,
3378             int contextStart, int contextEnd, boolean isRtl, int offset);
nGetRunCharacterAdvance(long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances, int advancesIndex)3379     private static native float nGetRunCharacterAdvance(long paintPtr, char[] text, int start,
3380             int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances,
3381             int advancesIndex);
nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance)3382     private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
3383             int contextStart, int contextEnd, boolean isRtl, float advance);
nGetFontMetricsIntForText(long paintPtr, char[] text, int start, int count, int ctxStart, int ctxCount, boolean isRtl, FontMetricsInt outMetrics)3384     private static native void nGetFontMetricsIntForText(long paintPtr, char[] text,
3385             int start, int count, int ctxStart, int ctxCount, boolean isRtl,
3386             FontMetricsInt outMetrics);
nGetFontMetricsIntForText(long paintPtr, String text, int start, int count, int ctxStart, int ctxCount, boolean isRtl, FontMetricsInt outMetrics)3387     private static native void nGetFontMetricsIntForText(long paintPtr, String text,
3388             int start, int count, int ctxStart, int ctxCount, boolean isRtl,
3389             FontMetricsInt outMetrics);
3390 
3391 
3392 
3393     // ---------------- @FastNative ------------------------
3394 
3395     @FastNative
nSetTextLocales(long paintPtr, String locales)3396     private static native int nSetTextLocales(long paintPtr, String locales);
3397     @FastNative
nSetFontFeatureSettings(long paintPtr, String settings)3398     private static native void nSetFontFeatureSettings(long paintPtr, String settings);
3399     @FastNative
nGetFontMetrics(long paintPtr, FontMetrics metrics)3400     private static native float nGetFontMetrics(long paintPtr, FontMetrics metrics);
3401     @FastNative
nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi)3402     private static native int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi);
3403 
3404     // ---------------- @CriticalNative ------------------------
3405 
3406     @CriticalNative
nReset(long paintPtr)3407     private static native void nReset(long paintPtr);
3408     @CriticalNative
nSet(long paintPtrDest, long paintPtrSrc)3409     private static native void nSet(long paintPtrDest, long paintPtrSrc);
3410     @CriticalNative
nGetStyle(long paintPtr)3411     private static native int nGetStyle(long paintPtr);
3412     @CriticalNative
nSetStyle(long paintPtr, int style)3413     private static native void nSetStyle(long paintPtr, int style);
3414     @CriticalNative
nGetStrokeCap(long paintPtr)3415     private static native int nGetStrokeCap(long paintPtr);
3416     @CriticalNative
nSetStrokeCap(long paintPtr, int cap)3417     private static native void nSetStrokeCap(long paintPtr, int cap);
3418     @CriticalNative
nGetStrokeJoin(long paintPtr)3419     private static native int nGetStrokeJoin(long paintPtr);
3420     @CriticalNative
nSetStrokeJoin(long paintPtr, int join)3421     private static native void nSetStrokeJoin(long paintPtr, int join);
3422     @CriticalNative
nGetFillPath(long paintPtr, long src, long dst)3423     private static native boolean nGetFillPath(long paintPtr, long src, long dst);
3424     @CriticalNative
nSetShader(long paintPtr, long shader)3425     private static native long nSetShader(long paintPtr, long shader);
3426     @CriticalNative
nSetColorFilter(long paintPtr, long filter)3427     private static native long nSetColorFilter(long paintPtr, long filter);
3428     @CriticalNative
nSetXfermode(long paintPtr, int xfermode)3429     private static native void nSetXfermode(long paintPtr, int xfermode);
3430     @CriticalNative
nSetPathEffect(long paintPtr, long effect)3431     private static native long nSetPathEffect(long paintPtr, long effect);
3432     @CriticalNative
nSetMaskFilter(long paintPtr, long maskfilter)3433     private static native long nSetMaskFilter(long paintPtr, long maskfilter);
3434     @CriticalNative
nSetTypeface(long paintPtr, long typeface)3435     private static native void nSetTypeface(long paintPtr, long typeface);
3436     @CriticalNative
nGetTextAlign(long paintPtr)3437     private static native int nGetTextAlign(long paintPtr);
3438     @CriticalNative
nSetTextAlign(long paintPtr, int align)3439     private static native void nSetTextAlign(long paintPtr, int align);
3440     @CriticalNative
nSetTextLocalesByMinikinLocaleListId(long paintPtr, int mMinikinLocaleListId)3441     private static native void nSetTextLocalesByMinikinLocaleListId(long paintPtr,
3442             int mMinikinLocaleListId);
3443     @CriticalNative
nSetShadowLayer(long paintPtr, float radius, float dx, float dy, long colorSpaceHandle, @ColorLong long shadowColor)3444     private static native void nSetShadowLayer(long paintPtr,
3445             float radius, float dx, float dy, long colorSpaceHandle,
3446             @ColorLong long shadowColor);
3447     @CriticalNative
nHasShadowLayer(long paintPtr)3448     private static native boolean nHasShadowLayer(long paintPtr);
3449     @CriticalNative
nGetLetterSpacing(long paintPtr)3450     private static native float nGetLetterSpacing(long paintPtr);
3451     @CriticalNative
nSetLetterSpacing(long paintPtr, float letterSpacing)3452     private static native void nSetLetterSpacing(long paintPtr, float letterSpacing);
3453     @CriticalNative
nGetWordSpacing(long paintPtr)3454     private static native float nGetWordSpacing(long paintPtr);
3455     @CriticalNative
nSetWordSpacing(long paintPtr, float wordSpacing)3456     private static native void nSetWordSpacing(long paintPtr, float wordSpacing);
3457     @CriticalNative
nGetStartHyphenEdit(long paintPtr)3458     private static native int nGetStartHyphenEdit(long paintPtr);
3459     @CriticalNative
nGetEndHyphenEdit(long paintPtr)3460     private static native int nGetEndHyphenEdit(long paintPtr);
3461     @CriticalNative
nSetStartHyphenEdit(long paintPtr, int hyphen)3462     private static native void nSetStartHyphenEdit(long paintPtr, int hyphen);
3463     @CriticalNative
nSetEndHyphenEdit(long paintPtr, int hyphen)3464     private static native void nSetEndHyphenEdit(long paintPtr, int hyphen);
3465     @CriticalNative
nSetStrokeMiter(long paintPtr, float miter)3466     private static native void nSetStrokeMiter(long paintPtr, float miter);
3467     @CriticalNative
nGetStrokeMiter(long paintPtr)3468     private static native float nGetStrokeMiter(long paintPtr);
3469     @CriticalNative
nSetStrokeWidth(long paintPtr, float width)3470     private static native void nSetStrokeWidth(long paintPtr, float width);
3471     @CriticalNative
nGetStrokeWidth(long paintPtr)3472     private static native float nGetStrokeWidth(long paintPtr);
3473     @CriticalNative
nSetAlpha(long paintPtr, int a)3474     private static native void nSetAlpha(long paintPtr, int a);
3475     @CriticalNative
nSetDither(long paintPtr, boolean dither)3476     private static native void nSetDither(long paintPtr, boolean dither);
3477     @CriticalNative
nGetFlags(long paintPtr)3478     private static native int nGetFlags(long paintPtr);
3479     @CriticalNative
nSetFlags(long paintPtr, int flags)3480     private static native void nSetFlags(long paintPtr, int flags);
3481     @CriticalNative
nGetHinting(long paintPtr)3482     private static native int nGetHinting(long paintPtr);
3483     @CriticalNative
nSetHinting(long paintPtr, int mode)3484     private static native void nSetHinting(long paintPtr, int mode);
3485     @CriticalNative
nSetAntiAlias(long paintPtr, boolean aa)3486     private static native void nSetAntiAlias(long paintPtr, boolean aa);
3487     @CriticalNative
nSetLinearText(long paintPtr, boolean linearText)3488     private static native void nSetLinearText(long paintPtr, boolean linearText);
3489     @CriticalNative
nSetSubpixelText(long paintPtr, boolean subpixelText)3490     private static native void nSetSubpixelText(long paintPtr, boolean subpixelText);
3491     @CriticalNative
nSetUnderlineText(long paintPtr, boolean underlineText)3492     private static native void nSetUnderlineText(long paintPtr, boolean underlineText);
3493     @CriticalNative
nSetFakeBoldText(long paintPtr, boolean fakeBoldText)3494     private static native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
3495     @CriticalNative
nSetFilterBitmap(long paintPtr, boolean filter)3496     private static native void nSetFilterBitmap(long paintPtr, boolean filter);
3497     @CriticalNative
nSetColor(long paintPtr, long colorSpaceHandle, @ColorLong long color)3498     private static native void nSetColor(long paintPtr, long colorSpaceHandle,
3499             @ColorLong long color);
3500     @CriticalNative
nSetColor(long paintPtr, @ColorInt int color)3501     private static native void nSetColor(long paintPtr, @ColorInt int color);
3502     @CriticalNative
nSetStrikeThruText(long paintPtr, boolean strikeThruText)3503     private static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
3504     @CriticalNative
nIsElegantTextHeight(long paintPtr)3505     private static native boolean nIsElegantTextHeight(long paintPtr);
3506     @CriticalNative
nSetElegantTextHeight(long paintPtr, boolean elegant)3507     private static native void nSetElegantTextHeight(long paintPtr, boolean elegant);
3508     @CriticalNative
nGetTextSize(long paintPtr)3509     private static native float nGetTextSize(long paintPtr);
3510     @CriticalNative
nGetTextScaleX(long paintPtr)3511     private static native float nGetTextScaleX(long paintPtr);
3512     @CriticalNative
nSetTextScaleX(long paintPtr, float scaleX)3513     private static native void nSetTextScaleX(long paintPtr, float scaleX);
3514     @CriticalNative
nGetTextSkewX(long paintPtr)3515     private static native float nGetTextSkewX(long paintPtr);
3516     @CriticalNative
nSetTextSkewX(long paintPtr, float skewX)3517     private static native void nSetTextSkewX(long paintPtr, float skewX);
3518     @CriticalNative
nAscent(long paintPtr)3519     private static native float nAscent(long paintPtr);
3520     @CriticalNative
nDescent(long paintPtr)3521     private static native float nDescent(long paintPtr);
3522     @CriticalNative
nGetUnderlinePosition(long paintPtr)3523     private static native float nGetUnderlinePosition(long paintPtr);
3524     @CriticalNative
nGetUnderlineThickness(long paintPtr)3525     private static native float nGetUnderlineThickness(long paintPtr);
3526     @CriticalNative
nGetStrikeThruPosition(long paintPtr)3527     private static native float nGetStrikeThruPosition(long paintPtr);
3528     @CriticalNative
nGetStrikeThruThickness(long paintPtr)3529     private static native float nGetStrikeThruThickness(long paintPtr);
3530     @CriticalNative
nSetTextSize(long paintPtr, float textSize)3531     private static native void nSetTextSize(long paintPtr, float textSize);
3532     @CriticalNative
nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr)3533     private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
3534 }
3535