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