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