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