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