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.NonNull; 21 import android.annotation.Size; 22 import android.os.LocaleList; 23 import android.text.GraphicsOperations; 24 import android.text.SpannableString; 25 import android.text.SpannedString; 26 import android.text.TextUtils; 27 28 import com.android.internal.annotations.GuardedBy; 29 30 import java.util.HashMap; 31 import java.util.Locale; 32 33 import libcore.util.NativeAllocationRegistry; 34 35 /** 36 * The Paint class holds the style and color information about how to draw 37 * geometries, text and bitmaps. 38 */ 39 public class Paint { 40 41 private long mNativePaint; 42 private long mNativeShader = 0; 43 44 // The approximate size of a native paint object. 45 private static final long NATIVE_PAINT_SIZE = 98; 46 47 // Use a Holder to allow static initialization of Paint in the boot image. 48 private static class NoImagePreloadHolder { 49 public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( 50 Paint.class.getClassLoader(), nGetNativeFinalizer(), NATIVE_PAINT_SIZE); 51 } 52 53 /** 54 * @hide 55 */ 56 public long mNativeTypeface; 57 58 private ColorFilter mColorFilter; 59 private MaskFilter mMaskFilter; 60 private PathEffect mPathEffect; 61 private Rasterizer mRasterizer; 62 private Shader mShader; 63 private Typeface mTypeface; 64 private Xfermode mXfermode; 65 66 private boolean mHasCompatScaling; 67 private float mCompatScaling; 68 private float mInvCompatScaling; 69 70 private LocaleList mLocales; 71 private String mFontFeatureSettings; 72 73 private static final Object sCacheLock = new Object(); 74 75 /** 76 * Cache for the Minikin language list ID. 77 * 78 * A map from a string representation of the LocaleList to Minikin's language list ID. 79 */ 80 @GuardedBy("sCacheLock") 81 private static final HashMap<String, Integer> sMinikinLangListIdCache = new HashMap<>(); 82 83 /** 84 * @hide 85 */ 86 public int mBidiFlags = BIDI_DEFAULT_LTR; 87 88 static final Style[] sStyleArray = { 89 Style.FILL, Style.STROKE, Style.FILL_AND_STROKE 90 }; 91 static final Cap[] sCapArray = { 92 Cap.BUTT, Cap.ROUND, Cap.SQUARE 93 }; 94 static final Join[] sJoinArray = { 95 Join.MITER, Join.ROUND, Join.BEVEL 96 }; 97 static final Align[] sAlignArray = { 98 Align.LEFT, Align.CENTER, Align.RIGHT 99 }; 100 101 /** 102 * Paint flag that enables antialiasing when drawing. 103 * 104 * <p>Enabling this flag will cause all draw operations that support 105 * antialiasing to use it.</p> 106 * 107 * @see #Paint(int) 108 * @see #setFlags(int) 109 */ 110 public static final int ANTI_ALIAS_FLAG = 0x01; 111 /** 112 * Paint flag that enables bilinear sampling on scaled bitmaps. 113 * 114 * <p>If cleared, scaled bitmaps will be drawn with nearest neighbor 115 * sampling, likely resulting in artifacts. This should generally be on 116 * when drawing bitmaps, unless performance-bound (rendering to software 117 * canvas) or preferring pixelation artifacts to blurriness when scaling 118 * significantly.</p> 119 * 120 * <p>If bitmaps are scaled for device density at creation time (as 121 * resource bitmaps often are) the filtering will already have been 122 * done.</p> 123 * 124 * @see #Paint(int) 125 * @see #setFlags(int) 126 */ 127 public static final int FILTER_BITMAP_FLAG = 0x02; 128 /** 129 * Paint flag that enables dithering when blitting. 130 * 131 * <p>Enabling this flag applies a dither to any blit operation where the 132 * target's colour space is more constrained than the source. 133 * 134 * @see #Paint(int) 135 * @see #setFlags(int) 136 */ 137 public static final int DITHER_FLAG = 0x04; 138 /** 139 * Paint flag that applies an underline decoration to drawn text. 140 * 141 * @see #Paint(int) 142 * @see #setFlags(int) 143 */ 144 public static final int UNDERLINE_TEXT_FLAG = 0x08; 145 /** 146 * Paint flag that applies a strike-through decoration to drawn text. 147 * 148 * @see #Paint(int) 149 * @see #setFlags(int) 150 */ 151 public static final int STRIKE_THRU_TEXT_FLAG = 0x10; 152 /** 153 * Paint flag that applies a synthetic bolding effect to drawn text. 154 * 155 * <p>Enabling this flag will cause text draw operations to apply a 156 * simulated bold effect when drawing a {@link Typeface} that is not 157 * already bold.</p> 158 * 159 * @see #Paint(int) 160 * @see #setFlags(int) 161 */ 162 public static final int FAKE_BOLD_TEXT_FLAG = 0x20; 163 /** 164 * Paint flag that enables smooth linear scaling of text. 165 * 166 * <p>Enabling this flag does not actually scale text, but rather adjusts 167 * text draw operations to deal gracefully with smooth adjustment of scale. 168 * When this flag is enabled, font hinting is disabled to prevent shape 169 * deformation between scale factors, and glyph caching is disabled due to 170 * the large number of glyph images that will be generated.</p> 171 * 172 * <p>{@link #SUBPIXEL_TEXT_FLAG} should be used in conjunction with this 173 * flag to prevent glyph positions from snapping to whole pixel values as 174 * scale factor is adjusted.</p> 175 * 176 * @see #Paint(int) 177 * @see #setFlags(int) 178 */ 179 public static final int LINEAR_TEXT_FLAG = 0x40; 180 /** 181 * Paint flag that enables subpixel positioning of text. 182 * 183 * <p>Enabling this flag causes glyph advances to be computed with subpixel 184 * accuracy.</p> 185 * 186 * <p>This can be used with {@link #LINEAR_TEXT_FLAG} to prevent text from 187 * jittering during smooth scale transitions.</p> 188 * 189 * @see #Paint(int) 190 * @see #setFlags(int) 191 */ 192 public static final int SUBPIXEL_TEXT_FLAG = 0x80; 193 /** Legacy Paint flag, no longer used. */ 194 public static final int DEV_KERN_TEXT_FLAG = 0x100; 195 /** @hide bit mask for the flag enabling subpixel glyph rendering for text */ 196 public static final int LCD_RENDER_TEXT_FLAG = 0x200; 197 /** 198 * Paint flag that enables the use of bitmap fonts when drawing text. 199 * 200 * <p>Disabling this flag will prevent text draw operations from using 201 * embedded bitmap strikes in fonts, causing fonts with both scalable 202 * outlines and bitmap strikes to draw only the scalable outlines, and 203 * fonts with only bitmap strikes to not draw at all.</p> 204 * 205 * @see #Paint(int) 206 * @see #setFlags(int) 207 */ 208 public static final int EMBEDDED_BITMAP_TEXT_FLAG = 0x400; 209 /** @hide bit mask for the flag forcing freetype's autohinter on for text */ 210 public static final int AUTO_HINTING_TEXT_FLAG = 0x800; 211 /** @hide bit mask for the flag enabling vertical rendering for text */ 212 public static final int VERTICAL_TEXT_FLAG = 0x1000; 213 214 // These flags are always set on a new/reset paint, even if flags 0 is passed. 215 static final int HIDDEN_DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG; 216 217 /** 218 * Font hinter option that disables font hinting. 219 * 220 * @see #setHinting(int) 221 */ 222 public static final int HINTING_OFF = 0x0; 223 224 /** 225 * Font hinter option that enables font hinting. 226 * 227 * @see #setHinting(int) 228 */ 229 public static final int HINTING_ON = 0x1; 230 231 /** 232 * Bidi flag to set LTR paragraph direction. 233 * 234 * @hide 235 */ 236 public static final int BIDI_LTR = 0x0; 237 238 /** 239 * Bidi flag to set RTL paragraph direction. 240 * 241 * @hide 242 */ 243 public static final int BIDI_RTL = 0x1; 244 245 /** 246 * Bidi flag to detect paragraph direction via heuristics, defaulting to 247 * LTR. 248 * 249 * @hide 250 */ 251 public static final int BIDI_DEFAULT_LTR = 0x2; 252 253 /** 254 * Bidi flag to detect paragraph direction via heuristics, defaulting to 255 * RTL. 256 * 257 * @hide 258 */ 259 public static final int BIDI_DEFAULT_RTL = 0x3; 260 261 /** 262 * Bidi flag to override direction to all LTR (ignore bidi). 263 * 264 * @hide 265 */ 266 public static final int BIDI_FORCE_LTR = 0x4; 267 268 /** 269 * Bidi flag to override direction to all RTL (ignore bidi). 270 * 271 * @hide 272 */ 273 public static final int BIDI_FORCE_RTL = 0x5; 274 275 /** 276 * Maximum Bidi flag value. 277 * @hide 278 */ 279 private static final int BIDI_MAX_FLAG_VALUE = BIDI_FORCE_RTL; 280 281 /** 282 * Mask for bidi flags. 283 * @hide 284 */ 285 private static final int BIDI_FLAG_MASK = 0x7; 286 287 /** 288 * Flag for getTextRunAdvances indicating left-to-right run direction. 289 * @hide 290 */ 291 public static final int DIRECTION_LTR = 0; 292 293 /** 294 * Flag for getTextRunAdvances indicating right-to-left run direction. 295 * @hide 296 */ 297 public static final int DIRECTION_RTL = 1; 298 299 /** 300 * Option for getTextRunCursor to compute the valid cursor after 301 * offset or the limit of the context, whichever is less. 302 * @hide 303 */ 304 public static final int CURSOR_AFTER = 0; 305 306 /** 307 * Option for getTextRunCursor to compute the valid cursor at or after 308 * the offset or the limit of the context, whichever is less. 309 * @hide 310 */ 311 public static final int CURSOR_AT_OR_AFTER = 1; 312 313 /** 314 * Option for getTextRunCursor to compute the valid cursor before 315 * offset or the start of the context, whichever is greater. 316 * @hide 317 */ 318 public static final int CURSOR_BEFORE = 2; 319 320 /** 321 * Option for getTextRunCursor to compute the valid cursor at or before 322 * offset or the start of the context, whichever is greater. 323 * @hide 324 */ 325 public static final int CURSOR_AT_OR_BEFORE = 3; 326 327 /** 328 * Option for getTextRunCursor to return offset if the cursor at offset 329 * is valid, or -1 if it isn't. 330 * @hide 331 */ 332 public static final int CURSOR_AT = 4; 333 334 /** 335 * Maximum cursor option value. 336 */ 337 private static final int CURSOR_OPT_MAX_VALUE = CURSOR_AT; 338 339 /** 340 * The Style specifies if the primitive being drawn is filled, stroked, or 341 * both (in the same color). The default is FILL. 342 */ 343 public enum Style { 344 /** 345 * Geometry and text drawn with this style will be filled, ignoring all 346 * stroke-related settings in the paint. 347 */ 348 FILL (0), 349 /** 350 * Geometry and text drawn with this style will be stroked, respecting 351 * the stroke-related fields on the paint. 352 */ 353 STROKE (1), 354 /** 355 * Geometry and text drawn with this style will be both filled and 356 * stroked at the same time, respecting the stroke-related fields on 357 * the paint. This mode can give unexpected results if the geometry 358 * is oriented counter-clockwise. This restriction does not apply to 359 * either FILL or STROKE. 360 */ 361 FILL_AND_STROKE (2); 362 Style(int nativeInt)363 Style(int nativeInt) { 364 this.nativeInt = nativeInt; 365 } 366 final int nativeInt; 367 } 368 369 /** 370 * The Cap specifies the treatment for the beginning and ending of 371 * stroked lines and paths. The default is BUTT. 372 */ 373 public enum Cap { 374 /** 375 * The stroke ends with the path, and does not project beyond it. 376 */ 377 BUTT (0), 378 /** 379 * The stroke projects out as a semicircle, with the center at the 380 * end of the path. 381 */ 382 ROUND (1), 383 /** 384 * The stroke projects out as a square, with the center at the end 385 * of the path. 386 */ 387 SQUARE (2); 388 Cap(int nativeInt)389 private Cap(int nativeInt) { 390 this.nativeInt = nativeInt; 391 } 392 final int nativeInt; 393 } 394 395 /** 396 * The Join specifies the treatment where lines and curve segments 397 * join on a stroked path. The default is MITER. 398 */ 399 public enum Join { 400 /** 401 * The outer edges of a join meet at a sharp angle 402 */ 403 MITER (0), 404 /** 405 * The outer edges of a join meet in a circular arc. 406 */ 407 ROUND (1), 408 /** 409 * The outer edges of a join meet with a straight line 410 */ 411 BEVEL (2); 412 Join(int nativeInt)413 private Join(int nativeInt) { 414 this.nativeInt = nativeInt; 415 } 416 final int nativeInt; 417 } 418 419 /** 420 * Align specifies how drawText aligns its text relative to the 421 * [x,y] coordinates. The default is LEFT. 422 */ 423 public enum Align { 424 /** 425 * The text is drawn to the right of the x,y origin 426 */ 427 LEFT (0), 428 /** 429 * The text is drawn centered horizontally on the x,y origin 430 */ 431 CENTER (1), 432 /** 433 * The text is drawn to the left of the x,y origin 434 */ 435 RIGHT (2); 436 Align(int nativeInt)437 private Align(int nativeInt) { 438 this.nativeInt = nativeInt; 439 } 440 final int nativeInt; 441 } 442 443 /** 444 * Create a new paint with default settings. 445 */ Paint()446 public Paint() { 447 this(0); 448 } 449 450 /** 451 * Create a new paint with the specified flags. Use setFlags() to change 452 * these after the paint is created. 453 * 454 * @param flags initial flag bits, as if they were passed via setFlags(). 455 */ Paint(int flags)456 public Paint(int flags) { 457 mNativePaint = nInit(); 458 NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePaint); 459 setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS); 460 // TODO: Turning off hinting has undesirable side effects, we need to 461 // revisit hinting once we add support for subpixel positioning 462 // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV 463 // ? HINTING_OFF : HINTING_ON); 464 mCompatScaling = mInvCompatScaling = 1; 465 setTextLocales(LocaleList.getAdjustedDefault()); 466 } 467 468 /** 469 * Create a new paint, initialized with the attributes in the specified 470 * paint parameter. 471 * 472 * @param paint Existing paint used to initialized the attributes of the 473 * new paint. 474 */ Paint(Paint paint)475 public Paint(Paint paint) { 476 mNativePaint = nInitWithPaint(paint.getNativeInstance()); 477 NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePaint); 478 setClassVariablesFrom(paint); 479 } 480 481 /** Restores the paint to its default settings. */ reset()482 public void reset() { 483 nReset(mNativePaint); 484 setFlags(HIDDEN_DEFAULT_PAINT_FLAGS); 485 486 // TODO: Turning off hinting has undesirable side effects, we need to 487 // revisit hinting once we add support for subpixel positioning 488 // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV 489 // ? HINTING_OFF : HINTING_ON); 490 491 mColorFilter = null; 492 mMaskFilter = null; 493 mPathEffect = null; 494 mRasterizer = null; 495 mShader = null; 496 mNativeShader = 0; 497 mTypeface = null; 498 mNativeTypeface = 0; 499 mXfermode = null; 500 501 mHasCompatScaling = false; 502 mCompatScaling = 1; 503 mInvCompatScaling = 1; 504 505 mBidiFlags = BIDI_DEFAULT_LTR; 506 setTextLocales(LocaleList.getAdjustedDefault()); 507 setElegantTextHeight(false); 508 mFontFeatureSettings = null; 509 } 510 511 /** 512 * Copy the fields from src into this paint. This is equivalent to calling 513 * get() on all of the src fields, and calling the corresponding set() 514 * methods on this. 515 */ set(Paint src)516 public void set(Paint src) { 517 if (this != src) { 518 // copy over the native settings 519 nSet(mNativePaint, src.mNativePaint); 520 setClassVariablesFrom(src); 521 } 522 } 523 524 /** 525 * Set all class variables using current values from the given 526 * {@link Paint}. 527 */ setClassVariablesFrom(Paint paint)528 private void setClassVariablesFrom(Paint paint) { 529 mColorFilter = paint.mColorFilter; 530 mMaskFilter = paint.mMaskFilter; 531 mPathEffect = paint.mPathEffect; 532 mRasterizer = paint.mRasterizer; 533 mShader = paint.mShader; 534 mNativeShader = paint.mNativeShader; 535 mTypeface = paint.mTypeface; 536 mNativeTypeface = paint.mNativeTypeface; 537 mXfermode = paint.mXfermode; 538 539 mHasCompatScaling = paint.mHasCompatScaling; 540 mCompatScaling = paint.mCompatScaling; 541 mInvCompatScaling = paint.mInvCompatScaling; 542 543 mBidiFlags = paint.mBidiFlags; 544 mLocales = paint.mLocales; 545 mFontFeatureSettings = paint.mFontFeatureSettings; 546 } 547 548 /** @hide */ setCompatibilityScaling(float factor)549 public void setCompatibilityScaling(float factor) { 550 if (factor == 1.0) { 551 mHasCompatScaling = false; 552 mCompatScaling = mInvCompatScaling = 1.0f; 553 } else { 554 mHasCompatScaling = true; 555 mCompatScaling = factor; 556 mInvCompatScaling = 1.0f/factor; 557 } 558 } 559 560 /** 561 * Return the pointer to the native object while ensuring that any 562 * mutable objects that are attached to the paint are also up-to-date. 563 * 564 * @hide 565 */ getNativeInstance()566 public long getNativeInstance() { 567 long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance(); 568 if (newNativeShader != mNativeShader) { 569 mNativeShader = newNativeShader; 570 nSetShader(mNativePaint, mNativeShader); 571 } 572 return mNativePaint; 573 } 574 575 /** 576 * Return the bidi flags on the paint. 577 * 578 * @return the bidi flags on the paint 579 * @hide 580 */ getBidiFlags()581 public int getBidiFlags() { 582 return mBidiFlags; 583 } 584 585 /** 586 * Set the bidi flags on the paint. 587 * @hide 588 */ setBidiFlags(int flags)589 public void setBidiFlags(int flags) { 590 // only flag value is the 3-bit BIDI control setting 591 flags &= BIDI_FLAG_MASK; 592 if (flags > BIDI_MAX_FLAG_VALUE) { 593 throw new IllegalArgumentException("unknown bidi flag: " + flags); 594 } 595 mBidiFlags = flags; 596 } 597 598 /** 599 * Return the paint's flags. Use the Flag enum to test flag values. 600 * 601 * @return the paint's flags (see enums ending in _Flag for bit masks) 602 */ getFlags()603 public int getFlags() { 604 return nGetFlags(mNativePaint); 605 } 606 nGetFlags(long paintPtr)607 private native int nGetFlags(long paintPtr); 608 609 /** 610 * Set the paint's flags. Use the Flag enum to specific flag values. 611 * 612 * @param flags The new flag bits for the paint 613 */ setFlags(int flags)614 public void setFlags(int flags) { 615 nSetFlags(mNativePaint, flags); 616 } 617 nSetFlags(long paintPtr, int flags)618 private native void nSetFlags(long paintPtr, int flags); 619 620 /** 621 * Return the paint's hinting mode. Returns either 622 * {@link #HINTING_OFF} or {@link #HINTING_ON}. 623 */ getHinting()624 public int getHinting() { 625 return nGetHinting(mNativePaint); 626 } 627 nGetHinting(long paintPtr)628 private native int nGetHinting(long paintPtr); 629 630 /** 631 * Set the paint's hinting mode. May be either 632 * {@link #HINTING_OFF} or {@link #HINTING_ON}. 633 */ setHinting(int mode)634 public void setHinting(int mode) { 635 nSetHinting(mNativePaint, mode); 636 } 637 nSetHinting(long paintPtr, int mode)638 private native void nSetHinting(long paintPtr, int mode); 639 640 /** 641 * Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set 642 * AntiAliasing smooths out the edges of what is being drawn, but is has 643 * no impact on the interior of the shape. See setDither() and 644 * setFilterBitmap() to affect how colors are treated. 645 * 646 * @return true if the antialias bit is set in the paint's flags. 647 */ isAntiAlias()648 public final boolean isAntiAlias() { 649 return (getFlags() & ANTI_ALIAS_FLAG) != 0; 650 } 651 652 /** 653 * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit 654 * AntiAliasing smooths out the edges of what is being drawn, but is has 655 * no impact on the interior of the shape. See setDither() and 656 * setFilterBitmap() to affect how colors are treated. 657 * 658 * @param aa true to set the antialias bit in the flags, false to clear it 659 */ setAntiAlias(boolean aa)660 public void setAntiAlias(boolean aa) { 661 nSetAntiAlias(mNativePaint, aa); 662 } 663 nSetAntiAlias(long paintPtr, boolean aa)664 private native void nSetAntiAlias(long paintPtr, boolean aa); 665 666 /** 667 * Helper for getFlags(), returning true if DITHER_FLAG bit is set 668 * Dithering affects how colors that are higher precision than the device 669 * are down-sampled. No dithering is generally faster, but higher precision 670 * colors are just truncated down (e.g. 8888 -> 565). Dithering tries to 671 * distribute the error inherent in this process, to reduce the visual 672 * artifacts. 673 * 674 * @return true if the dithering bit is set in the paint's flags. 675 */ isDither()676 public final boolean isDither() { 677 return (getFlags() & DITHER_FLAG) != 0; 678 } 679 680 /** 681 * Helper for setFlags(), setting or clearing the DITHER_FLAG bit 682 * Dithering affects how colors that are higher precision than the device 683 * are down-sampled. No dithering is generally faster, but higher precision 684 * colors are just truncated down (e.g. 8888 -> 565). Dithering tries to 685 * distribute the error inherent in this process, to reduce the visual 686 * artifacts. 687 * 688 * @param dither true to set the dithering bit in flags, false to clear it 689 */ setDither(boolean dither)690 public void setDither(boolean dither) { 691 nSetDither(mNativePaint, dither); 692 } 693 nSetDither(long paintPtr, boolean dither)694 private native void nSetDither(long paintPtr, boolean dither); 695 696 /** 697 * Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set 698 * 699 * @return true if the lineartext bit is set in the paint's flags 700 */ isLinearText()701 public final boolean isLinearText() { 702 return (getFlags() & LINEAR_TEXT_FLAG) != 0; 703 } 704 705 /** 706 * Helper for setFlags(), setting or clearing the LINEAR_TEXT_FLAG bit 707 * 708 * @param linearText true to set the linearText bit in the paint's flags, 709 * false to clear it. 710 */ setLinearText(boolean linearText)711 public void setLinearText(boolean linearText) { 712 nSetLinearText(mNativePaint, linearText); 713 } 714 nSetLinearText(long paintPtr, boolean linearText)715 private native void nSetLinearText(long paintPtr, boolean linearText); 716 717 /** 718 * Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set 719 * 720 * @return true if the subpixel bit is set in the paint's flags 721 */ isSubpixelText()722 public final boolean isSubpixelText() { 723 return (getFlags() & SUBPIXEL_TEXT_FLAG) != 0; 724 } 725 726 /** 727 * Helper for setFlags(), setting or clearing the SUBPIXEL_TEXT_FLAG bit 728 * 729 * @param subpixelText true to set the subpixelText bit in the paint's 730 * flags, false to clear it. 731 */ setSubpixelText(boolean subpixelText)732 public void setSubpixelText(boolean subpixelText) { 733 nSetSubpixelText(mNativePaint, subpixelText); 734 } 735 nSetSubpixelText(long paintPtr, boolean subpixelText)736 private native void nSetSubpixelText(long paintPtr, boolean subpixelText); 737 738 /** 739 * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set 740 * 741 * @return true if the underlineText bit is set in the paint's flags. 742 */ isUnderlineText()743 public final boolean isUnderlineText() { 744 return (getFlags() & UNDERLINE_TEXT_FLAG) != 0; 745 } 746 747 /** 748 * Helper for setFlags(), setting or clearing the UNDERLINE_TEXT_FLAG bit 749 * 750 * @param underlineText true to set the underlineText bit in the paint's 751 * flags, false to clear it. 752 */ setUnderlineText(boolean underlineText)753 public void setUnderlineText(boolean underlineText) { 754 nSetUnderlineText(mNativePaint, underlineText); 755 } 756 nSetUnderlineText(long paintPtr, boolean underlineText)757 private native void nSetUnderlineText(long paintPtr, boolean underlineText); 758 759 /** 760 * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set 761 * 762 * @return true if the strikeThruText bit is set in the paint's flags. 763 */ isStrikeThruText()764 public final boolean isStrikeThruText() { 765 return (getFlags() & STRIKE_THRU_TEXT_FLAG) != 0; 766 } 767 768 /** 769 * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit 770 * 771 * @param strikeThruText true to set the strikeThruText bit in the paint's 772 * flags, false to clear it. 773 */ setStrikeThruText(boolean strikeThruText)774 public void setStrikeThruText(boolean strikeThruText) { 775 nSetStrikeThruText(mNativePaint, strikeThruText); 776 } 777 nSetStrikeThruText(long paintPtr, boolean strikeThruText)778 private native void nSetStrikeThruText(long paintPtr, boolean strikeThruText); 779 780 /** 781 * Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set 782 * 783 * @return true if the fakeBoldText bit is set in the paint's flags. 784 */ isFakeBoldText()785 public final boolean isFakeBoldText() { 786 return (getFlags() & FAKE_BOLD_TEXT_FLAG) != 0; 787 } 788 789 /** 790 * Helper for setFlags(), setting or clearing the FAKE_BOLD_TEXT_FLAG bit 791 * 792 * @param fakeBoldText true to set the fakeBoldText bit in the paint's 793 * flags, false to clear it. 794 */ setFakeBoldText(boolean fakeBoldText)795 public void setFakeBoldText(boolean fakeBoldText) { 796 nSetFakeBoldText(mNativePaint, fakeBoldText); 797 } 798 nSetFakeBoldText(long paintPtr, boolean fakeBoldText)799 private native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText); 800 801 /** 802 * Whether or not the bitmap filter is activated. 803 * Filtering affects the sampling of bitmaps when they are transformed. 804 * Filtering does not affect how the colors in the bitmap are converted into 805 * device pixels. That is dependent on dithering and xfermodes. 806 * 807 * @see #setFilterBitmap(boolean) setFilterBitmap() 808 */ isFilterBitmap()809 public final boolean isFilterBitmap() { 810 return (getFlags() & FILTER_BITMAP_FLAG) != 0; 811 } 812 813 /** 814 * Helper for setFlags(), setting or clearing the FILTER_BITMAP_FLAG bit. 815 * Filtering affects the sampling of bitmaps when they are transformed. 816 * Filtering does not affect how the colors in the bitmap are converted into 817 * device pixels. That is dependent on dithering and xfermodes. 818 * 819 * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's 820 * flags, false to clear it. 821 */ setFilterBitmap(boolean filter)822 public void setFilterBitmap(boolean filter) { 823 nSetFilterBitmap(mNativePaint, filter); 824 } 825 nSetFilterBitmap(long paintPtr, boolean filter)826 private native void nSetFilterBitmap(long paintPtr, boolean filter); 827 828 /** 829 * Return the paint's style, used for controlling how primitives' 830 * geometries are interpreted (except for drawBitmap, which always assumes 831 * FILL_STYLE). 832 * 833 * @return the paint's style setting (Fill, Stroke, StrokeAndFill) 834 */ getStyle()835 public Style getStyle() { 836 return sStyleArray[nGetStyle(mNativePaint)]; 837 } 838 839 /** 840 * Set the paint's style, used for controlling how primitives' 841 * geometries are interpreted (except for drawBitmap, which always assumes 842 * Fill). 843 * 844 * @param style The new style to set in the paint 845 */ setStyle(Style style)846 public void setStyle(Style style) { 847 nSetStyle(mNativePaint, style.nativeInt); 848 } 849 850 /** 851 * Return the paint's color. Note that the color is a 32bit value 852 * containing alpha as well as r,g,b. This 32bit value is not premultiplied, 853 * meaning that its alpha can be any value, regardless of the values of 854 * r,g,b. See the Color class for more details. 855 * 856 * @return the paint's color (and alpha). 857 */ 858 @ColorInt getColor()859 public int getColor() { 860 return nGetColor(mNativePaint); 861 } 862 nGetColor(long paintPtr)863 private native int nGetColor(long paintPtr); 864 865 /** 866 * Set the paint's color. Note that the color is an int containing alpha 867 * as well as r,g,b. This 32bit value is not premultiplied, meaning that 868 * its alpha can be any value, regardless of the values of r,g,b. 869 * See the Color class for more details. 870 * 871 * @param color The new color (including alpha) to set in the paint. 872 */ setColor(@olorInt int color)873 public void setColor(@ColorInt int color) { 874 nSetColor(mNativePaint, color); 875 } 876 nSetColor(long paintPtr, @ColorInt int color)877 private native void nSetColor(long paintPtr, @ColorInt int color); 878 879 /** 880 * Helper to getColor() that just returns the color's alpha value. This is 881 * the same as calling getColor() >>> 24. It always returns a value between 882 * 0 (completely transparent) and 255 (completely opaque). 883 * 884 * @return the alpha component of the paint's color. 885 */ getAlpha()886 public int getAlpha() { 887 return nGetAlpha(mNativePaint); 888 } 889 nGetAlpha(long paintPtr)890 private native int nGetAlpha(long paintPtr); 891 892 /** 893 * Helper to setColor(), that only assigns the color's alpha value, 894 * leaving its r,g,b values unchanged. Results are undefined if the alpha 895 * value is outside of the range [0..255] 896 * 897 * @param a set the alpha component [0..255] of the paint's color. 898 */ setAlpha(int a)899 public void setAlpha(int a) { 900 nSetAlpha(mNativePaint, a); 901 } 902 nSetAlpha(long paintPtr, int a)903 private native void nSetAlpha(long paintPtr, int a); 904 905 /** 906 * Helper to setColor(), that takes a,r,g,b and constructs the color int 907 * 908 * @param a The new alpha component (0..255) of the paint's color. 909 * @param r The new red component (0..255) of the paint's color. 910 * @param g The new green component (0..255) of the paint's color. 911 * @param b The new blue component (0..255) of the paint's color. 912 */ setARGB(int a, int r, int g, int b)913 public void setARGB(int a, int r, int g, int b) { 914 setColor((a << 24) | (r << 16) | (g << 8) | b); 915 } 916 917 /** 918 * Return the width for stroking. 919 * <p /> 920 * A value of 0 strokes in hairline mode. 921 * Hairlines always draws a single pixel independent of the canva's matrix. 922 * 923 * @return the paint's stroke width, used whenever the paint's style is 924 * Stroke or StrokeAndFill. 925 */ getStrokeWidth()926 public float getStrokeWidth() { 927 return nGetStrokeWidth(mNativePaint); 928 } 929 nGetStrokeWidth(long paintPtr)930 private native float nGetStrokeWidth(long paintPtr); 931 932 /** 933 * Set the width for stroking. 934 * Pass 0 to stroke in hairline mode. 935 * Hairlines always draws a single pixel independent of the canva's matrix. 936 * 937 * @param width set the paint's stroke width, used whenever the paint's 938 * style is Stroke or StrokeAndFill. 939 */ setStrokeWidth(float width)940 public void setStrokeWidth(float width) { 941 nSetStrokeWidth(mNativePaint, width); 942 } 943 nSetStrokeWidth(long paintPtr, float width)944 private native void nSetStrokeWidth(long paintPtr, float width); 945 946 /** 947 * Return the paint's stroke miter value. Used to control the behavior 948 * of miter joins when the joins angle is sharp. 949 * 950 * @return the paint's miter limit, used whenever the paint's style is 951 * Stroke or StrokeAndFill. 952 */ getStrokeMiter()953 public float getStrokeMiter() { 954 return nGetStrokeMiter(mNativePaint); 955 } 956 nGetStrokeMiter(long paintPtr)957 private native float nGetStrokeMiter(long paintPtr); 958 959 /** 960 * Set the paint's stroke miter value. This is used to control the behavior 961 * of miter joins when the joins angle is sharp. This value must be >= 0. 962 * 963 * @param miter set the miter limit on the paint, used whenever the paint's 964 * style is Stroke or StrokeAndFill. 965 */ setStrokeMiter(float miter)966 public void setStrokeMiter(float miter) { 967 nSetStrokeMiter(mNativePaint, miter); 968 } 969 nSetStrokeMiter(long paintPtr, float miter)970 private native void nSetStrokeMiter(long paintPtr, float miter); 971 972 /** 973 * Return the paint's Cap, controlling how the start and end of stroked 974 * lines and paths are treated. 975 * 976 * @return the line cap style for the paint, used whenever the paint's 977 * style is Stroke or StrokeAndFill. 978 */ getStrokeCap()979 public Cap getStrokeCap() { 980 return sCapArray[nGetStrokeCap(mNativePaint)]; 981 } 982 983 /** 984 * Set the paint's Cap. 985 * 986 * @param cap set the paint's line cap style, used whenever the paint's 987 * style is Stroke or StrokeAndFill. 988 */ setStrokeCap(Cap cap)989 public void setStrokeCap(Cap cap) { 990 nSetStrokeCap(mNativePaint, cap.nativeInt); 991 } 992 993 /** 994 * Return the paint's stroke join type. 995 * 996 * @return the paint's Join. 997 */ getStrokeJoin()998 public Join getStrokeJoin() { 999 return sJoinArray[nGetStrokeJoin(mNativePaint)]; 1000 } 1001 1002 /** 1003 * Set the paint's Join. 1004 * 1005 * @param join set the paint's Join, used whenever the paint's style is 1006 * Stroke or StrokeAndFill. 1007 */ setStrokeJoin(Join join)1008 public void setStrokeJoin(Join join) { 1009 nSetStrokeJoin(mNativePaint, join.nativeInt); 1010 } 1011 1012 /** 1013 * Applies any/all effects (patheffect, stroking) to src, returning the 1014 * result in dst. The result is that drawing src with this paint will be 1015 * the same as drawing dst with a default paint (at least from the 1016 * geometric perspective). 1017 * 1018 * @param src input path 1019 * @param dst output path (may be the same as src) 1020 * @return true if the path should be filled, or false if it should be 1021 * drawn with a hairline (width == 0) 1022 */ getFillPath(Path src, Path dst)1023 public boolean getFillPath(Path src, Path dst) { 1024 return nGetFillPath(mNativePaint, src.readOnlyNI(), dst.mutateNI()); 1025 } 1026 1027 /** 1028 * Get the paint's shader object. 1029 * 1030 * @return the paint's shader (or null) 1031 */ getShader()1032 public Shader getShader() { 1033 return mShader; 1034 } 1035 1036 /** 1037 * Set or clear the shader object. 1038 * <p /> 1039 * Pass null to clear any previous shader. 1040 * As a convenience, the parameter passed is also returned. 1041 * 1042 * @param shader May be null. the new shader to be installed in the paint 1043 * @return shader 1044 */ setShader(Shader shader)1045 public Shader setShader(Shader shader) { 1046 // If mShader changes, cached value of native shader aren't valid, since 1047 // old shader's pointer may be reused by another shader allocation later 1048 if (mShader != shader) { 1049 mNativeShader = -1; 1050 } 1051 // Defer setting the shader natively until getNativeInstance() is called 1052 mShader = shader; 1053 return shader; 1054 } 1055 1056 /** 1057 * Get the paint's colorfilter (maybe be null). 1058 * 1059 * @return the paint's colorfilter (maybe be null) 1060 */ getColorFilter()1061 public ColorFilter getColorFilter() { 1062 return mColorFilter; 1063 } 1064 1065 /** 1066 * Set or clear the paint's colorfilter, returning the parameter. 1067 * 1068 * @param filter May be null. The new filter to be installed in the paint 1069 * @return filter 1070 */ setColorFilter(ColorFilter filter)1071 public ColorFilter setColorFilter(ColorFilter filter) { 1072 long filterNative = 0; 1073 if (filter != null) 1074 filterNative = filter.native_instance; 1075 nSetColorFilter(mNativePaint, filterNative); 1076 mColorFilter = filter; 1077 return filter; 1078 } 1079 1080 /** 1081 * Get the paint's xfermode object. 1082 * 1083 * @return the paint's xfermode (or null) 1084 */ getXfermode()1085 public Xfermode getXfermode() { 1086 return mXfermode; 1087 } 1088 1089 /** 1090 * Set or clear the xfermode object. 1091 * <p /> 1092 * Pass null to clear any previous xfermode. 1093 * As a convenience, the parameter passed is also returned. 1094 * 1095 * @param xfermode May be null. The xfermode to be installed in the paint 1096 * @return xfermode 1097 */ setXfermode(Xfermode xfermode)1098 public Xfermode setXfermode(Xfermode xfermode) { 1099 long xfermodeNative = 0; 1100 if (xfermode != null) 1101 xfermodeNative = xfermode.native_instance; 1102 nSetXfermode(mNativePaint, xfermodeNative); 1103 mXfermode = xfermode; 1104 return xfermode; 1105 } 1106 1107 /** 1108 * Get the paint's patheffect object. 1109 * 1110 * @return the paint's patheffect (or null) 1111 */ getPathEffect()1112 public PathEffect getPathEffect() { 1113 return mPathEffect; 1114 } 1115 1116 /** 1117 * Set or clear the patheffect object. 1118 * <p /> 1119 * Pass null to clear any previous patheffect. 1120 * As a convenience, the parameter passed is also returned. 1121 * 1122 * @param effect May be null. The patheffect to be installed in the paint 1123 * @return effect 1124 */ setPathEffect(PathEffect effect)1125 public PathEffect setPathEffect(PathEffect effect) { 1126 long effectNative = 0; 1127 if (effect != null) { 1128 effectNative = effect.native_instance; 1129 } 1130 nSetPathEffect(mNativePaint, effectNative); 1131 mPathEffect = effect; 1132 return effect; 1133 } 1134 1135 /** 1136 * Get the paint's maskfilter object. 1137 * 1138 * @return the paint's maskfilter (or null) 1139 */ getMaskFilter()1140 public MaskFilter getMaskFilter() { 1141 return mMaskFilter; 1142 } 1143 1144 /** 1145 * Set or clear the maskfilter object. 1146 * <p /> 1147 * Pass null to clear any previous maskfilter. 1148 * As a convenience, the parameter passed is also returned. 1149 * 1150 * @param maskfilter May be null. The maskfilter to be installed in the 1151 * paint 1152 * @return maskfilter 1153 */ setMaskFilter(MaskFilter maskfilter)1154 public MaskFilter setMaskFilter(MaskFilter maskfilter) { 1155 long maskfilterNative = 0; 1156 if (maskfilter != null) { 1157 maskfilterNative = maskfilter.native_instance; 1158 } 1159 nSetMaskFilter(mNativePaint, maskfilterNative); 1160 mMaskFilter = maskfilter; 1161 return maskfilter; 1162 } 1163 1164 /** 1165 * Get the paint's typeface object. 1166 * <p /> 1167 * The typeface object identifies which font to use when drawing or 1168 * measuring text. 1169 * 1170 * @return the paint's typeface (or null) 1171 */ getTypeface()1172 public Typeface getTypeface() { 1173 return mTypeface; 1174 } 1175 1176 /** 1177 * Set or clear the typeface object. 1178 * <p /> 1179 * Pass null to clear any previous typeface. 1180 * As a convenience, the parameter passed is also returned. 1181 * 1182 * @param typeface May be null. The typeface to be installed in the paint 1183 * @return typeface 1184 */ setTypeface(Typeface typeface)1185 public Typeface setTypeface(Typeface typeface) { 1186 long typefaceNative = 0; 1187 if (typeface != null) { 1188 typefaceNative = typeface.native_instance; 1189 } 1190 nSetTypeface(mNativePaint, typefaceNative); 1191 mTypeface = typeface; 1192 mNativeTypeface = typefaceNative; 1193 return typeface; 1194 } 1195 1196 /** 1197 * Get the paint's rasterizer (or null). 1198 * <p /> 1199 * The raster controls/modifies how paths/text are turned into alpha masks. 1200 * 1201 * @return the paint's rasterizer (or null) 1202 * 1203 * @deprecated Rasterizer is not supported by either the HW or PDF backends. 1204 */ 1205 @Deprecated getRasterizer()1206 public Rasterizer getRasterizer() { 1207 return mRasterizer; 1208 } 1209 1210 /** 1211 * Set or clear the rasterizer object. 1212 * <p /> 1213 * Pass null to clear any previous rasterizer. 1214 * As a convenience, the parameter passed is also returned. 1215 * 1216 * @param rasterizer May be null. The new rasterizer to be installed in 1217 * the paint. 1218 * @return rasterizer 1219 * 1220 * @deprecated Rasterizer is not supported by either the HW or PDF backends. 1221 */ 1222 @Deprecated setRasterizer(Rasterizer rasterizer)1223 public Rasterizer setRasterizer(Rasterizer rasterizer) { 1224 long rasterizerNative = 0; 1225 if (rasterizer != null) { 1226 rasterizerNative = rasterizer.native_instance; 1227 } 1228 nSetRasterizer(mNativePaint, rasterizerNative); 1229 mRasterizer = rasterizer; 1230 return rasterizer; 1231 } 1232 1233 /** 1234 * This draws a shadow layer below the main layer, with the specified 1235 * offset and color, and blur radius. If radius is 0, then the shadow 1236 * layer is removed. 1237 * <p> 1238 * Can be used to create a blurred shadow underneath text. Support for use 1239 * with other drawing operations is constrained to the software rendering 1240 * pipeline. 1241 * <p> 1242 * The alpha of the shadow will be the paint's alpha if the shadow color is 1243 * opaque, or the alpha from the shadow color if not. 1244 */ setShadowLayer(float radius, float dx, float dy, int shadowColor)1245 public void setShadowLayer(float radius, float dx, float dy, int shadowColor) { 1246 nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor); 1247 } 1248 1249 /** 1250 * Clear the shadow layer. 1251 */ clearShadowLayer()1252 public void clearShadowLayer() { 1253 setShadowLayer(0, 0, 0, 0); 1254 } 1255 1256 /** 1257 * Checks if the paint has a shadow layer attached 1258 * 1259 * @return true if the paint has a shadow layer attached and false otherwise 1260 * @hide 1261 */ hasShadowLayer()1262 public boolean hasShadowLayer() { 1263 return nHasShadowLayer(mNativePaint); 1264 } 1265 1266 /** 1267 * Return the paint's Align value for drawing text. This controls how the 1268 * text is positioned relative to its origin. LEFT align means that all of 1269 * the text will be drawn to the right of its origin (i.e. the origin 1270 * specifieds the LEFT edge of the text) and so on. 1271 * 1272 * @return the paint's Align value for drawing text. 1273 */ getTextAlign()1274 public Align getTextAlign() { 1275 return sAlignArray[nGetTextAlign(mNativePaint)]; 1276 } 1277 1278 /** 1279 * Set the paint's text alignment. This controls how the 1280 * text is positioned relative to its origin. LEFT align means that all of 1281 * the text will be drawn to the right of its origin (i.e. the origin 1282 * specifieds the LEFT edge of the text) and so on. 1283 * 1284 * @param align set the paint's Align value for drawing text. 1285 */ setTextAlign(Align align)1286 public void setTextAlign(Align align) { 1287 nSetTextAlign(mNativePaint, align.nativeInt); 1288 } 1289 1290 /** 1291 * Get the text's primary Locale. Note that this is not all of the locale-related information 1292 * Paint has. Use {@link #getTextLocales()} to get the complete list. 1293 * 1294 * @return the paint's primary Locale used for drawing text, never null. 1295 */ 1296 @NonNull getTextLocale()1297 public Locale getTextLocale() { 1298 return mLocales.get(0); 1299 } 1300 1301 /** 1302 * Get the text locale list. 1303 * 1304 * @return the paint's LocaleList used for drawing text, never null or empty. 1305 */ 1306 @NonNull @Size(min=1) getTextLocales()1307 public LocaleList getTextLocales() { 1308 return mLocales; 1309 } 1310 1311 /** 1312 * Set the text locale list to a one-member list consisting of just the locale. 1313 * 1314 * See {@link #setTextLocales(LocaleList)} for how the locale list affects 1315 * the way the text is drawn for some languages. 1316 * 1317 * @param locale the paint's locale value for drawing text, must not be null. 1318 */ setTextLocale(@onNull Locale locale)1319 public void setTextLocale(@NonNull Locale locale) { 1320 if (locale == null) { 1321 throw new IllegalArgumentException("locale cannot be null"); 1322 } 1323 if (mLocales != null && mLocales.size() == 1 && locale.equals(mLocales.get(0))) { 1324 return; 1325 } 1326 mLocales = new LocaleList(locale); 1327 syncTextLocalesWithMinikin(); 1328 } 1329 1330 /** 1331 * Set the text locale list. 1332 * 1333 * The text locale list affects how the text is drawn for some languages. 1334 * 1335 * For example, if the locale list contains {@link Locale#CHINESE} or {@link Locale#CHINA}, 1336 * then the text renderer will prefer to draw text using a Chinese font. Likewise, 1337 * if the locale list contains {@link Locale#JAPANESE} or {@link Locale#JAPAN}, then the text 1338 * renderer will prefer to draw text using a Japanese font. If the locale list contains both, 1339 * the order those locales appear in the list is considered for deciding the font. 1340 * 1341 * This distinction is important because Chinese and Japanese text both use many 1342 * of the same Unicode code points but their appearance is subtly different for 1343 * each language. 1344 * 1345 * By default, the text locale list is initialized to a one-member list just containing the 1346 * system locales. This assumes that the text to be rendered will most likely be in the user's 1347 * preferred language. 1348 * 1349 * If the actual language or languages of the text is/are known, then they can be provided to 1350 * the text renderer using this method. The text renderer may attempt to guess the 1351 * language script based on the contents of the text to be drawn independent of 1352 * the text locale here. Specifying the text locales just helps it do a better 1353 * job in certain ambiguous cases. 1354 * 1355 * @param locales the paint's locale list for drawing text, must not be null or empty. 1356 */ setTextLocales(@onNull @izemin=1) LocaleList locales)1357 public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) { 1358 if (locales == null || locales.isEmpty()) { 1359 throw new IllegalArgumentException("locales cannot be null or empty"); 1360 } 1361 if (locales.equals(mLocales)) return; 1362 mLocales = locales; 1363 syncTextLocalesWithMinikin(); 1364 } 1365 syncTextLocalesWithMinikin()1366 private void syncTextLocalesWithMinikin() { 1367 final String languageTags = mLocales.toLanguageTags(); 1368 final Integer minikinLangListId; 1369 synchronized (sCacheLock) { 1370 minikinLangListId = sMinikinLangListIdCache.get(languageTags); 1371 if (minikinLangListId == null) { 1372 final int newID = nSetTextLocales(mNativePaint, languageTags); 1373 sMinikinLangListIdCache.put(languageTags, newID); 1374 return; 1375 } 1376 } 1377 nSetTextLocalesByMinikinLangListId(mNativePaint, minikinLangListId.intValue()); 1378 } 1379 1380 /** 1381 * Get the elegant metrics flag. 1382 * 1383 * @return true if elegant metrics are enabled for text drawing. 1384 */ isElegantTextHeight()1385 public boolean isElegantTextHeight() { 1386 return nIsElegantTextHeight(mNativePaint); 1387 } 1388 nIsElegantTextHeight(long paintPtr)1389 private native boolean nIsElegantTextHeight(long paintPtr); 1390 1391 /** 1392 * Set the paint's elegant height metrics flag. This setting selects font 1393 * variants that have not been compacted to fit Latin-based vertical 1394 * metrics, and also increases top and bottom bounds to provide more space. 1395 * 1396 * @param elegant set the paint's elegant metrics flag for drawing text. 1397 */ setElegantTextHeight(boolean elegant)1398 public void setElegantTextHeight(boolean elegant) { 1399 nSetElegantTextHeight(mNativePaint, elegant); 1400 } 1401 nSetElegantTextHeight(long paintPtr, boolean elegant)1402 private native void nSetElegantTextHeight(long paintPtr, boolean elegant); 1403 1404 /** 1405 * Return the paint's text size. 1406 * 1407 * @return the paint's text size. 1408 */ getTextSize()1409 public float getTextSize() { 1410 return nGetTextSize(mNativePaint); 1411 } 1412 nGetTextSize(long paintPtr)1413 private native float nGetTextSize(long paintPtr); 1414 1415 /** 1416 * Set the paint's text size. This value must be > 0 1417 * 1418 * @param textSize set the paint's text size. 1419 */ setTextSize(float textSize)1420 public void setTextSize(float textSize) { 1421 nSetTextSize(mNativePaint, textSize); 1422 } 1423 nSetTextSize(long paintPtr, float textSize)1424 private native void nSetTextSize(long paintPtr, float textSize); 1425 1426 /** 1427 * Return the paint's horizontal scale factor for text. The default value 1428 * is 1.0. 1429 * 1430 * @return the paint's scale factor in X for drawing/measuring text 1431 */ getTextScaleX()1432 public float getTextScaleX() { 1433 return nGetTextScaleX(mNativePaint); 1434 } 1435 nGetTextScaleX(long paintPtr)1436 private native float nGetTextScaleX(long paintPtr); 1437 1438 /** 1439 * Set the paint's horizontal scale factor for text. The default value 1440 * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will 1441 * stretch the text narrower. 1442 * 1443 * @param scaleX set the paint's scale in X for drawing/measuring text. 1444 */ setTextScaleX(float scaleX)1445 public void setTextScaleX(float scaleX) { 1446 nSetTextScaleX(mNativePaint, scaleX); 1447 } 1448 nSetTextScaleX(long paintPtr, float scaleX)1449 private native void nSetTextScaleX(long paintPtr, float scaleX); 1450 1451 /** 1452 * Return the paint's horizontal skew factor for text. The default value 1453 * is 0. 1454 * 1455 * @return the paint's skew factor in X for drawing text. 1456 */ getTextSkewX()1457 public float getTextSkewX() { 1458 return nGetTextSkewX(mNativePaint); 1459 } 1460 nGetTextSkewX(long paintPtr)1461 private native float nGetTextSkewX(long paintPtr); 1462 1463 /** 1464 * Set the paint's horizontal skew factor for text. The default value 1465 * is 0. For approximating oblique text, use values around -0.25. 1466 * 1467 * @param skewX set the paint's skew factor in X for drawing text. 1468 */ setTextSkewX(float skewX)1469 public void setTextSkewX(float skewX) { 1470 nSetTextSkewX(mNativePaint, skewX); 1471 } 1472 nSetTextSkewX(long paintPtr, float skewX)1473 private native void nSetTextSkewX(long paintPtr, float skewX); 1474 1475 /** 1476 * Return the paint's letter-spacing for text. The default value 1477 * is 0. 1478 * 1479 * @return the paint's letter-spacing for drawing text. 1480 */ getLetterSpacing()1481 public float getLetterSpacing() { 1482 return nGetLetterSpacing(mNativePaint); 1483 } 1484 1485 /** 1486 * Set the paint's letter-spacing for text. The default value 1487 * is 0. The value is in 'EM' units. Typical values for slight 1488 * expansion will be around 0.05. Negative values tighten text. 1489 * 1490 * @param letterSpacing set the paint's letter-spacing for drawing text. 1491 */ setLetterSpacing(float letterSpacing)1492 public void setLetterSpacing(float letterSpacing) { 1493 nSetLetterSpacing(mNativePaint, letterSpacing); 1494 } 1495 1496 /** 1497 * Returns the font feature settings. The format is the same as the CSS 1498 * font-feature-settings attribute: 1499 * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings"> 1500 * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a> 1501 * 1502 * @return the paint's currently set font feature settings. Default is null. 1503 * 1504 * @see #setFontFeatureSettings(String) 1505 */ getFontFeatureSettings()1506 public String getFontFeatureSettings() { 1507 return mFontFeatureSettings; 1508 } 1509 1510 /** 1511 * Set font feature settings. 1512 * 1513 * The format is the same as the CSS font-feature-settings attribute: 1514 * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings"> 1515 * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a> 1516 * 1517 * @see #getFontFeatureSettings() 1518 * 1519 * @param settings the font feature settings string to use, may be null. 1520 */ setFontFeatureSettings(String settings)1521 public void setFontFeatureSettings(String settings) { 1522 if (settings != null && settings.equals("")) { 1523 settings = null; 1524 } 1525 if ((settings == null && mFontFeatureSettings == null) 1526 || (settings != null && settings.equals(mFontFeatureSettings))) { 1527 return; 1528 } 1529 mFontFeatureSettings = settings; 1530 nSetFontFeatureSettings(mNativePaint, settings); 1531 } 1532 1533 /** 1534 * Get the current value of hyphen edit. 1535 * 1536 * @return the current hyphen edit value 1537 * 1538 * @hide 1539 */ getHyphenEdit()1540 public int getHyphenEdit() { 1541 return nGetHyphenEdit(mNativePaint); 1542 } 1543 1544 /** 1545 * Set a hyphen edit on the paint (causes a hyphen to be added to text when 1546 * measured or drawn). 1547 * 1548 * @param hyphen 0 for no edit, 1 for adding a hyphen (other values in future) 1549 * 1550 * @hide 1551 */ setHyphenEdit(int hyphen)1552 public void setHyphenEdit(int hyphen) { 1553 nSetHyphenEdit(mNativePaint, hyphen); 1554 } 1555 1556 /** 1557 * Return the distance above (negative) the baseline (ascent) based on the 1558 * current typeface and text size. 1559 * 1560 * @return the distance above (negative) the baseline (ascent) based on the 1561 * current typeface and text size. 1562 */ ascent()1563 public float ascent() { 1564 return nAscent(mNativePaint, mNativeTypeface); 1565 } 1566 nAscent(long paintPtr, long typefacePtr)1567 private native float nAscent(long paintPtr, long typefacePtr); 1568 1569 /** 1570 * Return the distance below (positive) the baseline (descent) based on the 1571 * current typeface and text size. 1572 * 1573 * @return the distance below (positive) the baseline (descent) based on 1574 * the current typeface and text size. 1575 */ descent()1576 public float descent() { 1577 return nDescent(mNativePaint, mNativeTypeface); 1578 } 1579 nDescent(long paintPtr, long typefacePtr)1580 private native float nDescent(long paintPtr, long typefacePtr); 1581 1582 /** 1583 * Class that describes the various metrics for a font at a given text size. 1584 * Remember, Y values increase going down, so those values will be positive, 1585 * and values that measure distances going up will be negative. This class 1586 * is returned by getFontMetrics(). 1587 */ 1588 public static class FontMetrics { 1589 /** 1590 * The maximum distance above the baseline for the tallest glyph in 1591 * the font at a given text size. 1592 */ 1593 public float top; 1594 /** 1595 * The recommended distance above the baseline for singled spaced text. 1596 */ 1597 public float ascent; 1598 /** 1599 * The recommended distance below the baseline for singled spaced text. 1600 */ 1601 public float descent; 1602 /** 1603 * The maximum distance below the baseline for the lowest glyph in 1604 * the font at a given text size. 1605 */ 1606 public float bottom; 1607 /** 1608 * The recommended additional space to add between lines of text. 1609 */ 1610 public float leading; 1611 } 1612 1613 /** 1614 * Return the font's recommended interline spacing, given the Paint's 1615 * settings for typeface, textSize, etc. If metrics is not null, return the 1616 * fontmetric values in it. 1617 * 1618 * @param metrics If this object is not null, its fields are filled with 1619 * the appropriate values given the paint's text attributes. 1620 * @return the font's recommended interline spacing. 1621 */ getFontMetrics(FontMetrics metrics)1622 public float getFontMetrics(FontMetrics metrics) { 1623 return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics); 1624 } 1625 nGetFontMetrics(long paintPtr, long typefacePtr, FontMetrics metrics)1626 private native float nGetFontMetrics(long paintPtr, 1627 long typefacePtr, FontMetrics metrics); 1628 1629 /** 1630 * Allocates a new FontMetrics object, and then calls getFontMetrics(fm) 1631 * with it, returning the object. 1632 */ getFontMetrics()1633 public FontMetrics getFontMetrics() { 1634 FontMetrics fm = new FontMetrics(); 1635 getFontMetrics(fm); 1636 return fm; 1637 } 1638 1639 /** 1640 * Convenience method for callers that want to have FontMetrics values as 1641 * integers. 1642 */ 1643 public static class FontMetricsInt { 1644 public int top; 1645 public int ascent; 1646 public int descent; 1647 public int bottom; 1648 public int leading; 1649 toString()1650 @Override public String toString() { 1651 return "FontMetricsInt: top=" + top + " ascent=" + ascent + 1652 " descent=" + descent + " bottom=" + bottom + 1653 " leading=" + leading; 1654 } 1655 } 1656 1657 /** 1658 * Return the font's interline spacing, given the Paint's settings for 1659 * typeface, textSize, etc. If metrics is not null, return the fontmetric 1660 * values in it. Note: all values have been converted to integers from 1661 * floats, in such a way has to make the answers useful for both spacing 1662 * and clipping. If you want more control over the rounding, call 1663 * getFontMetrics(). 1664 * 1665 * @return the font's interline spacing. 1666 */ getFontMetricsInt(FontMetricsInt fmi)1667 public int getFontMetricsInt(FontMetricsInt fmi) { 1668 return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi); 1669 } 1670 nGetFontMetricsInt(long paintPtr, long typefacePtr, FontMetricsInt fmi)1671 private native int nGetFontMetricsInt(long paintPtr, 1672 long typefacePtr, FontMetricsInt fmi); 1673 getFontMetricsInt()1674 public FontMetricsInt getFontMetricsInt() { 1675 FontMetricsInt fm = new FontMetricsInt(); 1676 getFontMetricsInt(fm); 1677 return fm; 1678 } 1679 1680 /** 1681 * Return the recommend line spacing based on the current typeface and 1682 * text size. 1683 * 1684 * @return recommend line spacing based on the current typeface and 1685 * text size. 1686 */ getFontSpacing()1687 public float getFontSpacing() { 1688 return getFontMetrics(null); 1689 } 1690 1691 /** 1692 * Return the width of the text. 1693 * 1694 * @param text The text to measure. Cannot be null. 1695 * @param index The index of the first character to start measuring 1696 * @param count THe number of characters to measure, beginning with start 1697 * @return The width of the text 1698 */ measureText(char[] text, int index, int count)1699 public float measureText(char[] text, int index, int count) { 1700 if (text == null) { 1701 throw new IllegalArgumentException("text cannot be null"); 1702 } 1703 if ((index | count) < 0 || index + count > text.length) { 1704 throw new ArrayIndexOutOfBoundsException(); 1705 } 1706 1707 if (text.length == 0 || count == 0) { 1708 return 0f; 1709 } 1710 if (!mHasCompatScaling) { 1711 return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text, 1712 index, count, index, count, mBidiFlags, null, 0)); 1713 } 1714 1715 final float oldSize = getTextSize(); 1716 setTextSize(oldSize * mCompatScaling); 1717 float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, 1718 count, mBidiFlags, null, 0); 1719 setTextSize(oldSize); 1720 return (float) Math.ceil(w*mInvCompatScaling); 1721 } 1722 1723 /** 1724 * Return the width of the text. 1725 * 1726 * @param text The text to measure. Cannot be null. 1727 * @param start The index of the first character to start measuring 1728 * @param end 1 beyond the index of the last character to measure 1729 * @return The width of the text 1730 */ measureText(String text, int start, int end)1731 public float measureText(String text, int start, int end) { 1732 if (text == null) { 1733 throw new IllegalArgumentException("text cannot be null"); 1734 } 1735 if ((start | end | (end - start) | (text.length() - end)) < 0) { 1736 throw new IndexOutOfBoundsException(); 1737 } 1738 1739 if (text.length() == 0 || start == end) { 1740 return 0f; 1741 } 1742 if (!mHasCompatScaling) { 1743 return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text, 1744 start, end, start, end, mBidiFlags, null, 0)); 1745 } 1746 final float oldSize = getTextSize(); 1747 setTextSize(oldSize * mCompatScaling); 1748 float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, 1749 end, mBidiFlags, null, 0); 1750 setTextSize(oldSize); 1751 return (float) Math.ceil(w * mInvCompatScaling); 1752 } 1753 1754 /** 1755 * Return the width of the text. 1756 * 1757 * @param text The text to measure. Cannot be null. 1758 * @return The width of the text 1759 */ measureText(String text)1760 public float measureText(String text) { 1761 if (text == null) { 1762 throw new IllegalArgumentException("text cannot be null"); 1763 } 1764 return measureText(text, 0, text.length()); 1765 } 1766 1767 /** 1768 * Return the width of the text. 1769 * 1770 * @param text The text to measure 1771 * @param start The index of the first character to start measuring 1772 * @param end 1 beyond the index of the last character to measure 1773 * @return The width of the text 1774 */ measureText(CharSequence text, int start, int end)1775 public float measureText(CharSequence text, int start, int end) { 1776 if (text == null) { 1777 throw new IllegalArgumentException("text cannot be null"); 1778 } 1779 if ((start | end | (end - start) | (text.length() - end)) < 0) { 1780 throw new IndexOutOfBoundsException(); 1781 } 1782 1783 if (text.length() == 0 || start == end) { 1784 return 0f; 1785 } 1786 if (text instanceof String) { 1787 return measureText((String)text, start, end); 1788 } 1789 if (text instanceof SpannedString || 1790 text instanceof SpannableString) { 1791 return measureText(text.toString(), start, end); 1792 } 1793 if (text instanceof GraphicsOperations) { 1794 return ((GraphicsOperations)text).measureText(start, end, this); 1795 } 1796 1797 char[] buf = TemporaryBuffer.obtain(end - start); 1798 TextUtils.getChars(text, start, end, buf, 0); 1799 float result = measureText(buf, 0, end - start); 1800 TemporaryBuffer.recycle(buf); 1801 return result; 1802 } 1803 1804 /** 1805 * Measure the text, stopping early if the measured width exceeds maxWidth. 1806 * Return the number of chars that were measured, and if measuredWidth is 1807 * not null, return in it the actual width measured. 1808 * 1809 * @param text The text to measure. Cannot be null. 1810 * @param index The offset into text to begin measuring at 1811 * @param count The number of maximum number of entries to measure. If count 1812 * is negative, then the characters are measured in reverse order. 1813 * @param maxWidth The maximum width to accumulate. 1814 * @param measuredWidth Optional. If not null, returns the actual width 1815 * measured. 1816 * @return The number of chars that were measured. Will always be <= 1817 * abs(count). 1818 */ breakText(char[] text, int index, int count, float maxWidth, float[] measuredWidth)1819 public int breakText(char[] text, int index, int count, 1820 float maxWidth, float[] measuredWidth) { 1821 if (text == null) { 1822 throw new IllegalArgumentException("text cannot be null"); 1823 } 1824 if (index < 0 || text.length - index < Math.abs(count)) { 1825 throw new ArrayIndexOutOfBoundsException(); 1826 } 1827 1828 if (text.length == 0 || count == 0) { 1829 return 0; 1830 } 1831 if (!mHasCompatScaling) { 1832 return nBreakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth, 1833 mBidiFlags, measuredWidth); 1834 } 1835 1836 final float oldSize = getTextSize(); 1837 setTextSize(oldSize * mCompatScaling); 1838 int res = nBreakText(mNativePaint, mNativeTypeface, text, index, count, 1839 maxWidth * mCompatScaling, mBidiFlags, measuredWidth); 1840 setTextSize(oldSize); 1841 if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling; 1842 return res; 1843 } 1844 nBreakText(long nObject, long nTypeface, char[] text, int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth)1845 private static native int nBreakText(long nObject, long nTypeface, 1846 char[] text, int index, int count, 1847 float maxWidth, int bidiFlags, float[] measuredWidth); 1848 1849 /** 1850 * Measure the text, stopping early if the measured width exceeds maxWidth. 1851 * Return the number of chars that were measured, and if measuredWidth is 1852 * not null, return in it the actual width measured. 1853 * 1854 * @param text The text to measure. Cannot be null. 1855 * @param start The offset into text to begin measuring at 1856 * @param end The end of the text slice to measure. 1857 * @param measureForwards If true, measure forwards, starting at start. 1858 * Otherwise, measure backwards, starting with end. 1859 * @param maxWidth The maximum width to accumulate. 1860 * @param measuredWidth Optional. If not null, returns the actual width 1861 * measured. 1862 * @return The number of chars that were measured. Will always be <= 1863 * abs(end - start). 1864 */ breakText(CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measuredWidth)1865 public int breakText(CharSequence text, int start, int end, 1866 boolean measureForwards, 1867 float maxWidth, float[] measuredWidth) { 1868 if (text == null) { 1869 throw new IllegalArgumentException("text cannot be null"); 1870 } 1871 if ((start | end | (end - start) | (text.length() - end)) < 0) { 1872 throw new IndexOutOfBoundsException(); 1873 } 1874 1875 if (text.length() == 0 || start == end) { 1876 return 0; 1877 } 1878 if (start == 0 && text instanceof String && end == text.length()) { 1879 return breakText((String) text, measureForwards, maxWidth, 1880 measuredWidth); 1881 } 1882 1883 char[] buf = TemporaryBuffer.obtain(end - start); 1884 int result; 1885 1886 TextUtils.getChars(text, start, end, buf, 0); 1887 1888 if (measureForwards) { 1889 result = breakText(buf, 0, end - start, maxWidth, measuredWidth); 1890 } else { 1891 result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth); 1892 } 1893 1894 TemporaryBuffer.recycle(buf); 1895 return result; 1896 } 1897 1898 /** 1899 * Measure the text, stopping early if the measured width exceeds maxWidth. 1900 * Return the number of chars that were measured, and if measuredWidth is 1901 * not null, return in it the actual width measured. 1902 * 1903 * @param text The text to measure. Cannot be null. 1904 * @param measureForwards If true, measure forwards, starting with the 1905 * first character in the string. Otherwise, 1906 * measure backwards, starting with the 1907 * last character in the string. 1908 * @param maxWidth The maximum width to accumulate. 1909 * @param measuredWidth Optional. If not null, returns the actual width 1910 * measured. 1911 * @return The number of chars that were measured. Will always be <= 1912 * abs(count). 1913 */ breakText(String text, boolean measureForwards, float maxWidth, float[] measuredWidth)1914 public int breakText(String text, boolean measureForwards, 1915 float maxWidth, float[] measuredWidth) { 1916 if (text == null) { 1917 throw new IllegalArgumentException("text cannot be null"); 1918 } 1919 1920 if (text.length() == 0) { 1921 return 0; 1922 } 1923 if (!mHasCompatScaling) { 1924 return nBreakText(mNativePaint, mNativeTypeface, text, measureForwards, 1925 maxWidth, mBidiFlags, measuredWidth); 1926 } 1927 1928 final float oldSize = getTextSize(); 1929 setTextSize(oldSize*mCompatScaling); 1930 int res = nBreakText(mNativePaint, mNativeTypeface, text, measureForwards, 1931 maxWidth*mCompatScaling, mBidiFlags, measuredWidth); 1932 setTextSize(oldSize); 1933 if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling; 1934 return res; 1935 } 1936 nBreakText(long nObject, long nTypeface, String text, boolean measureForwards, float maxWidth, int bidiFlags, float[] measuredWidth)1937 private static native int nBreakText(long nObject, long nTypeface, 1938 String text, boolean measureForwards, 1939 float maxWidth, int bidiFlags, float[] measuredWidth); 1940 1941 /** 1942 * Return the advance widths for the characters in the string. 1943 * 1944 * @param text The text to measure. Cannot be null. 1945 * @param index The index of the first char to to measure 1946 * @param count The number of chars starting with index to measure 1947 * @param widths array to receive the advance widths of the characters. 1948 * Must be at least a large as count. 1949 * @return the actual number of widths returned. 1950 */ getTextWidths(char[] text, int index, int count, float[] widths)1951 public int getTextWidths(char[] text, int index, int count, 1952 float[] widths) { 1953 if (text == null) { 1954 throw new IllegalArgumentException("text cannot be null"); 1955 } 1956 if ((index | count) < 0 || index + count > text.length 1957 || count > widths.length) { 1958 throw new ArrayIndexOutOfBoundsException(); 1959 } 1960 1961 if (text.length == 0 || count == 0) { 1962 return 0; 1963 } 1964 if (!mHasCompatScaling) { 1965 nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count, 1966 mBidiFlags, widths, 0); 1967 return count; 1968 } 1969 1970 final float oldSize = getTextSize(); 1971 setTextSize(oldSize * mCompatScaling); 1972 nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count, 1973 mBidiFlags, widths, 0); 1974 setTextSize(oldSize); 1975 for (int i = 0; i < count; i++) { 1976 widths[i] *= mInvCompatScaling; 1977 } 1978 return count; 1979 } 1980 1981 /** 1982 * Return the advance widths for the characters in the string. 1983 * 1984 * @param text The text to measure. Cannot be null. 1985 * @param start The index of the first char to to measure 1986 * @param end The end of the text slice to measure 1987 * @param widths array to receive the advance widths of the characters. 1988 * Must be at least a large as (end - start). 1989 * @return the actual number of widths returned. 1990 */ getTextWidths(CharSequence text, int start, int end, float[] widths)1991 public int getTextWidths(CharSequence text, int start, int end, 1992 float[] widths) { 1993 if (text == null) { 1994 throw new IllegalArgumentException("text cannot be null"); 1995 } 1996 if ((start | end | (end - start) | (text.length() - end)) < 0) { 1997 throw new IndexOutOfBoundsException(); 1998 } 1999 if (end - start > widths.length) { 2000 throw new ArrayIndexOutOfBoundsException(); 2001 } 2002 2003 if (text.length() == 0 || start == end) { 2004 return 0; 2005 } 2006 if (text instanceof String) { 2007 return getTextWidths((String) text, start, end, widths); 2008 } 2009 if (text instanceof SpannedString || 2010 text instanceof SpannableString) { 2011 return getTextWidths(text.toString(), start, end, widths); 2012 } 2013 if (text instanceof GraphicsOperations) { 2014 return ((GraphicsOperations) text).getTextWidths(start, end, 2015 widths, this); 2016 } 2017 2018 char[] buf = TemporaryBuffer.obtain(end - start); 2019 TextUtils.getChars(text, start, end, buf, 0); 2020 int result = getTextWidths(buf, 0, end - start, widths); 2021 TemporaryBuffer.recycle(buf); 2022 return result; 2023 } 2024 2025 /** 2026 * Return the advance widths for the characters in the string. 2027 * 2028 * @param text The text to measure. Cannot be null. 2029 * @param start The index of the first char to to measure 2030 * @param end The end of the text slice to measure 2031 * @param widths array to receive the advance widths of the characters. 2032 * Must be at least a large as the text. 2033 * @return the number of code units in the specified text. 2034 */ getTextWidths(String text, int start, int end, float[] widths)2035 public int getTextWidths(String text, int start, int end, float[] widths) { 2036 if (text == null) { 2037 throw new IllegalArgumentException("text cannot be null"); 2038 } 2039 if ((start | end | (end - start) | (text.length() - end)) < 0) { 2040 throw new IndexOutOfBoundsException(); 2041 } 2042 if (end - start > widths.length) { 2043 throw new ArrayIndexOutOfBoundsException(); 2044 } 2045 2046 if (text.length() == 0 || start == end) { 2047 return 0; 2048 } 2049 if (!mHasCompatScaling) { 2050 nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end, 2051 mBidiFlags, widths, 0); 2052 return end - start; 2053 } 2054 2055 final float oldSize = getTextSize(); 2056 setTextSize(oldSize * mCompatScaling); 2057 nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end, 2058 mBidiFlags, widths, 0); 2059 setTextSize(oldSize); 2060 for (int i = 0; i < end - start; i++) { 2061 widths[i] *= mInvCompatScaling; 2062 } 2063 return end - start; 2064 } 2065 2066 /** 2067 * Return the advance widths for the characters in the string. 2068 * 2069 * @param text The text to measure 2070 * @param widths array to receive the advance widths of the characters. 2071 * Must be at least a large as the text. 2072 * @return the number of code units in the specified text. 2073 */ getTextWidths(String text, float[] widths)2074 public int getTextWidths(String text, float[] widths) { 2075 return getTextWidths(text, 0, text.length(), widths); 2076 } 2077 2078 /** 2079 * Convenience overload that takes a char array instead of a 2080 * String. 2081 * 2082 * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int) 2083 * @hide 2084 */ getTextRunAdvances(char[] chars, int index, int count, int contextIndex, int contextCount, boolean isRtl, float[] advances, int advancesIndex)2085 public float getTextRunAdvances(char[] chars, int index, int count, 2086 int contextIndex, int contextCount, boolean isRtl, float[] advances, 2087 int advancesIndex) { 2088 2089 if (chars == null) { 2090 throw new IllegalArgumentException("text cannot be null"); 2091 } 2092 if ((index | count | contextIndex | contextCount | advancesIndex 2093 | (index - contextIndex) | (contextCount - count) 2094 | ((contextIndex + contextCount) - (index + count)) 2095 | (chars.length - (contextIndex + contextCount)) 2096 | (advances == null ? 0 : 2097 (advances.length - (advancesIndex + count)))) < 0) { 2098 throw new IndexOutOfBoundsException(); 2099 } 2100 2101 if (chars.length == 0 || count == 0){ 2102 return 0f; 2103 } 2104 if (!mHasCompatScaling) { 2105 return nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count, 2106 contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, 2107 advancesIndex); 2108 } 2109 2110 final float oldSize = getTextSize(); 2111 setTextSize(oldSize * mCompatScaling); 2112 float res = nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count, 2113 contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, 2114 advancesIndex); 2115 setTextSize(oldSize); 2116 2117 if (advances != null) { 2118 for (int i = advancesIndex, e = i + count; i < e; i++) { 2119 advances[i] *= mInvCompatScaling; 2120 } 2121 } 2122 return res * mInvCompatScaling; // assume errors are not significant 2123 } 2124 2125 /** 2126 * Convenience overload that takes a CharSequence instead of a 2127 * String. 2128 * 2129 * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int) 2130 * @hide 2131 */ getTextRunAdvances(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float[] advances, int advancesIndex)2132 public float getTextRunAdvances(CharSequence text, int start, int end, 2133 int contextStart, int contextEnd, boolean isRtl, float[] advances, 2134 int advancesIndex) { 2135 if (text == null) { 2136 throw new IllegalArgumentException("text cannot be null"); 2137 } 2138 if ((start | end | contextStart | contextEnd | advancesIndex | (end - start) 2139 | (start - contextStart) | (contextEnd - end) 2140 | (text.length() - contextEnd) 2141 | (advances == null ? 0 : 2142 (advances.length - advancesIndex - (end - start)))) < 0) { 2143 throw new IndexOutOfBoundsException(); 2144 } 2145 2146 if (text instanceof String) { 2147 return getTextRunAdvances((String) text, start, end, 2148 contextStart, contextEnd, isRtl, advances, advancesIndex); 2149 } 2150 if (text instanceof SpannedString || 2151 text instanceof SpannableString) { 2152 return getTextRunAdvances(text.toString(), start, end, 2153 contextStart, contextEnd, isRtl, advances, advancesIndex); 2154 } 2155 if (text instanceof GraphicsOperations) { 2156 return ((GraphicsOperations) text).getTextRunAdvances(start, end, 2157 contextStart, contextEnd, isRtl, advances, advancesIndex, this); 2158 } 2159 if (text.length() == 0 || end == start) { 2160 return 0f; 2161 } 2162 2163 int contextLen = contextEnd - contextStart; 2164 int len = end - start; 2165 char[] buf = TemporaryBuffer.obtain(contextLen); 2166 TextUtils.getChars(text, contextStart, contextEnd, buf, 0); 2167 float result = getTextRunAdvances(buf, start - contextStart, len, 2168 0, contextLen, isRtl, advances, advancesIndex); 2169 TemporaryBuffer.recycle(buf); 2170 return result; 2171 } 2172 2173 /** 2174 * Returns the total advance width for the characters in the run 2175 * between start and end, and if advances is not null, the advance 2176 * assigned to each of these characters (java chars). 2177 * 2178 * <p>The trailing surrogate in a valid surrogate pair is assigned 2179 * an advance of 0. Thus the number of returned advances is 2180 * always equal to count, not to the number of unicode codepoints 2181 * represented by the run. 2182 * 2183 * <p>In the case of conjuncts or combining marks, the total 2184 * advance is assigned to the first logical character, and the 2185 * following characters are assigned an advance of 0. 2186 * 2187 * <p>This generates the sum of the advances of glyphs for 2188 * characters in a reordered cluster as the width of the first 2189 * logical character in the cluster, and 0 for the widths of all 2190 * other characters in the cluster. In effect, such clusters are 2191 * treated like conjuncts. 2192 * 2193 * <p>The shaping bounds limit the amount of context available 2194 * outside start and end that can be used for shaping analysis. 2195 * These bounds typically reflect changes in bidi level or font 2196 * metrics across which shaping does not occur. 2197 * 2198 * @param text the text to measure. Cannot be null. 2199 * @param start the index of the first character to measure 2200 * @param end the index past the last character to measure 2201 * @param contextStart the index of the first character to use for shaping context, 2202 * must be <= start 2203 * @param contextEnd the index past the last character to use for shaping context, 2204 * must be >= end 2205 * @param isRtl whether the run is in RTL direction 2206 * @param advances array to receive the advances, must have room for all advances, 2207 * can be null if only total advance is needed 2208 * @param advancesIndex the position in advances at which to put the 2209 * advance corresponding to the character at start 2210 * @return the total advance 2211 * 2212 * @hide 2213 */ getTextRunAdvances(String text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float[] advances, int advancesIndex)2214 public float getTextRunAdvances(String text, int start, int end, int contextStart, 2215 int contextEnd, boolean isRtl, float[] advances, int advancesIndex) { 2216 if (text == null) { 2217 throw new IllegalArgumentException("text cannot be null"); 2218 } 2219 if ((start | end | contextStart | contextEnd | advancesIndex | (end - start) 2220 | (start - contextStart) | (contextEnd - end) 2221 | (text.length() - contextEnd) 2222 | (advances == null ? 0 : 2223 (advances.length - advancesIndex - (end - start)))) < 0) { 2224 throw new IndexOutOfBoundsException(); 2225 } 2226 2227 if (text.length() == 0 || start == end) { 2228 return 0f; 2229 } 2230 2231 if (!mHasCompatScaling) { 2232 return nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, 2233 contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, 2234 advancesIndex); 2235 } 2236 2237 final float oldSize = getTextSize(); 2238 setTextSize(oldSize * mCompatScaling); 2239 float totalAdvance = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, 2240 end, contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, 2241 advancesIndex); 2242 setTextSize(oldSize); 2243 2244 if (advances != null) { 2245 for (int i = advancesIndex, e = i + (end - start); i < e; i++) { 2246 advances[i] *= mInvCompatScaling; 2247 } 2248 } 2249 return totalAdvance * mInvCompatScaling; // assume errors are insignificant 2250 } 2251 2252 /** 2253 * Returns the next cursor position in the run. This avoids placing the 2254 * cursor between surrogates, between characters that form conjuncts, 2255 * between base characters and combining marks, or within a reordering 2256 * cluster. 2257 * 2258 * <p>ContextStart and offset are relative to the start of text. 2259 * The context is the shaping context for cursor movement, generally 2260 * the bounds of the metric span enclosing the cursor in the direction of 2261 * movement. 2262 * 2263 * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid 2264 * cursor position, this returns -1. Otherwise this will never return a 2265 * value before contextStart or after contextStart + contextLength. 2266 * 2267 * @param text the text 2268 * @param contextStart the start of the context 2269 * @param contextLength the length of the context 2270 * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR} 2271 * @param offset the cursor position to move from 2272 * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER}, 2273 * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE}, 2274 * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT} 2275 * @return the offset of the next position, or -1 2276 * @hide 2277 */ getTextRunCursor(char[] text, int contextStart, int contextLength, int dir, int offset, int cursorOpt)2278 public int getTextRunCursor(char[] text, int contextStart, int contextLength, 2279 int dir, int offset, int cursorOpt) { 2280 int contextEnd = contextStart + contextLength; 2281 if (((contextStart | contextEnd | offset | (contextEnd - contextStart) 2282 | (offset - contextStart) | (contextEnd - offset) 2283 | (text.length - contextEnd) | cursorOpt) < 0) 2284 || cursorOpt > CURSOR_OPT_MAX_VALUE) { 2285 throw new IndexOutOfBoundsException(); 2286 } 2287 2288 return nGetTextRunCursor(mNativePaint, text, 2289 contextStart, contextLength, dir, offset, cursorOpt); 2290 } 2291 2292 /** 2293 * Returns the next cursor position in the run. This avoids placing the 2294 * cursor between surrogates, between characters that form conjuncts, 2295 * between base characters and combining marks, or within a reordering 2296 * cluster. 2297 * 2298 * <p>ContextStart, contextEnd, and offset are relative to the start of 2299 * text. The context is the shaping context for cursor movement, generally 2300 * the bounds of the metric span enclosing the cursor in the direction of 2301 * movement. 2302 * 2303 * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid 2304 * cursor position, this returns -1. Otherwise this will never return a 2305 * value before contextStart or after contextEnd. 2306 * 2307 * @param text the text 2308 * @param contextStart the start of the context 2309 * @param contextEnd the end of the context 2310 * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR} 2311 * @param offset the cursor position to move from 2312 * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER}, 2313 * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE}, 2314 * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT} 2315 * @return the offset of the next position, or -1 2316 * @hide 2317 */ getTextRunCursor(CharSequence text, int contextStart, int contextEnd, int dir, int offset, int cursorOpt)2318 public int getTextRunCursor(CharSequence text, int contextStart, 2319 int contextEnd, int dir, int offset, int cursorOpt) { 2320 2321 if (text instanceof String || text instanceof SpannedString || 2322 text instanceof SpannableString) { 2323 return getTextRunCursor(text.toString(), contextStart, contextEnd, 2324 dir, offset, cursorOpt); 2325 } 2326 if (text instanceof GraphicsOperations) { 2327 return ((GraphicsOperations) text).getTextRunCursor( 2328 contextStart, contextEnd, dir, offset, cursorOpt, this); 2329 } 2330 2331 int contextLen = contextEnd - contextStart; 2332 char[] buf = TemporaryBuffer.obtain(contextLen); 2333 TextUtils.getChars(text, contextStart, contextEnd, buf, 0); 2334 int relPos = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt); 2335 TemporaryBuffer.recycle(buf); 2336 return (relPos == -1) ? -1 : relPos + contextStart; 2337 } 2338 2339 /** 2340 * Returns the next cursor position in the run. This avoids placing the 2341 * cursor between surrogates, between characters that form conjuncts, 2342 * between base characters and combining marks, or within a reordering 2343 * cluster. 2344 * 2345 * <p>ContextStart, contextEnd, and offset are relative to the start of 2346 * text. The context is the shaping context for cursor movement, generally 2347 * the bounds of the metric span enclosing the cursor in the direction of 2348 * movement. 2349 * 2350 * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid 2351 * cursor position, this returns -1. Otherwise this will never return a 2352 * value before contextStart or after contextEnd. 2353 * 2354 * @param text the text 2355 * @param contextStart the start of the context 2356 * @param contextEnd the end of the context 2357 * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR} 2358 * @param offset the cursor position to move from 2359 * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER}, 2360 * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE}, 2361 * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT} 2362 * @return the offset of the next position, or -1 2363 * @hide 2364 */ getTextRunCursor(String text, int contextStart, int contextEnd, int dir, int offset, int cursorOpt)2365 public int getTextRunCursor(String text, int contextStart, int contextEnd, 2366 int dir, int offset, int cursorOpt) { 2367 if (((contextStart | contextEnd | offset | (contextEnd - contextStart) 2368 | (offset - contextStart) | (contextEnd - offset) 2369 | (text.length() - contextEnd) | cursorOpt) < 0) 2370 || cursorOpt > CURSOR_OPT_MAX_VALUE) { 2371 throw new IndexOutOfBoundsException(); 2372 } 2373 2374 return nGetTextRunCursor(mNativePaint, text, 2375 contextStart, contextEnd, dir, offset, cursorOpt); 2376 } 2377 2378 /** 2379 * Return the path (outline) for the specified text. 2380 * Note: just like Canvas.drawText, this will respect the Align setting in 2381 * the paint. 2382 * 2383 * @param text The text to retrieve the path from 2384 * @param index The index of the first character in text 2385 * @param count The number of characterss starting with index 2386 * @param x The x coordinate of the text's origin 2387 * @param y The y coordinate of the text's origin 2388 * @param path The path to receive the data describing the text. Must 2389 * be allocated by the caller. 2390 */ getTextPath(char[] text, int index, int count, float x, float y, Path path)2391 public void getTextPath(char[] text, int index, int count, 2392 float x, float y, Path path) { 2393 if ((index | count) < 0 || index + count > text.length) { 2394 throw new ArrayIndexOutOfBoundsException(); 2395 } 2396 nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y, 2397 path.mutateNI()); 2398 } 2399 2400 /** 2401 * Return the path (outline) for the specified text. 2402 * Note: just like Canvas.drawText, this will respect the Align setting 2403 * in the paint. 2404 * 2405 * @param text The text to retrieve the path from 2406 * @param start The first character in the text 2407 * @param end 1 past the last charcter in the text 2408 * @param x The x coordinate of the text's origin 2409 * @param y The y coordinate of the text's origin 2410 * @param path The path to receive the data describing the text. Must 2411 * be allocated by the caller. 2412 */ getTextPath(String text, int start, int end, float x, float y, Path path)2413 public void getTextPath(String text, int start, int end, 2414 float x, float y, Path path) { 2415 if ((start | end | (end - start) | (text.length() - end)) < 0) { 2416 throw new IndexOutOfBoundsException(); 2417 } 2418 nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y, 2419 path.mutateNI()); 2420 } 2421 2422 /** 2423 * Return in bounds (allocated by the caller) the smallest rectangle that 2424 * encloses all of the characters, with an implied origin at (0,0). 2425 * 2426 * @param text String to measure and return its bounds 2427 * @param start Index of the first char in the string to measure 2428 * @param end 1 past the last char in the string measure 2429 * @param bounds Returns the unioned bounds of all the text. Must be 2430 * allocated by the caller. 2431 */ getTextBounds(String text, int start, int end, Rect bounds)2432 public void getTextBounds(String text, int start, int end, Rect bounds) { 2433 if ((start | end | (end - start) | (text.length() - end)) < 0) { 2434 throw new IndexOutOfBoundsException(); 2435 } 2436 if (bounds == null) { 2437 throw new NullPointerException("need bounds Rect"); 2438 } 2439 nGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds); 2440 } 2441 2442 /** 2443 * Return in bounds (allocated by the caller) the smallest rectangle that 2444 * encloses all of the characters, with an implied origin at (0,0). 2445 * 2446 * @param text Array of chars to measure and return their unioned bounds 2447 * @param index Index of the first char in the array to measure 2448 * @param count The number of chars, beginning at index, to measure 2449 * @param bounds Returns the unioned bounds of all the text. Must be 2450 * allocated by the caller. 2451 */ getTextBounds(char[] text, int index, int count, Rect bounds)2452 public void getTextBounds(char[] text, int index, int count, Rect bounds) { 2453 if ((index | count) < 0 || index + count > text.length) { 2454 throw new ArrayIndexOutOfBoundsException(); 2455 } 2456 if (bounds == null) { 2457 throw new NullPointerException("need bounds Rect"); 2458 } 2459 nGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags, 2460 bounds); 2461 } 2462 2463 /** 2464 * Determine whether the typeface set on the paint has a glyph supporting the string. The 2465 * simplest case is when the string contains a single character, in which this method 2466 * determines whether the font has the character. In the case of multiple characters, the 2467 * method returns true if there is a single glyph representing the ligature. For example, if 2468 * the input is a pair of regional indicator symbols, determine whether there is an emoji flag 2469 * for the pair. 2470 * 2471 * <p>Finally, if the string contains a variation selector, the method only returns true if 2472 * the fonts contains a glyph specific to that variation. 2473 * 2474 * <p>Checking is done on the entire fallback chain, not just the immediate font referenced. 2475 * 2476 * @param string the string to test whether there is glyph support 2477 * @return true if the typeface has a glyph for the string 2478 */ hasGlyph(String string)2479 public boolean hasGlyph(String string) { 2480 return nHasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string); 2481 } 2482 2483 /** 2484 * Measure cursor position within a run of text. 2485 * 2486 * <p>The run of text includes the characters from {@code start} to {@code end} in the text. In 2487 * addition, the range {@code contextStart} to {@code contextEnd} is used as context for the 2488 * purpose of complex text shaping, such as Arabic text potentially shaped differently based on 2489 * the text next to it. 2490 * 2491 * <p>All text outside the range {@code contextStart..contextEnd} is ignored. The text between 2492 * {@code start} and {@code end} will be laid out to be measured. 2493 * 2494 * <p>The returned width measurement is the advance from {@code start} to {@code offset}. It is 2495 * generally a positive value, no matter the direction of the run. If {@code offset == end}, 2496 * the return value is simply the width of the whole run from {@code start} to {@code end}. 2497 * 2498 * <p>Ligatures are formed for characters in the range {@code start..end} (but not for 2499 * {@code start..contextStart} or {@code end..contextEnd}). If {@code offset} points to a 2500 * character in the middle of such a formed ligature, but at a grapheme cluster boundary, the 2501 * return value will also reflect an advance in the middle of the ligature. See 2502 * {@link #getOffsetForAdvance} for more discussion of grapheme cluster boundaries. 2503 * 2504 * <p>The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is 2505 * suitable only for runs of a single direction. 2506 * 2507 * <p>All indices are relative to the start of {@code text}. Further, {@code 0 <= contextStart 2508 * <= start <= offset <= end <= contextEnd <= text.length} must hold on entry. 2509 * 2510 * @param text the text to measure. Cannot be null. 2511 * @param start the index of the start of the range to measure 2512 * @param end the index + 1 of the end of the range to measure 2513 * @param contextStart the index of the start of the shaping context 2514 * @param contextEnd the index + 1 of the end of the shaping context 2515 * @param isRtl whether the run is in RTL direction 2516 * @param offset index of caret position 2517 * @return width measurement between start and offset 2518 */ getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)2519 public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd, 2520 boolean isRtl, int offset) { 2521 if (text == null) { 2522 throw new IllegalArgumentException("text cannot be null"); 2523 } 2524 if ((contextStart | start | offset | end | contextEnd 2525 | start - contextStart | offset - start | end - offset 2526 | contextEnd - end | text.length - contextEnd) < 0) { 2527 throw new IndexOutOfBoundsException(); 2528 } 2529 if (end == start) { 2530 return 0.0f; 2531 } 2532 // TODO: take mCompatScaling into account (or eliminate compat scaling)? 2533 return nGetRunAdvance(mNativePaint, mNativeTypeface, text, start, end, 2534 contextStart, contextEnd, isRtl, offset); 2535 } 2536 2537 /** 2538 * @see #getRunAdvance(char[], int, int, int, int, boolean, int) 2539 * 2540 * @param text the text to measure. Cannot be null. 2541 * @param start the index of the start of the range to measure 2542 * @param end the index + 1 of the end of the range to measure 2543 * @param contextStart the index of the start of the shaping context 2544 * @param contextEnd the index + 1 of the end of the shaping context 2545 * @param isRtl whether the run is in RTL direction 2546 * @param offset index of caret position 2547 * @return width measurement between start and offset 2548 */ getRunAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)2549 public float getRunAdvance(CharSequence text, int start, int end, int contextStart, 2550 int contextEnd, boolean isRtl, int offset) { 2551 if (text == null) { 2552 throw new IllegalArgumentException("text cannot be null"); 2553 } 2554 if ((contextStart | start | offset | end | contextEnd 2555 | start - contextStart | offset - start | end - offset 2556 | contextEnd - end | text.length() - contextEnd) < 0) { 2557 throw new IndexOutOfBoundsException(); 2558 } 2559 if (end == start) { 2560 return 0.0f; 2561 } 2562 // TODO performance: specialized alternatives to avoid buffer copy, if win is significant 2563 char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart); 2564 TextUtils.getChars(text, contextStart, contextEnd, buf, 0); 2565 float result = getRunAdvance(buf, start - contextStart, end - contextStart, 0, 2566 contextEnd - contextStart, isRtl, offset - contextStart); 2567 TemporaryBuffer.recycle(buf); 2568 return result; 2569 } 2570 2571 /** 2572 * Get the character offset within the string whose position is closest to the specified 2573 * horizontal position. 2574 * 2575 * <p>The returned value is generally the value of {@code offset} for which 2576 * {@link #getRunAdvance} yields a result most closely approximating {@code advance}, 2577 * and which is also on a grapheme cluster boundary. As such, it is the preferred method 2578 * for positioning a cursor in response to a touch or pointer event. The grapheme cluster 2579 * boundaries are based on 2580 * <a href="http://unicode.org/reports/tr29/">Unicode Standard Annex #29</a> but with some 2581 * tailoring for better user experience. 2582 * 2583 * <p>Note that {@code advance} is a (generally positive) width measurement relative to the start 2584 * of the run. Thus, for RTL runs it the distance from the point to the right edge. 2585 * 2586 * <p>All indices are relative to the start of {@code text}. Further, {@code 0 <= contextStart 2587 * <= start <= end <= contextEnd <= text.length} must hold on entry, and {@code start <= result 2588 * <= end} will hold on return. 2589 * 2590 * @param text the text to measure. Cannot be null. 2591 * @param start the index of the start of the range to measure 2592 * @param end the index + 1 of the end of the range to measure 2593 * @param contextStart the index of the start of the shaping context 2594 * @param contextEnd the index + 1 of the end of the range to measure 2595 * @param isRtl whether the run is in RTL direction 2596 * @param advance width relative to start of run 2597 * @return index of offset 2598 */ getOffsetForAdvance(char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance)2599 public int getOffsetForAdvance(char[] text, int start, int end, int contextStart, 2600 int contextEnd, boolean isRtl, float advance) { 2601 if (text == null) { 2602 throw new IllegalArgumentException("text cannot be null"); 2603 } 2604 if ((contextStart | start | end | contextEnd 2605 | start - contextStart | end - start | contextEnd - end 2606 | text.length - contextEnd) < 0) { 2607 throw new IndexOutOfBoundsException(); 2608 } 2609 // TODO: take mCompatScaling into account (or eliminate compat scaling)? 2610 return nGetOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end, 2611 contextStart, contextEnd, isRtl, advance); 2612 } 2613 2614 /** 2615 * @see #getOffsetForAdvance(char[], int, int, int, int, boolean, float) 2616 * 2617 * @param text the text to measure. Cannot be null. 2618 * @param start the index of the start of the range to measure 2619 * @param end the index + 1 of the end of the range to measure 2620 * @param contextStart the index of the start of the shaping context 2621 * @param contextEnd the index + 1 of the end of the range to measure 2622 * @param isRtl whether the run is in RTL direction 2623 * @param advance width relative to start of run 2624 * @return index of offset 2625 */ getOffsetForAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance)2626 public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart, 2627 int contextEnd, boolean isRtl, float advance) { 2628 if (text == null) { 2629 throw new IllegalArgumentException("text cannot be null"); 2630 } 2631 if ((contextStart | start | end | contextEnd 2632 | start - contextStart | end - start | contextEnd - end 2633 | text.length() - contextEnd) < 0) { 2634 throw new IndexOutOfBoundsException(); 2635 } 2636 // TODO performance: specialized alternatives to avoid buffer copy, if win is significant 2637 char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart); 2638 TextUtils.getChars(text, contextStart, contextEnd, buf, 0); 2639 int result = getOffsetForAdvance(buf, start - contextStart, end - contextStart, 0, 2640 contextEnd - contextStart, isRtl, advance) + contextStart; 2641 TemporaryBuffer.recycle(buf); 2642 return result; 2643 } 2644 nInit()2645 private static native long nInit(); nInitWithPaint(long paint)2646 private static native long nInitWithPaint(long paint); nReset(long paintPtr)2647 private static native void nReset(long paintPtr); nSet(long paintPtrDest, long paintPtrSrc)2648 private static native void nSet(long paintPtrDest, long paintPtrSrc); nGetStyle(long paintPtr)2649 private static native int nGetStyle(long paintPtr); nSetStyle(long paintPtr, int style)2650 private static native void nSetStyle(long paintPtr, int style); nGetStrokeCap(long paintPtr)2651 private static native int nGetStrokeCap(long paintPtr); nSetStrokeCap(long paintPtr, int cap)2652 private static native void nSetStrokeCap(long paintPtr, int cap); nGetStrokeJoin(long paintPtr)2653 private static native int nGetStrokeJoin(long paintPtr); nSetStrokeJoin(long paintPtr, int join)2654 private static native void nSetStrokeJoin(long paintPtr, 2655 int join); nGetFillPath(long paintPtr, long src, long dst)2656 private static native boolean nGetFillPath(long paintPtr, 2657 long src, long dst); nSetShader(long paintPtr, long shader)2658 private static native long nSetShader(long paintPtr, long shader); nSetColorFilter(long paintPtr, long filter)2659 private static native long nSetColorFilter(long paintPtr, 2660 long filter); nSetXfermode(long paintPtr, long xfermode)2661 private static native long nSetXfermode(long paintPtr, 2662 long xfermode); nSetPathEffect(long paintPtr, long effect)2663 private static native long nSetPathEffect(long paintPtr, 2664 long effect); nSetMaskFilter(long paintPtr, long maskfilter)2665 private static native long nSetMaskFilter(long paintPtr, 2666 long maskfilter); nSetTypeface(long paintPtr, long typeface)2667 private static native long nSetTypeface(long paintPtr, 2668 long typeface); nSetRasterizer(long paintPtr, long rasterizer)2669 private static native long nSetRasterizer(long paintPtr, 2670 long rasterizer); 2671 nGetTextAlign(long paintPtr)2672 private static native int nGetTextAlign(long paintPtr); nSetTextAlign(long paintPtr, int align)2673 private static native void nSetTextAlign(long paintPtr, 2674 int align); 2675 nSetTextLocales(long paintPtr, String locales)2676 private static native int nSetTextLocales(long paintPtr, String locales); nSetTextLocalesByMinikinLangListId(long paintPtr, int mMinikinLangListId)2677 private static native void nSetTextLocalesByMinikinLangListId(long paintPtr, 2678 int mMinikinLangListId); 2679 nGetTextAdvances(long paintPtr, long typefacePtr, char[] text, int index, int count, int contextIndex, int contextCount, int bidiFlags, float[] advances, int advancesIndex)2680 private static native float nGetTextAdvances(long paintPtr, long typefacePtr, 2681 char[] text, int index, int count, int contextIndex, int contextCount, 2682 int bidiFlags, float[] advances, int advancesIndex); nGetTextAdvances(long paintPtr, long typefacePtr, String text, int start, int end, int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex)2683 private static native float nGetTextAdvances(long paintPtr, long typefacePtr, 2684 String text, int start, int end, int contextStart, int contextEnd, 2685 int bidiFlags, float[] advances, int advancesIndex); 2686 nGetTextRunCursor(long paintPtr, char[] text, int contextStart, int contextLength, int dir, int offset, int cursorOpt)2687 private native int nGetTextRunCursor(long paintPtr, char[] text, 2688 int contextStart, int contextLength, int dir, int offset, int cursorOpt); nGetTextRunCursor(long paintPtr, String text, int contextStart, int contextEnd, int dir, int offset, int cursorOpt)2689 private native int nGetTextRunCursor(long paintPtr, String text, 2690 int contextStart, int contextEnd, int dir, int offset, int cursorOpt); 2691 nGetTextPath(long paintPtr, long typefacePtr, int bidiFlags, char[] text, int index, int count, float x, float y, long path)2692 private static native void nGetTextPath(long paintPtr, long typefacePtr, 2693 int bidiFlags, char[] text, int index, int count, float x, float y, long path); nGetTextPath(long paintPtr, long typefacePtr, int bidiFlags, String text, int start, int end, float x, float y, long path)2694 private static native void nGetTextPath(long paintPtr, long typefacePtr, 2695 int bidiFlags, String text, int start, int end, float x, float y, long path); nGetStringBounds(long nativePaint, long typefacePtr, String text, int start, int end, int bidiFlags, Rect bounds)2696 private static native void nGetStringBounds(long nativePaint, long typefacePtr, 2697 String text, int start, int end, int bidiFlags, Rect bounds); nGetCharArrayBounds(long nativePaint, long typefacePtr, char[] text, int index, int count, int bidiFlags, Rect bounds)2698 private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr, 2699 char[] text, int index, int count, int bidiFlags, Rect bounds); nGetNativeFinalizer()2700 private static native long nGetNativeFinalizer(); 2701 nSetShadowLayer(long paintPtr, float radius, float dx, float dy, int color)2702 private static native void nSetShadowLayer(long paintPtr, 2703 float radius, float dx, float dy, int color); nHasShadowLayer(long paintPtr)2704 private static native boolean nHasShadowLayer(long paintPtr); 2705 nGetLetterSpacing(long paintPtr)2706 private static native float nGetLetterSpacing(long paintPtr); nSetLetterSpacing(long paintPtr, float letterSpacing)2707 private static native void nSetLetterSpacing(long paintPtr, 2708 float letterSpacing); nSetFontFeatureSettings(long paintPtr, String settings)2709 private static native void nSetFontFeatureSettings(long paintPtr, 2710 String settings); nGetHyphenEdit(long paintPtr)2711 private static native int nGetHyphenEdit(long paintPtr); nSetHyphenEdit(long paintPtr, int hyphen)2712 private static native void nSetHyphenEdit(long paintPtr, int hyphen); nHasGlyph(long paintPtr, long typefacePtr, int bidiFlags, String string)2713 private static native boolean nHasGlyph(long paintPtr, long typefacePtr, 2714 int bidiFlags, String string); nGetRunAdvance(long paintPtr, long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)2715 private static native float nGetRunAdvance(long paintPtr, long typefacePtr, 2716 char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, 2717 int offset); nGetOffsetForAdvance(long paintPtr, long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float advance)2718 private static native int nGetOffsetForAdvance(long paintPtr, 2719 long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd, 2720 boolean isRtl, float advance); 2721 } 2722