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