2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.graphics;
19 import android.annotation.ColorInt;
20 import android.annotation.NonNull;
21 import android.annotation.Size;
22 import android.text.GraphicsOperations;
23 import android.text.SpannableString;
24 import android.text.SpannedString;
25 import android.text.TextUtils;
26 import android.util.LocaleList;
28 import com.android.internal.annotations.GuardedBy;
30 import java.util.HashMap;
31 import java.util.Locale;
34 * The Paint class holds the style and color information about how to draw
35 * geometries, text and bitmaps.
39 private long mNativePaint;
40 private long mNativeShader = 0;
45 public long mNativeTypeface;
47 private ColorFilter mColorFilter;
48 private MaskFilter mMaskFilter;
49 private PathEffect mPathEffect;
50 private Rasterizer mRasterizer;
51 private Shader mShader;
52 private Typeface mTypeface;
53 private Xfermode mXfermode;
55 private boolean mHasCompatScaling;
56 private float mCompatScaling;
57 private float mInvCompatScaling;
59 private LocaleList mLocales;
60 private String mFontFeatureSettings;
62 private static final Object sCacheLock = new Object();
65 * Cache for the Minikin language list ID.
67 * A map from a string representation of the LocaleList to Minikin's language list ID.
69 @GuardedBy("sCacheLock")
70 private static final HashMap<String, Integer> sMinikinLangListIdCache = new HashMap<>();
75 public int mBidiFlags = BIDI_DEFAULT_LTR;
77 static final Style[] sStyleArray = {
78 Style.FILL, Style.STROKE, Style.FILL_AND_STROKE
80 static final Cap[] sCapArray = {
81 Cap.BUTT, Cap.ROUND, Cap.SQUARE
83 static final Join[] sJoinArray = {
84 Join.MITER, Join.ROUND, Join.BEVEL
86 static final Align[] sAlignArray = {
87 Align.LEFT, Align.CENTER, Align.RIGHT
91 * Paint flag that enables antialiasing when drawing.
93 * <p>Enabling this flag will cause all draw operations that support
94 * antialiasing to use it.</p>
99 public static final int ANTI_ALIAS_FLAG = 0x01;
101 * Paint flag that enables bilinear sampling on scaled bitmaps.
103 * <p>If cleared, scaled bitmaps will be drawn with nearest neighbor
104 * sampling, likely resulting in artifacts. This should generally be on
105 * when drawing bitmaps, unless performance-bound (rendering to software
106 * canvas) or preferring pixelation artifacts to blurriness when scaling
109 * <p>If bitmaps are scaled for device density at creation time (as
110 * resource bitmaps often are) the filtering will already have been
114 * @see #setFlags(int)
116 public static final int FILTER_BITMAP_FLAG = 0x02;
118 * Paint flag that enables dithering when blitting.
120 * <p>Enabling this flag applies a dither to any blit operation where the
121 * target's colour space is more constrained than the source.
124 * @see #setFlags(int)
126 public static final int DITHER_FLAG = 0x04;
128 * Paint flag that applies an underline decoration to drawn text.
131 * @see #setFlags(int)
133 public static final int UNDERLINE_TEXT_FLAG = 0x08;
135 * Paint flag that applies a strike-through decoration to drawn text.
138 * @see #setFlags(int)
140 public static final int STRIKE_THRU_TEXT_FLAG = 0x10;
142 * Paint flag that applies a synthetic bolding effect to drawn text.
144 * <p>Enabling this flag will cause text draw operations to apply a
145 * simulated bold effect when drawing a {@link Typeface} that is not
149 * @see #setFlags(int)
151 public static final int FAKE_BOLD_TEXT_FLAG = 0x20;
153 * Paint flag that enables smooth linear scaling of text.
155 * <p>Enabling this flag does not actually scale text, but rather adjusts
156 * text draw operations to deal gracefully with smooth adjustment of scale.
157 * When this flag is enabled, font hinting is disabled to prevent shape
158 * deformation between scale factors, and glyph caching is disabled due to
159 * the large number of glyph images that will be generated.</p>
161 * <p>{@link #SUBPIXEL_TEXT_FLAG} should be used in conjunction with this
162 * flag to prevent glyph positions from snapping to whole pixel values as
163 * scale factor is adjusted.</p>
166 * @see #setFlags(int)
168 public static final int LINEAR_TEXT_FLAG = 0x40;
170 * Paint flag that enables subpixel positioning of text.
172 * <p>Enabling this flag causes glyph advances to be computed with subpixel
175 * <p>This can be used with {@link #LINEAR_TEXT_FLAG} to prevent text from
176 * jittering during smooth scale transitions.</p>
179 * @see #setFlags(int)
181 public static final int SUBPIXEL_TEXT_FLAG = 0x80;
182 /** Legacy Paint flag, no longer used. */
183 public static final int DEV_KERN_TEXT_FLAG = 0x100;
184 /** @hide bit mask for the flag enabling subpixel glyph rendering for text */
185 public static final int LCD_RENDER_TEXT_FLAG = 0x200;
187 * Paint flag that enables the use of bitmap fonts when drawing text.
189 * <p>Disabling this flag will prevent text draw operations from using
190 * embedded bitmap strikes in fonts, causing fonts with both scalable
191 * outlines and bitmap strikes to draw only the scalable outlines, and
192 * fonts with only bitmap strikes to not draw at all.</p>
195 * @see #setFlags(int)
197 public static final int EMBEDDED_BITMAP_TEXT_FLAG = 0x400;
198 /** @hide bit mask for the flag forcing freetype's autohinter on for text */
199 public static final int AUTO_HINTING_TEXT_FLAG = 0x800;
200 /** @hide bit mask for the flag enabling vertical rendering for text */
201 public static final int VERTICAL_TEXT_FLAG = 0x1000;
203 // These flags are always set on a new/reset paint, even if flags 0 is passed.
204 static final int HIDDEN_DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG;
207 * Font hinter option that disables font hinting.
209 * @see #setHinting(int)
211 public static final int HINTING_OFF = 0x0;
214 * Font hinter option that enables font hinting.
216 * @see #setHinting(int)
218 public static final int HINTING_ON = 0x1;
221 * Bidi flag to set LTR paragraph direction.
225 public static final int BIDI_LTR = 0x0;
228 * Bidi flag to set RTL paragraph direction.
232 public static final int BIDI_RTL = 0x1;
235 * Bidi flag to detect paragraph direction via heuristics, defaulting to
240 public static final int BIDI_DEFAULT_LTR = 0x2;
243 * Bidi flag to detect paragraph direction via heuristics, defaulting to
248 public static final int BIDI_DEFAULT_RTL = 0x3;
251 * Bidi flag to override direction to all LTR (ignore bidi).
255 public static final int BIDI_FORCE_LTR = 0x4;
258 * Bidi flag to override direction to all RTL (ignore bidi).
262 public static final int BIDI_FORCE_RTL = 0x5;
265 * Maximum Bidi flag value.
268 private static final int BIDI_MAX_FLAG_VALUE = BIDI_FORCE_RTL;
271 * Mask for bidi flags.
274 private static final int BIDI_FLAG_MASK = 0x7;
277 * Flag for getTextRunAdvances indicating left-to-right run direction.
280 public static final int DIRECTION_LTR = 0;
283 * Flag for getTextRunAdvances indicating right-to-left run direction.
286 public static final int DIRECTION_RTL = 1;
289 * Option for getTextRunCursor to compute the valid cursor after
290 * offset or the limit of the context, whichever is less.
293 public static final int CURSOR_AFTER = 0;
296 * Option for getTextRunCursor to compute the valid cursor at or after
297 * the offset or the limit of the context, whichever is less.
300 public static final int CURSOR_AT_OR_AFTER = 1;
303 * Option for getTextRunCursor to compute the valid cursor before
304 * offset or the start of the context, whichever is greater.
307 public static final int CURSOR_BEFORE = 2;
310 * Option for getTextRunCursor to compute the valid cursor at or before
311 * offset or the start of the context, whichever is greater.
314 public static final int CURSOR_AT_OR_BEFORE = 3;
317 * Option for getTextRunCursor to return offset if the cursor at offset
318 * is valid, or -1 if it isn't.
321 public static final int CURSOR_AT = 4;
324 * Maximum cursor option value.
326 private static final int CURSOR_OPT_MAX_VALUE = CURSOR_AT;
329 * The Style specifies if the primitive being drawn is filled, stroked, or
330 * both (in the same color). The default is FILL.
334 * Geometry and text drawn with this style will be filled, ignoring all
335 * stroke-related settings in the paint.
339 * Geometry and text drawn with this style will be stroked, respecting
340 * the stroke-related fields on the paint.
344 * Geometry and text drawn with this style will be both filled and
345 * stroked at the same time, respecting the stroke-related fields on
346 * the paint. This mode can give unexpected results if the geometry
347 * is oriented counter-clockwise. This restriction does not apply to
348 * either FILL or STROKE.
352 Style(int nativeInt) {
353 this.nativeInt = nativeInt;
359 * The Cap specifies the treatment for the beginning and ending of
360 * stroked lines and paths. The default is BUTT.
364 * The stroke ends with the path, and does not project beyond it.
368 * The stroke projects out as a semicircle, with the center at the
373 * The stroke projects out as a square, with the center at the end
378 private Cap(int nativeInt) {
379 this.nativeInt = nativeInt;
385 * The Join specifies the treatment where lines and curve segments
386 * join on a stroked path. The default is MITER.
390 * The outer edges of a join meet at a sharp angle
394 * The outer edges of a join meet in a circular arc.
398 * The outer edges of a join meet with a straight line
402 private Join(int nativeInt) {
403 this.nativeInt = nativeInt;
409 * Align specifies how drawText aligns its text relative to the
410 * [x,y] coordinates. The default is LEFT.
414 * The text is drawn to the right of the x,y origin
418 * The text is drawn centered horizontally on the x,y origin
422 * The text is drawn to the left of the x,y origin
426 private Align(int nativeInt) {
427 this.nativeInt = nativeInt;
433 * Create a new paint with default settings.
440 * Create a new paint with the specified flags. Use setFlags() to change
441 * these after the paint is created.
443 * @param flags initial flag bits, as if they were passed via setFlags().
445 public Paint(int flags) {
446 mNativePaint = nInit();
447 setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
448 // TODO: Turning off hinting has undesirable side effects, we need to
449 // revisit hinting once we add support for subpixel positioning
450 // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
451 // ? HINTING_OFF : HINTING_ON);
452 mCompatScaling = mInvCompatScaling = 1;
453 setTextLocales(LocaleList.getDefault());
457 * Create a new paint, initialized with the attributes in the specified
460 * @param paint Existing paint used to initialized the attributes of the
463 public Paint(Paint paint) {
464 mNativePaint = nInitWithPaint(paint.getNativeInstance());
465 setClassVariablesFrom(paint);
468 /** Restores the paint to its default settings. */
469 public void reset() {
470 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
471 nReset(mNativePaint);
472 setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
474 // TODO: Turning off hinting has undesirable side effects, we need to
475 // revisit hinting once we add support for subpixel positioning
476 // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
477 // ? HINTING_OFF : HINTING_ON);
489 mHasCompatScaling = false;
491 mInvCompatScaling = 1;
493 mBidiFlags = BIDI_DEFAULT_LTR;
494 setTextLocales(LocaleList.getDefault());
495 setElegantTextHeight(false);
496 mFontFeatureSettings = null;
500 * Copy the fields from src into this paint. This is equivalent to calling
501 * get() on all of the src fields, and calling the corresponding set()
504 public void set(Paint src) {
505 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
506 if (src.mNativePaint == 0) throw new NullPointerException("Source is already finalized!");
508 // copy over the native settings
509 nSet(mNativePaint, src.mNativePaint);
510 setClassVariablesFrom(src);
515 * Set all class variables using current values from the given
518 private void setClassVariablesFrom(Paint paint) {
519 mColorFilter = paint.mColorFilter;
520 mMaskFilter = paint.mMaskFilter;
521 mPathEffect = paint.mPathEffect;
522 mRasterizer = paint.mRasterizer;
523 mShader = paint.mShader;
524 mNativeShader = paint.mNativeShader;
525 mTypeface = paint.mTypeface;
526 mNativeTypeface = paint.mNativeTypeface;
527 mXfermode = paint.mXfermode;
529 mHasCompatScaling = paint.mHasCompatScaling;
530 mCompatScaling = paint.mCompatScaling;
531 mInvCompatScaling = paint.mInvCompatScaling;
533 mBidiFlags = paint.mBidiFlags;
534 mLocales = paint.mLocales;
535 mFontFeatureSettings = paint.mFontFeatureSettings;
539 public void setCompatibilityScaling(float factor) {
541 mHasCompatScaling = false;
542 mCompatScaling = mInvCompatScaling = 1.0f;
544 mHasCompatScaling = true;
545 mCompatScaling = factor;
546 mInvCompatScaling = 1.0f/factor;
551 * Return the pointer to the native object while ensuring that any
552 * mutable objects that are attached to the paint are also up-to-date.
556 public long getNativeInstance() {
557 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
558 long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance();
559 if (newNativeShader != mNativeShader) {
560 mNativeShader = newNativeShader;
561 nSetShader(mNativePaint, mNativeShader);
567 * Return the bidi flags on the paint.
569 * @return the bidi flags on the paint
572 public int getBidiFlags() {
577 * Set the bidi flags on the paint.
580 public void setBidiFlags(int flags) {
581 // only flag value is the 3-bit BIDI control setting
582 flags &= BIDI_FLAG_MASK;
583 if (flags > BIDI_MAX_FLAG_VALUE) {
584 throw new IllegalArgumentException("unknown bidi flag: " + flags);
590 * Return the paint's flags. Use the Flag enum to test flag values.
592 * @return the paint's flags (see enums ending in _Flag for bit masks)
594 public int getFlags() {
595 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
596 return nGetFlags(mNativePaint);
599 private native int nGetFlags(long paintPtr);
602 * Set the paint's flags. Use the Flag enum to specific flag values.
604 * @param flags The new flag bits for the paint
606 public void setFlags(int flags) {
607 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
608 nSetFlags(mNativePaint, flags);
611 private native void nSetFlags(long paintPtr, int flags);
614 * Return the paint's hinting mode. Returns either
615 * {@link #HINTING_OFF} or {@link #HINTING_ON}.
617 public int getHinting() {
618 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
619 return nGetHinting(mNativePaint);
622 private native int nGetHinting(long paintPtr);
625 * Set the paint's hinting mode. May be either
626 * {@link #HINTING_OFF} or {@link #HINTING_ON}.
628 public void setHinting(int mode) {
629 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
630 nSetHinting(mNativePaint, mode);
633 private native void nSetHinting(long paintPtr, int mode);
636 * Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set
637 * AntiAliasing smooths out the edges of what is being drawn, but is has
638 * no impact on the interior of the shape. See setDither() and
639 * setFilterBitmap() to affect how colors are treated.
641 * @return true if the antialias bit is set in the paint's flags.
643 public final boolean isAntiAlias() {
644 return (getFlags() & ANTI_ALIAS_FLAG) != 0;
648 * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit
649 * AntiAliasing smooths out the edges of what is being drawn, but is has
650 * no impact on the interior of the shape. See setDither() and
651 * setFilterBitmap() to affect how colors are treated.
653 * @param aa true to set the antialias bit in the flags, false to clear it
655 public void setAntiAlias(boolean aa) {
656 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
657 nSetAntiAlias(mNativePaint, aa);
660 private native void nSetAntiAlias(long paintPtr, boolean aa);
663 * Helper for getFlags(), returning true if DITHER_FLAG bit is set
664 * Dithering affects how colors that are higher precision than the device
665 * are down-sampled. No dithering is generally faster, but higher precision
666 * colors are just truncated down (e.g. 8888 -> 565). Dithering tries to
667 * distribute the error inherent in this process, to reduce the visual
670 * @return true if the dithering bit is set in the paint's flags.
672 public final boolean isDither() {
673 return (getFlags() & DITHER_FLAG) != 0;
677 * Helper for setFlags(), setting or clearing the DITHER_FLAG bit
678 * Dithering affects how colors that are higher precision than the device
679 * are down-sampled. No dithering is generally faster, but higher precision
680 * colors are just truncated down (e.g. 8888 -> 565). Dithering tries to
681 * distribute the error inherent in this process, to reduce the visual
684 * @param dither true to set the dithering bit in flags, false to clear it
686 public void setDither(boolean dither) {
687 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
688 nSetDither(mNativePaint, dither);
691 private native void nSetDither(long paintPtr, boolean dither);
694 * Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set
696 * @return true if the lineartext bit is set in the paint's flags
698 public final boolean isLinearText() {
699 return (getFlags() & LINEAR_TEXT_FLAG) != 0;
703 * Helper for setFlags(), setting or clearing the LINEAR_TEXT_FLAG bit
705 * @param linearText true to set the linearText bit in the paint's flags,
708 public void setLinearText(boolean linearText) {
709 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
710 nSetLinearText(mNativePaint, linearText);
713 private native void nSetLinearText(long paintPtr, boolean linearText);
716 * Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set
718 * @return true if the subpixel bit is set in the paint's flags
720 public final boolean isSubpixelText() {
721 return (getFlags() & SUBPIXEL_TEXT_FLAG) != 0;
725 * Helper for setFlags(), setting or clearing the SUBPIXEL_TEXT_FLAG bit
727 * @param subpixelText true to set the subpixelText bit in the paint's
728 * flags, false to clear it.
730 public void setSubpixelText(boolean subpixelText) {
731 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
732 nSetSubpixelText(mNativePaint, subpixelText);
735 private native void nSetSubpixelText(long paintPtr, boolean subpixelText);
738 * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
740 * @return true if the underlineText bit is set in the paint's flags.
742 public final boolean isUnderlineText() {
743 return (getFlags() & UNDERLINE_TEXT_FLAG) != 0;
747 * Helper for setFlags(), setting or clearing the UNDERLINE_TEXT_FLAG bit
749 * @param underlineText true to set the underlineText bit in the paint's
750 * flags, false to clear it.
752 public void setUnderlineText(boolean underlineText) {
753 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
754 nSetUnderlineText(mNativePaint, underlineText);
757 private native void nSetUnderlineText(long paintPtr, boolean underlineText);
760 * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
762 * @return true if the strikeThruText bit is set in the paint's flags.
764 public final boolean isStrikeThruText() {
765 return (getFlags() & STRIKE_THRU_TEXT_FLAG) != 0;
769 * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit
771 * @param strikeThruText true to set the strikeThruText bit in the paint's
772 * flags, false to clear it.
774 public void setStrikeThruText(boolean strikeThruText) {
775 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
776 nSetStrikeThruText(mNativePaint, strikeThruText);
779 private native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
782 * Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set
784 * @return true if the fakeBoldText bit is set in the paint's flags.
786 public final boolean isFakeBoldText() {
787 return (getFlags() & FAKE_BOLD_TEXT_FLAG) != 0;
791 * Helper for setFlags(), setting or clearing the FAKE_BOLD_TEXT_FLAG bit
793 * @param fakeBoldText true to set the fakeBoldText bit in the paint's
794 * flags, false to clear it.
796 public void setFakeBoldText(boolean fakeBoldText) {
797 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
798 nSetFakeBoldText(mNativePaint, fakeBoldText);
801 private native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
804 * Whether or not the bitmap filter is activated.
805 * Filtering affects the sampling of bitmaps when they are transformed.
806 * Filtering does not affect how the colors in the bitmap are converted into
807 * device pixels. That is dependent on dithering and xfermodes.
809 * @see #setFilterBitmap(boolean) setFilterBitmap()
811 public final boolean isFilterBitmap() {
812 return (getFlags() & FILTER_BITMAP_FLAG) != 0;
816 * Helper for setFlags(), setting or clearing the FILTER_BITMAP_FLAG bit.
817 * Filtering affects the sampling of bitmaps when they are transformed.
818 * Filtering does not affect how the colors in the bitmap are converted into
819 * device pixels. That is dependent on dithering and xfermodes.
821 * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's
822 * flags, false to clear it.
824 public void setFilterBitmap(boolean filter) {
825 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
826 nSetFilterBitmap(mNativePaint, filter);
829 private native void nSetFilterBitmap(long paintPtr, boolean filter);
832 * Return the paint's style, used for controlling how primitives'
833 * geometries are interpreted (except for drawBitmap, which always assumes
836 * @return the paint's style setting (Fill, Stroke, StrokeAndFill)
838 public Style getStyle() {
839 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
840 return sStyleArray[nGetStyle(mNativePaint)];
844 * Set the paint's style, used for controlling how primitives'
845 * geometries are interpreted (except for drawBitmap, which always assumes
848 * @param style The new style to set in the paint
850 public void setStyle(Style style) {
851 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
852 nSetStyle(mNativePaint, style.nativeInt);
856 * Return the paint's color. Note that the color is a 32bit value
857 * containing alpha as well as r,g,b. This 32bit value is not premultiplied,
858 * meaning that its alpha can be any value, regardless of the values of
859 * r,g,b. See the Color class for more details.
861 * @return the paint's color (and alpha).
864 public int getColor() {
865 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
866 return nGetColor(mNativePaint);
869 private native int nGetColor(long paintPtr);
872 * Set the paint's color. Note that the color is an int containing alpha
873 * as well as r,g,b. This 32bit value is not premultiplied, meaning that
874 * its alpha can be any value, regardless of the values of r,g,b.
875 * See the Color class for more details.
877 * @param color The new color (including alpha) to set in the paint.
879 public void setColor(@ColorInt int color) {
880 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
881 nSetColor(mNativePaint, color);
884 private native void nSetColor(long paintPtr, @ColorInt int color);
887 * Helper to getColor() that just returns the color's alpha value. This is
888 * the same as calling getColor() >>> 24. It always returns a value between
889 * 0 (completely transparent) and 255 (completely opaque).
891 * @return the alpha component of the paint's color.
893 public int getAlpha() {
894 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
895 return nGetAlpha(mNativePaint);
898 private native int nGetAlpha(long paintPtr);
901 * Helper to setColor(), that only assigns the color's alpha value,
902 * leaving its r,g,b values unchanged. Results are undefined if the alpha
903 * value is outside of the range [0..255]
905 * @param a set the alpha component [0..255] of the paint's color.
907 public void setAlpha(int a) {
908 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
909 nSetAlpha(mNativePaint, a);
912 private native void nSetAlpha(long paintPtr, int a);
915 * Helper to setColor(), that takes a,r,g,b and constructs the color int
917 * @param a The new alpha component (0..255) of the paint's color.
918 * @param r The new red component (0..255) of the paint's color.
919 * @param g The new green component (0..255) of the paint's color.
920 * @param b The new blue component (0..255) of the paint's color.
922 public void setARGB(int a, int r, int g, int b) {
923 setColor((a << 24) | (r << 16) | (g << 8) | b);
927 * Return the width for stroking.
929 * A value of 0 strokes in hairline mode.
930 * Hairlines always draws a single pixel independent of the canva's matrix.
932 * @return the paint's stroke width, used whenever the paint's style is
933 * Stroke or StrokeAndFill.
935 public float getStrokeWidth() {
936 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
937 return nGetStrokeWidth(mNativePaint);
940 private native float nGetStrokeWidth(long paintPtr);
943 * Set the width for stroking.
944 * Pass 0 to stroke in hairline mode.
945 * Hairlines always draws a single pixel independent of the canva's matrix.
947 * @param width set the paint's stroke width, used whenever the paint's
948 * style is Stroke or StrokeAndFill.
950 public void setStrokeWidth(float width) {
951 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
952 nSetStrokeWidth(mNativePaint, width);
955 private native void nSetStrokeWidth(long paintPtr, float width);
958 * Return the paint's stroke miter value. Used to control the behavior
959 * of miter joins when the joins angle is sharp.
961 * @return the paint's miter limit, used whenever the paint's style is
962 * Stroke or StrokeAndFill.
964 public float getStrokeMiter() {
965 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
966 return nGetStrokeMiter(mNativePaint);
969 private native float nGetStrokeMiter(long paintPtr);
972 * Set the paint's stroke miter value. This is used to control the behavior
973 * of miter joins when the joins angle is sharp. This value must be >= 0.
975 * @param miter set the miter limit on the paint, used whenever the paint's
976 * style is Stroke or StrokeAndFill.
978 public void setStrokeMiter(float miter) {
979 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
980 nSetStrokeMiter(mNativePaint, miter);
983 private native void nSetStrokeMiter(long paintPtr, float miter);
986 * Return the paint's Cap, controlling how the start and end of stroked
987 * lines and paths are treated.
989 * @return the line cap style for the paint, used whenever the paint's
990 * style is Stroke or StrokeAndFill.
992 public Cap getStrokeCap() {
993 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
994 return sCapArray[nGetStrokeCap(mNativePaint)];
998 * Set the paint's Cap.
1000 * @param cap set the paint's line cap style, used whenever the paint's
1001 * style is Stroke or StrokeAndFill.
1003 public void setStrokeCap(Cap cap) {
1004 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1005 nSetStrokeCap(mNativePaint, cap.nativeInt);
1009 * Return the paint's stroke join type.
1011 * @return the paint's Join.
1013 public Join getStrokeJoin() {
1014 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1015 return sJoinArray[nGetStrokeJoin(mNativePaint)];
1019 * Set the paint's Join.
1021 * @param join set the paint's Join, used whenever the paint's style is
1022 * Stroke or StrokeAndFill.
1024 public void setStrokeJoin(Join join) {
1025 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1026 nSetStrokeJoin(mNativePaint, join.nativeInt);
1030 * Applies any/all effects (patheffect, stroking) to src, returning the
1031 * result in dst. The result is that drawing src with this paint will be
1032 * the same as drawing dst with a default paint (at least from the
1033 * geometric perspective).
1035 * @param src input path
1036 * @param dst output path (may be the same as src)
1037 * @return true if the path should be filled, or false if it should be
1038 * drawn with a hairline (width == 0)
1040 public boolean getFillPath(Path src, Path dst) {
1041 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1042 return nGetFillPath(mNativePaint, src.ni(), dst.ni());
1046 * Get the paint's shader object.
1048 * @return the paint's shader (or null)
1050 public Shader getShader() {
1055 * Set or clear the shader object.
1057 * Pass null to clear any previous shader.
1058 * As a convenience, the parameter passed is also returned.
1060 * @param shader May be null. the new shader to be installed in the paint
1063 public Shader setShader(Shader shader) {
1064 // Defer setting the shader natively until getNativeInstance() is called
1070 * Get the paint's colorfilter (maybe be null).
1072 * @return the paint's colorfilter (maybe be null)
1074 public ColorFilter getColorFilter() {
1075 return mColorFilter;
1079 * Set or clear the paint's colorfilter, returning the parameter.
1081 * @param filter May be null. The new filter to be installed in the paint
1084 public ColorFilter setColorFilter(ColorFilter filter) {
1085 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1086 long filterNative = 0;
1088 filterNative = filter.native_instance;
1089 nSetColorFilter(mNativePaint, filterNative);
1090 mColorFilter = filter;
1095 * Get the paint's xfermode object.
1097 * @return the paint's xfermode (or null)
1099 public Xfermode getXfermode() {
1104 * Set or clear the xfermode object.
1106 * Pass null to clear any previous xfermode.
1107 * As a convenience, the parameter passed is also returned.
1109 * @param xfermode May be null. The xfermode to be installed in the paint
1112 public Xfermode setXfermode(Xfermode xfermode) {
1113 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1114 long xfermodeNative = 0;
1115 if (xfermode != null)
1116 xfermodeNative = xfermode.native_instance;
1117 nSetXfermode(mNativePaint, xfermodeNative);
1118 mXfermode = xfermode;
1123 * Get the paint's patheffect object.
1125 * @return the paint's patheffect (or null)
1127 public PathEffect getPathEffect() {
1132 * Set or clear the patheffect object.
1134 * Pass null to clear any previous patheffect.
1135 * As a convenience, the parameter passed is also returned.
1137 * @param effect May be null. The patheffect to be installed in the paint
1140 public PathEffect setPathEffect(PathEffect effect) {
1141 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1142 long effectNative = 0;
1143 if (effect != null) {
1144 effectNative = effect.native_instance;
1146 nSetPathEffect(mNativePaint, effectNative);
1147 mPathEffect = effect;
1152 * Get the paint's maskfilter object.
1154 * @return the paint's maskfilter (or null)
1156 public MaskFilter getMaskFilter() {
1161 * Set or clear the maskfilter object.
1163 * Pass null to clear any previous maskfilter.
1164 * As a convenience, the parameter passed is also returned.
1166 * @param maskfilter May be null. The maskfilter to be installed in the
1168 * @return maskfilter
1170 public MaskFilter setMaskFilter(MaskFilter maskfilter) {
1171 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1172 long maskfilterNative = 0;
1173 if (maskfilter != null) {
1174 maskfilterNative = maskfilter.native_instance;
1176 nSetMaskFilter(mNativePaint, maskfilterNative);
1177 mMaskFilter = maskfilter;
1182 * Get the paint's typeface object.
1184 * The typeface object identifies which font to use when drawing or
1187 * @return the paint's typeface (or null)
1189 public Typeface getTypeface() {
1194 * Set or clear the typeface object.
1196 * Pass null to clear any previous typeface.
1197 * As a convenience, the parameter passed is also returned.
1199 * @param typeface May be null. The typeface to be installed in the paint
1202 public Typeface setTypeface(Typeface typeface) {
1203 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1204 long typefaceNative = 0;
1205 if (typeface != null) {
1206 typefaceNative = typeface.native_instance;
1208 nSetTypeface(mNativePaint, typefaceNative);
1209 mTypeface = typeface;
1210 mNativeTypeface = typefaceNative;
1215 * Get the paint's rasterizer (or null).
1217 * The raster controls/modifies how paths/text are turned into alpha masks.
1219 * @return the paint's rasterizer (or null)
1221 * @deprecated Rasterizer is not supported by either the HW or PDF backends.
1224 public Rasterizer getRasterizer() {
1229 * Set or clear the rasterizer object.
1231 * Pass null to clear any previous rasterizer.
1232 * As a convenience, the parameter passed is also returned.
1234 * @param rasterizer May be null. The new rasterizer to be installed in
1236 * @return rasterizer
1238 * @deprecated Rasterizer is not supported by either the HW or PDF backends.
1241 public Rasterizer setRasterizer(Rasterizer rasterizer) {
1242 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1243 long rasterizerNative = 0;
1244 if (rasterizer != null) {
1245 rasterizerNative = rasterizer.native_instance;
1247 nSetRasterizer(mNativePaint, rasterizerNative);
1248 mRasterizer = rasterizer;
1253 * This draws a shadow layer below the main layer, with the specified
1254 * offset and color, and blur radius. If radius is 0, then the shadow
1257 * Can be used to create a blurred shadow underneath text. Support for use
1258 * with other drawing operations is constrained to the software rendering
1261 * The alpha of the shadow will be the paint's alpha if the shadow color is
1262 * opaque, or the alpha from the shadow color if not.
1264 public void setShadowLayer(float radius, float dx, float dy, int shadowColor) {
1265 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1266 nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
1270 * Clear the shadow layer.
1272 public void clearShadowLayer() {
1273 setShadowLayer(0, 0, 0, 0);
1277 * Checks if the paint has a shadow layer attached
1279 * @return true if the paint has a shadow layer attached and false otherwise
1282 public boolean hasShadowLayer() {
1283 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1284 return nHasShadowLayer(mNativePaint);
1288 * Return the paint's Align value for drawing text. This controls how the
1289 * text is positioned relative to its origin. LEFT align means that all of
1290 * the text will be drawn to the right of its origin (i.e. the origin
1291 * specifieds the LEFT edge of the text) and so on.
1293 * @return the paint's Align value for drawing text.
1295 public Align getTextAlign() {
1296 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1297 return sAlignArray[nGetTextAlign(mNativePaint)];
1301 * Set the paint's text alignment. This controls how the
1302 * text is positioned relative to its origin. LEFT align means that all of
1303 * the text will be drawn to the right of its origin (i.e. the origin
1304 * specifieds the LEFT edge of the text) and so on.
1306 * @param align set the paint's Align value for drawing text.
1308 public void setTextAlign(Align align) {
1309 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1310 nSetTextAlign(mNativePaint, align.nativeInt);
1314 * Get the text's primary Locale. Note that this is not all of the locale-related information
1315 * Paint has. Use {@link #getTextLocales()} to get the complete list.
1317 * @return the paint's primary Locale used for drawing text, never null.
1320 public Locale getTextLocale() {
1321 return mLocales.getPrimary();
1325 * Get the text locale list.
1327 * @return the paint's LocaleList used for drawing text, never null or empty.
1329 @NonNull @Size(min=1)
1330 public LocaleList getTextLocales() {
1335 * Set the text locale list to a one-member list consisting of just the locale.
1337 * See {@link #setTextLocales(LocaleList)} for how the locale list affects
1338 * the way the text is drawn for some languages.
1340 * @param locale the paint's locale value for drawing text, must not be null.
1342 public void setTextLocale(@NonNull Locale locale) {
1343 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1344 if (locale == null) {
1345 throw new IllegalArgumentException("locale cannot be null");
1347 if (mLocales != null && mLocales.size() == 1 && locale.equals(mLocales.getPrimary())) {
1350 mLocales = new LocaleList(locale);
1351 syncTextLocalesWithMinikin();
1355 * Set the text locale list.
1357 * The text locale list affects how the text is drawn for some languages.
1359 * For example, if the locale list contains {@link Locale#CHINESE} or {@link Locale#CHINA},
1360 * then the text renderer will prefer to draw text using a Chinese font. Likewise,
1361 * if the locale list contains {@link Locale#JAPANESE} or {@link Locale#JAPAN}, then the text
1362 * renderer will prefer to draw text using a Japanese font. If the locale list contains both,
1363 * the order those locales appear in the list is considered for deciding the font.
1365 * This distinction is important because Chinese and Japanese text both use many
1366 * of the same Unicode code points but their appearance is subtly different for
1369 * By default, the text locale list is initialized to a one-member list just containing the
1370 * system locale (as returned by {@link LocaleList#getDefault()}). This assumes that the text to
1371 * be rendered will most likely be in the user's preferred language.
1373 * If the actual language or languages of the text is/are known, then they can be provided to
1374 * the text renderer using this method. The text renderer may attempt to guess the
1375 * language script based on the contents of the text to be drawn independent of
1376 * the text locale here. Specifying the text locales just helps it do a better
1377 * job in certain ambiguous cases.
1379 * @param locales the paint's locale list for drawing text, must not be null or empty.
1381 public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
1382 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1383 if (locales == null || locales.isEmpty()) {
1384 throw new IllegalArgumentException("locales cannot be null or empty");
1386 if (locales.equals(mLocales)) return;
1388 syncTextLocalesWithMinikin();
1391 private void syncTextLocalesWithMinikin() {
1392 final String languageTags = mLocales.toLanguageTags();
1393 final Integer minikinLangListId;
1394 synchronized (sCacheLock) {
1395 minikinLangListId = sMinikinLangListIdCache.get(languageTags);
1396 if (minikinLangListId == null) {
1397 final int newID = nSetTextLocales(mNativePaint, languageTags);
1398 sMinikinLangListIdCache.put(languageTags, newID);
1402 nSetTextLocalesByMinikinLangListId(mNativePaint, minikinLangListId.intValue());
1406 * Get the elegant metrics flag.
1408 * @return true if elegant metrics are enabled for text drawing.
1410 public boolean isElegantTextHeight() {
1411 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1412 return nIsElegantTextHeight(mNativePaint);
1415 private native boolean nIsElegantTextHeight(long paintPtr);
1418 * Set the paint's elegant height metrics flag. This setting selects font
1419 * variants that have not been compacted to fit Latin-based vertical
1420 * metrics, and also increases top and bottom bounds to provide more space.
1422 * @param elegant set the paint's elegant metrics flag for drawing text.
1424 public void setElegantTextHeight(boolean elegant) {
1425 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1426 nSetElegantTextHeight(mNativePaint, elegant);
1429 private native void nSetElegantTextHeight(long paintPtr, boolean elegant);
1432 * Return the paint's text size.
1434 * @return the paint's text size.
1436 public float getTextSize() {
1437 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1438 return nGetTextSize(mNativePaint);
1441 private native float nGetTextSize(long paintPtr);
1444 * Set the paint's text size. This value must be > 0
1446 * @param textSize set the paint's text size.
1448 public void setTextSize(float textSize) {
1449 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1450 nSetTextSize(mNativePaint, textSize);
1453 private native void nSetTextSize(long paintPtr, float textSize);
1456 * Return the paint's horizontal scale factor for text. The default value
1459 * @return the paint's scale factor in X for drawing/measuring text
1461 public float getTextScaleX() {
1462 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1463 return nGetTextScaleX(mNativePaint);
1466 private native float nGetTextScaleX(long paintPtr);
1469 * Set the paint's horizontal scale factor for text. The default value
1470 * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
1471 * stretch the text narrower.
1473 * @param scaleX set the paint's scale in X for drawing/measuring text.
1475 public void setTextScaleX(float scaleX) {
1476 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1477 nSetTextScaleX(mNativePaint, scaleX);
1480 private native void nSetTextScaleX(long paintPtr, float scaleX);
1483 * Return the paint's horizontal skew factor for text. The default value
1486 * @return the paint's skew factor in X for drawing text.
1488 public float getTextSkewX() {
1489 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1490 return nGetTextSkewX(mNativePaint);
1493 private native float nGetTextSkewX(long paintPtr);
1496 * Set the paint's horizontal skew factor for text. The default value
1497 * is 0. For approximating oblique text, use values around -0.25.
1499 * @param skewX set the paint's skew factor in X for drawing text.
1501 public void setTextSkewX(float skewX) {
1502 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1503 nSetTextSkewX(mNativePaint, skewX);
1506 private native void nSetTextSkewX(long paintPtr, float skewX);
1509 * Return the paint's letter-spacing for text. The default value
1512 * @return the paint's letter-spacing for drawing text.
1514 public float getLetterSpacing() {
1515 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1516 return nGetLetterSpacing(mNativePaint);
1520 * Set the paint's letter-spacing for text. The default value
1521 * is 0. The value is in 'EM' units. Typical values for slight
1522 * expansion will be around 0.05. Negative values tighten text.
1524 * @param letterSpacing set the paint's letter-spacing for drawing text.
1526 public void setLetterSpacing(float letterSpacing) {
1527 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1528 nSetLetterSpacing(mNativePaint, letterSpacing);
1532 * Get font feature settings. Default is null.
1534 * @return the paint's currently set font feature settings.
1536 public String getFontFeatureSettings() {
1537 return mFontFeatureSettings;
1541 * Set font feature settings.
1543 * The format is the same as the CSS font-feature-settings attribute:
1544 * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings
1546 * @param settings the font feature settings string to use, may be null.
1548 public void setFontFeatureSettings(String settings) {
1549 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1550 if (settings != null && settings.equals("")) {
1553 if ((settings == null && mFontFeatureSettings == null)
1554 || (settings != null && settings.equals(mFontFeatureSettings))) {
1557 mFontFeatureSettings = settings;
1558 nSetFontFeatureSettings(mNativePaint, settings);
1562 * Get the current value of hyphen edit.
1564 * @return the current hyphen edit value
1568 public int getHyphenEdit() {
1569 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1570 return nGetHyphenEdit(mNativePaint);
1574 * Set a hyphen edit on the paint (causes a hyphen to be added to text when
1575 * measured or drawn).
1577 * @param hyphen 0 for no edit, 1 for adding a hyphen (other values in future)
1581 public void setHyphenEdit(int hyphen) {
1582 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1583 nSetHyphenEdit(mNativePaint, hyphen);
1587 * Return the distance above (negative) the baseline (ascent) based on the
1588 * current typeface and text size.
1590 * @return the distance above (negative) the baseline (ascent) based on the
1591 * current typeface and text size.
1593 public float ascent() {
1594 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1595 return nAscent(mNativePaint, mNativeTypeface);
1598 private native float nAscent(long paintPtr, long typefacePtr);
1601 * Return the distance below (positive) the baseline (descent) based on the
1602 * current typeface and text size.
1604 * @return the distance below (positive) the baseline (descent) based on
1605 * the current typeface and text size.
1607 public float descent() {
1608 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1609 return nDescent(mNativePaint, mNativeTypeface);
1612 private native float nDescent(long paintPtr, long typefacePtr);
1615 * Class that describes the various metrics for a font at a given text size.
1616 * Remember, Y values increase going down, so those values will be positive,
1617 * and values that measure distances going up will be negative. This class
1618 * is returned by getFontMetrics().
1620 public static class FontMetrics {
1622 * The maximum distance above the baseline for the tallest glyph in
1623 * the font at a given text size.
1627 * The recommended distance above the baseline for singled spaced text.
1629 public float ascent;
1631 * The recommended distance below the baseline for singled spaced text.
1633 public float descent;
1635 * The maximum distance below the baseline for the lowest glyph in
1636 * the font at a given text size.
1638 public float bottom;
1640 * The recommended additional space to add between lines of text.
1642 public float leading;
1646 * Return the font's recommended interline spacing, given the Paint's
1647 * settings for typeface, textSize, etc. If metrics is not null, return the
1648 * fontmetric values in it.
1650 * @param metrics If this object is not null, its fields are filled with
1651 * the appropriate values given the paint's text attributes.
1652 * @return the font's recommended interline spacing.
1654 public float getFontMetrics(FontMetrics metrics) {
1655 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1656 return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
1659 private native float nGetFontMetrics(long paintPtr,
1660 long typefacePtr, FontMetrics metrics);
1663 * Allocates a new FontMetrics object, and then calls getFontMetrics(fm)
1664 * with it, returning the object.
1666 public FontMetrics getFontMetrics() {
1667 FontMetrics fm = new FontMetrics();
1673 * Convenience method for callers that want to have FontMetrics values as
1676 public static class FontMetricsInt {
1683 @Override public String toString() {
1684 return "FontMetricsInt: top=" + top + " ascent=" + ascent +
1685 " descent=" + descent + " bottom=" + bottom +
1686 " leading=" + leading;
1691 * Return the font's interline spacing, given the Paint's settings for
1692 * typeface, textSize, etc. If metrics is not null, return the fontmetric
1693 * values in it. Note: all values have been converted to integers from
1694 * floats, in such a way has to make the answers useful for both spacing
1695 * and clipping. If you want more control over the rounding, call
1698 * @return the font's interline spacing.
1700 public int getFontMetricsInt(FontMetricsInt fmi) {
1701 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1702 return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
1705 private native int nGetFontMetricsInt(long paintPtr,
1706 long typefacePtr, FontMetricsInt fmi);
1708 public FontMetricsInt getFontMetricsInt() {
1709 FontMetricsInt fm = new FontMetricsInt();
1710 getFontMetricsInt(fm);
1715 * Return the recommend line spacing based on the current typeface and
1718 * @return recommend line spacing based on the current typeface and
1721 public float getFontSpacing() {
1722 return getFontMetrics(null);
1726 * Return the width of the text.
1728 * @param text The text to measure. Cannot be null.
1729 * @param index The index of the first character to start measuring
1730 * @param count THe number of characters to measure, beginning with start
1731 * @return The width of the text
1733 public float measureText(char[] text, int index, int count) {
1734 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1736 throw new IllegalArgumentException("text cannot be null");
1738 if ((index | count) < 0 || index + count > text.length) {
1739 throw new ArrayIndexOutOfBoundsException();
1742 if (text.length == 0 || count == 0) {
1745 if (!mHasCompatScaling) {
1746 return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
1747 index, count, index, count, mBidiFlags, null, 0));
1750 final float oldSize = getTextSize();
1751 setTextSize(oldSize * mCompatScaling);
1752 float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index,
1753 count, mBidiFlags, null, 0);
1754 setTextSize(oldSize);
1755 return (float) Math.ceil(w*mInvCompatScaling);
1759 * Return the width of the text.
1761 * @param text The text to measure. Cannot be null.
1762 * @param start The index of the first character to start measuring
1763 * @param end 1 beyond the index of the last character to measure
1764 * @return The width of the text
1766 public float measureText(String text, int start, int end) {
1767 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1769 throw new IllegalArgumentException("text cannot be null");
1771 if ((start | end | (end - start) | (text.length() - end)) < 0) {
1772 throw new IndexOutOfBoundsException();
1775 if (text.length() == 0 || start == end) {
1778 if (!mHasCompatScaling) {
1779 return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
1780 start, end, start, end, mBidiFlags, null, 0));
1782 final float oldSize = getTextSize();
1783 setTextSize(oldSize * mCompatScaling);
1784 float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start,
1785 end, mBidiFlags, null, 0);
1786 setTextSize(oldSize);
1787 return (float) Math.ceil(w * mInvCompatScaling);
1791 * Return the width of the text.
1793 * @param text The text to measure. Cannot be null.
1794 * @return The width of the text
1796 public float measureText(String text) {
1797 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1799 throw new IllegalArgumentException("text cannot be null");
1801 return measureText(text, 0, text.length());
1805 * Return the width of the text.
1807 * @param text The text to measure
1808 * @param start The index of the first character to start measuring
1809 * @param end 1 beyond the index of the last character to measure
1810 * @return The width of the text
1812 public float measureText(CharSequence text, int start, int end) {
1814 throw new IllegalArgumentException("text cannot be null");
1816 if ((start | end | (end - start) | (text.length() - end)) < 0) {
1817 throw new IndexOutOfBoundsException();
1820 if (text.length() == 0 || start == end) {
1823 if (text instanceof String) {
1824 return measureText((String)text, start, end);
1826 if (text instanceof SpannedString ||
1827 text instanceof SpannableString) {
1828 return measureText(text.toString(), start, end);
1830 if (text instanceof GraphicsOperations) {
1831 return ((GraphicsOperations)text).measureText(start, end, this);
1834 char[] buf = TemporaryBuffer.obtain(end - start);
1835 TextUtils.getChars(text, start, end, buf, 0);
1836 float result = measureText(buf, 0, end - start);
1837 TemporaryBuffer.recycle(buf);
1842 * Measure the text, stopping early if the measured width exceeds maxWidth.
1843 * Return the number of chars that were measured, and if measuredWidth is
1844 * not null, return in it the actual width measured.
1846 * @param text The text to measure. Cannot be null.
1847 * @param index The offset into text to begin measuring at
1848 * @param count The number of maximum number of entries to measure. If count
1849 * is negative, then the characters are measured in reverse order.
1850 * @param maxWidth The maximum width to accumulate.
1851 * @param measuredWidth Optional. If not null, returns the actual width
1853 * @return The number of chars that were measured. Will always be <=
1856 public int breakText(char[] text, int index, int count,
1857 float maxWidth, float[] measuredWidth) {
1858 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1860 throw new IllegalArgumentException("text cannot be null");
1862 if (index < 0 || text.length - index < Math.abs(count)) {
1863 throw new ArrayIndexOutOfBoundsException();
1866 if (text.length == 0 || count == 0) {
1869 if (!mHasCompatScaling) {
1870 return nBreakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
1871 mBidiFlags, measuredWidth);
1874 final float oldSize = getTextSize();
1875 setTextSize(oldSize * mCompatScaling);
1876 int res = nBreakText(mNativePaint, mNativeTypeface, text, index, count,
1877 maxWidth * mCompatScaling, mBidiFlags, measuredWidth);
1878 setTextSize(oldSize);
1879 if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
1883 private static native int nBreakText(long nObject, long nTypeface,
1884 char[] text, int index, int count,
1885 float maxWidth, int bidiFlags, float[] measuredWidth);
1888 * Measure the text, stopping early if the measured width exceeds maxWidth.
1889 * Return the number of chars that were measured, and if measuredWidth is
1890 * not null, return in it the actual width measured.
1892 * @param text The text to measure. Cannot be null.
1893 * @param start The offset into text to begin measuring at
1894 * @param end The end of the text slice to measure.
1895 * @param measureForwards If true, measure forwards, starting at start.
1896 * Otherwise, measure backwards, starting with end.
1897 * @param maxWidth The maximum width to accumulate.
1898 * @param measuredWidth Optional. If not null, returns the actual width
1900 * @return The number of chars that were measured. Will always be <=
1903 public int breakText(CharSequence text, int start, int end,
1904 boolean measureForwards,
1905 float maxWidth, float[] measuredWidth) {
1906 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1908 throw new IllegalArgumentException("text cannot be null");
1910 if ((start | end | (end - start) | (text.length() - end)) < 0) {
1911 throw new IndexOutOfBoundsException();
1914 if (text.length() == 0 || start == end) {
1917 if (start == 0 && text instanceof String && end == text.length()) {
1918 return breakText((String) text, measureForwards, maxWidth,
1922 char[] buf = TemporaryBuffer.obtain(end - start);
1925 TextUtils.getChars(text, start, end, buf, 0);
1927 if (measureForwards) {
1928 result = breakText(buf, 0, end - start, maxWidth, measuredWidth);
1930 result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth);
1933 TemporaryBuffer.recycle(buf);
1938 * Measure the text, stopping early if the measured width exceeds maxWidth.
1939 * Return the number of chars that were measured, and if measuredWidth is
1940 * not null, return in it the actual width measured.
1942 * @param text The text to measure. Cannot be null.
1943 * @param measureForwards If true, measure forwards, starting with the
1944 * first character in the string. Otherwise,
1945 * measure backwards, starting with the
1946 * last character in the string.
1947 * @param maxWidth The maximum width to accumulate.
1948 * @param measuredWidth Optional. If not null, returns the actual width
1950 * @return The number of chars that were measured. Will always be <=
1953 public int breakText(String text, boolean measureForwards,
1954 float maxWidth, float[] measuredWidth) {
1955 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1957 throw new IllegalArgumentException("text cannot be null");
1960 if (text.length() == 0) {
1963 if (!mHasCompatScaling) {
1964 return nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
1965 maxWidth, mBidiFlags, measuredWidth);
1968 final float oldSize = getTextSize();
1969 setTextSize(oldSize*mCompatScaling);
1970 int res = nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
1971 maxWidth*mCompatScaling, mBidiFlags, measuredWidth);
1972 setTextSize(oldSize);
1973 if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
1977 private static native int nBreakText(long nObject, long nTypeface,
1978 String text, boolean measureForwards,
1979 float maxWidth, int bidiFlags, float[] measuredWidth);
1982 * Return the advance widths for the characters in the string.
1984 * @param text The text to measure. Cannot be null.
1985 * @param index The index of the first char to to measure
1986 * @param count The number of chars starting with index to measure
1987 * @param widths array to receive the advance widths of the characters.
1988 * Must be at least a large as count.
1989 * @return the actual number of widths returned.
1991 public int getTextWidths(char[] text, int index, int count,
1993 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1995 throw new IllegalArgumentException("text cannot be null");
1997 if ((index | count) < 0 || index + count > text.length
1998 || count > widths.length) {
1999 throw new ArrayIndexOutOfBoundsException();
2002 if (text.length == 0 || count == 0) {
2005 if (!mHasCompatScaling) {
2006 nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
2007 mBidiFlags, widths, 0);
2011 final float oldSize = getTextSize();
2012 setTextSize(oldSize * mCompatScaling);
2013 nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
2014 mBidiFlags, widths, 0);
2015 setTextSize(oldSize);
2016 for (int i = 0; i < count; i++) {
2017 widths[i] *= mInvCompatScaling;
2023 * Return the advance widths for the characters in the string.
2025 * @param text The text to measure. Cannot be null.
2026 * @param start The index of the first char to to measure
2027 * @param end The end of the text slice to measure
2028 * @param widths array to receive the advance widths of the characters.
2029 * Must be at least a large as (end - start).
2030 * @return the actual number of widths returned.
2032 public int getTextWidths(CharSequence text, int start, int end,
2035 throw new IllegalArgumentException("text cannot be null");
2037 if ((start | end | (end - start) | (text.length() - end)) < 0) {
2038 throw new IndexOutOfBoundsException();
2040 if (end - start > widths.length) {
2041 throw new ArrayIndexOutOfBoundsException();
2044 if (text.length() == 0 || start == end) {
2047 if (text instanceof String) {
2048 return getTextWidths((String) text, start, end, widths);
2050 if (text instanceof SpannedString ||
2051 text instanceof SpannableString) {
2052 return getTextWidths(text.toString(), start, end, widths);
2054 if (text instanceof GraphicsOperations) {
2055 return ((GraphicsOperations) text).getTextWidths(start, end,
2059 char[] buf = TemporaryBuffer.obtain(end - start);
2060 TextUtils.getChars(text, start, end, buf, 0);
2061 int result = getTextWidths(buf, 0, end - start, widths);
2062 TemporaryBuffer.recycle(buf);
2067 * Return the advance widths for the characters in the string.
2069 * @param text The text to measure. Cannot be null.
2070 * @param start The index of the first char to to measure
2071 * @param end The end of the text slice to measure
2072 * @param widths array to receive the advance widths of the characters.
2073 * Must be at least a large as the text.
2074 * @return the number of code units in the specified text.
2076 public int getTextWidths(String text, int start, int end, float[] widths) {
2077 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2079 throw new IllegalArgumentException("text cannot be null");
2081 if ((start | end | (end - start) | (text.length() - end)) < 0) {
2082 throw new IndexOutOfBoundsException();
2084 if (end - start > widths.length) {
2085 throw new ArrayIndexOutOfBoundsException();
2088 if (text.length() == 0 || start == end) {
2091 if (!mHasCompatScaling) {
2092 nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
2093 mBidiFlags, widths, 0);
2097 final float oldSize = getTextSize();
2098 setTextSize(oldSize * mCompatScaling);
2099 nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
2100 mBidiFlags, widths, 0);
2101 setTextSize(oldSize);
2102 for (int i = 0; i < end - start; i++) {
2103 widths[i] *= mInvCompatScaling;
2109 * Return the advance widths for the characters in the string.
2111 * @param text The text to measure
2112 * @param widths array to receive the advance widths of the characters.
2113 * Must be at least a large as the text.
2114 * @return the number of code units in the specified text.
2116 public int getTextWidths(String text, float[] widths) {
2117 return getTextWidths(text, 0, text.length(), widths);
2121 * Convenience overload that takes a char array instead of a
2124 * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
2127 public float getTextRunAdvances(char[] chars, int index, int count,
2128 int contextIndex, int contextCount, boolean isRtl, float[] advances,
2129 int advancesIndex) {
2131 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2132 if (chars == null) {
2133 throw new IllegalArgumentException("text cannot be null");
2135 if ((index | count | contextIndex | contextCount | advancesIndex
2136 | (index - contextIndex) | (contextCount - count)
2137 | ((contextIndex + contextCount) - (index + count))
2138 | (chars.length - (contextIndex + contextCount))
2139 | (advances == null ? 0 :
2140 (advances.length - (advancesIndex + count)))) < 0) {
2141 throw new IndexOutOfBoundsException();
2144 if (chars.length == 0 || count == 0){
2147 if (!mHasCompatScaling) {
2148 return nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
2149 contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
2153 final float oldSize = getTextSize();
2154 setTextSize(oldSize * mCompatScaling);
2155 float res = nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
2156 contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
2158 setTextSize(oldSize);
2160 if (advances != null) {
2161 for (int i = advancesIndex, e = i + count; i < e; i++) {
2162 advances[i] *= mInvCompatScaling;
2165 return res * mInvCompatScaling; // assume errors are not significant
2169 * Convenience overload that takes a CharSequence instead of a
2172 * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
2175 public float getTextRunAdvances(CharSequence text, int start, int end,
2176 int contextStart, int contextEnd, boolean isRtl, float[] advances,
2177 int advancesIndex) {
2178 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2180 throw new IllegalArgumentException("text cannot be null");
2182 if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
2183 | (start - contextStart) | (contextEnd - end)
2184 | (text.length() - contextEnd)
2185 | (advances == null ? 0 :
2186 (advances.length - advancesIndex - (end - start)))) < 0) {
2187 throw new IndexOutOfBoundsException();
2190 if (text instanceof String) {
2191 return getTextRunAdvances((String) text, start, end,
2192 contextStart, contextEnd, isRtl, advances, advancesIndex);
2194 if (text instanceof SpannedString ||
2195 text instanceof SpannableString) {
2196 return getTextRunAdvances(text.toString(), start, end,
2197 contextStart, contextEnd, isRtl, advances, advancesIndex);
2199 if (text instanceof GraphicsOperations) {
2200 return ((GraphicsOperations) text).getTextRunAdvances(start, end,
2201 contextStart, contextEnd, isRtl, advances, advancesIndex, this);
2203 if (text.length() == 0 || end == start) {
2207 int contextLen = contextEnd - contextStart;
2208 int len = end - start;
2209 char[] buf = TemporaryBuffer.obtain(contextLen);
2210 TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
2211 float result = getTextRunAdvances(buf, start - contextStart, len,
2212 0, contextLen, isRtl, advances, advancesIndex);
2213 TemporaryBuffer.recycle(buf);
2218 * Returns the total advance width for the characters in the run
2219 * between start and end, and if advances is not null, the advance
2220 * assigned to each of these characters (java chars).
2222 * <p>The trailing surrogate in a valid surrogate pair is assigned
2223 * an advance of 0. Thus the number of returned advances is
2224 * always equal to count, not to the number of unicode codepoints
2225 * represented by the run.
2227 * <p>In the case of conjuncts or combining marks, the total
2228 * advance is assigned to the first logical character, and the
2229 * following characters are assigned an advance of 0.
2231 * <p>This generates the sum of the advances of glyphs for
2232 * characters in a reordered cluster as the width of the first
2233 * logical character in the cluster, and 0 for the widths of all
2234 * other characters in the cluster. In effect, such clusters are
2235 * treated like conjuncts.
2237 * <p>The shaping bounds limit the amount of context available
2238 * outside start and end that can be used for shaping analysis.
2239 * These bounds typically reflect changes in bidi level or font
2240 * metrics across which shaping does not occur.
2242 * @param text the text to measure. Cannot be null.
2243 * @param start the index of the first character to measure
2244 * @param end the index past the last character to measure
2245 * @param contextStart the index of the first character to use for shaping context,
2247 * @param contextEnd the index past the last character to use for shaping context,
2249 * @param isRtl whether the run is in RTL direction
2250 * @param advances array to receive the advances, must have room for all advances,
2251 * can be null if only total advance is needed
2252 * @param advancesIndex the position in advances at which to put the
2253 * advance corresponding to the character at start
2254 * @return the total advance
2258 public float getTextRunAdvances(String text, int start, int end, int contextStart,
2259 int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
2260 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2262 throw new IllegalArgumentException("text cannot be null");
2264 if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
2265 | (start - contextStart) | (contextEnd - end)
2266 | (text.length() - contextEnd)
2267 | (advances == null ? 0 :
2268 (advances.length - advancesIndex - (end - start)))) < 0) {
2269 throw new IndexOutOfBoundsException();
2272 if (text.length() == 0 || start == end) {
2276 if (!mHasCompatScaling) {
2277 return nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end,
2278 contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
2282 final float oldSize = getTextSize();
2283 setTextSize(oldSize * mCompatScaling);
2284 float totalAdvance = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start,
2285 end, contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
2287 setTextSize(oldSize);
2289 if (advances != null) {
2290 for (int i = advancesIndex, e = i + (end - start); i < e; i++) {
2291 advances[i] *= mInvCompatScaling;
2294 return totalAdvance * mInvCompatScaling; // assume errors are insignificant
2298 * Returns the next cursor position in the run. This avoids placing the
2299 * cursor between surrogates, between characters that form conjuncts,
2300 * between base characters and combining marks, or within a reordering
2303 * <p>ContextStart and offset are relative to the start of text.
2304 * The context is the shaping context for cursor movement, generally
2305 * the bounds of the metric span enclosing the cursor in the direction of
2308 * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
2309 * cursor position, this returns -1. Otherwise this will never return a
2310 * value before contextStart or after contextStart + contextLength.
2312 * @param text the text
2313 * @param contextStart the start of the context
2314 * @param contextLength the length of the context
2315 * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
2316 * @param offset the cursor position to move from
2317 * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
2318 * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
2319 * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
2320 * @return the offset of the next position, or -1
2323 public int getTextRunCursor(char[] text, int contextStart, int contextLength,
2324 int dir, int offset, int cursorOpt) {
2325 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2326 int contextEnd = contextStart + contextLength;
2327 if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
2328 | (offset - contextStart) | (contextEnd - offset)
2329 | (text.length - contextEnd) | cursorOpt) < 0)
2330 || cursorOpt > CURSOR_OPT_MAX_VALUE) {
2331 throw new IndexOutOfBoundsException();
2334 return nGetTextRunCursor(mNativePaint, text,
2335 contextStart, contextLength, dir, offset, cursorOpt);
2339 * Returns the next cursor position in the run. This avoids placing the
2340 * cursor between surrogates, between characters that form conjuncts,
2341 * between base characters and combining marks, or within a reordering
2344 * <p>ContextStart, contextEnd, and offset are relative to the start of
2345 * text. The context is the shaping context for cursor movement, generally
2346 * the bounds of the metric span enclosing the cursor in the direction of
2349 * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
2350 * cursor position, this returns -1. Otherwise this will never return a
2351 * value before contextStart or after contextEnd.
2353 * @param text the text
2354 * @param contextStart the start of the context
2355 * @param contextEnd the end of the context
2356 * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
2357 * @param offset the cursor position to move from
2358 * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
2359 * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
2360 * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
2361 * @return the offset of the next position, or -1
2364 public int getTextRunCursor(CharSequence text, int contextStart,
2365 int contextEnd, int dir, int offset, int cursorOpt) {
2367 if (text instanceof String || text instanceof SpannedString ||
2368 text instanceof SpannableString) {
2369 return getTextRunCursor(text.toString(), contextStart, contextEnd,
2370 dir, offset, cursorOpt);
2372 if (text instanceof GraphicsOperations) {
2373 return ((GraphicsOperations) text).getTextRunCursor(
2374 contextStart, contextEnd, dir, offset, cursorOpt, this);
2377 int contextLen = contextEnd - contextStart;
2378 char[] buf = TemporaryBuffer.obtain(contextLen);
2379 TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
2380 int relPos = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt);
2381 TemporaryBuffer.recycle(buf);
2382 return (relPos == -1) ? -1 : relPos + contextStart;
2386 * Returns the next cursor position in the run. This avoids placing the
2387 * cursor between surrogates, between characters that form conjuncts,
2388 * between base characters and combining marks, or within a reordering
2391 * <p>ContextStart, contextEnd, and offset are relative to the start of
2392 * text. The context is the shaping context for cursor movement, generally
2393 * the bounds of the metric span enclosing the cursor in the direction of
2396 * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
2397 * cursor position, this returns -1. Otherwise this will never return a
2398 * value before contextStart or after contextEnd.
2400 * @param text the text
2401 * @param contextStart the start of the context
2402 * @param contextEnd the end of the context
2403 * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
2404 * @param offset the cursor position to move from
2405 * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
2406 * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
2407 * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
2408 * @return the offset of the next position, or -1
2411 public int getTextRunCursor(String text, int contextStart, int contextEnd,
2412 int dir, int offset, int cursorOpt) {
2413 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2414 if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
2415 | (offset - contextStart) | (contextEnd - offset)
2416 | (text.length() - contextEnd) | cursorOpt) < 0)
2417 || cursorOpt > CURSOR_OPT_MAX_VALUE) {
2418 throw new IndexOutOfBoundsException();
2421 return nGetTextRunCursor(mNativePaint, text,
2422 contextStart, contextEnd, dir, offset, cursorOpt);
2426 * Return the path (outline) for the specified text.
2427 * Note: just like Canvas.drawText, this will respect the Align setting in
2430 * @param text The text to retrieve the path from
2431 * @param index The index of the first character in text
2432 * @param count The number of characterss starting with index
2433 * @param x The x coordinate of the text's origin
2434 * @param y The y coordinate of the text's origin
2435 * @param path The path to receive the data describing the text. Must
2436 * be allocated by the caller.
2438 public void getTextPath(char[] text, int index, int count,
2439 float x, float y, Path path) {
2440 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2441 if ((index | count) < 0 || index + count > text.length) {
2442 throw new ArrayIndexOutOfBoundsException();
2444 nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
2449 * Return the path (outline) for the specified text.
2450 * Note: just like Canvas.drawText, this will respect the Align setting
2453 * @param text The text to retrieve the path from
2454 * @param start The first character in the text
2455 * @param end 1 past the last charcter in the text
2456 * @param x The x coordinate of the text's origin
2457 * @param y The y coordinate of the text's origin
2458 * @param path The path to receive the data describing the text. Must
2459 * be allocated by the caller.
2461 public void getTextPath(String text, int start, int end,
2462 float x, float y, Path path) {
2463 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2464 if ((start | end | (end - start) | (text.length() - end)) < 0) {
2465 throw new IndexOutOfBoundsException();
2467 nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
2472 * Return in bounds (allocated by the caller) the smallest rectangle that
2473 * encloses all of the characters, with an implied origin at (0,0).
2475 * @param text String to measure and return its bounds
2476 * @param start Index of the first char in the string to measure
2477 * @param end 1 past the last char in the string measure
2478 * @param bounds Returns the unioned bounds of all the text. Must be
2479 * allocated by the caller.
2481 public void getTextBounds(String text, int start, int end, Rect bounds) {
2482 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2483 if ((start | end | (end - start) | (text.length() - end)) < 0) {
2484 throw new IndexOutOfBoundsException();
2486 if (bounds == null) {
2487 throw new NullPointerException("need bounds Rect");
2489 nGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
2493 * Return in bounds (allocated by the caller) the smallest rectangle that
2494 * encloses all of the characters, with an implied origin at (0,0).
2496 * @param text Array of chars to measure and return their unioned bounds
2497 * @param index Index of the first char in the array to measure
2498 * @param count The number of chars, beginning at index, to measure
2499 * @param bounds Returns the unioned bounds of all the text. Must be
2500 * allocated by the caller.
2502 public void getTextBounds(char[] text, int index, int count, Rect bounds) {
2503 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2504 if ((index | count) < 0 || index + count > text.length) {
2505 throw new ArrayIndexOutOfBoundsException();
2507 if (bounds == null) {
2508 throw new NullPointerException("need bounds Rect");
2510 nGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
2515 * Determine whether the typeface set on the paint has a glyph supporting the string. The
2516 * simplest case is when the string contains a single character, in which this method
2517 * determines whether the font has the character. In the case of multiple characters, the
2518 * method returns true if there is a single glyph representing the ligature. For example, if
2519 * the input is a pair of regional indicator symbols, determine whether there is an emoji flag
2522 * <p>Finally, if the string contains a variation selector, the method only returns true if
2523 * the fonts contains a glyph specific to that variation.
2525 * <p>Checking is done on the entire fallback chain, not just the immediate font referenced.
2527 * @param string the string to test whether there is glyph support
2528 * @return true if the typeface has a glyph for the string
2530 public boolean hasGlyph(String string) {
2531 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2532 return nHasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
2536 * Measure cursor position within a run of text.
2538 * <p>The run of text includes the characters from {@code start} to {@code end} in the text. In
2539 * addition, the range {@code contextStart} to {@code contextEnd} is used as context for the
2540 * purpose of complex text shaping, such as Arabic text potentially shaped differently based on
2541 * the text next to it.
2543 * <p>All text outside the range {@code contextStart..contextEnd} is ignored. The text between
2544 * {@code start} and {@code end} will be laid out to be measured.
2546 * <p>The returned width measurement is the advance from {@code start} to {@code offset}. It is
2547 * generally a positive value, no matter the direction of the run. If {@code offset == end},
2548 * the return value is simply the width of the whole run from {@code start} to {@code end}.
2550 * <p>Ligatures are formed for characters in the range {@code start..end} (but not for
2551 * {@code start..contextStart} or {@code end..contextEnd}). If {@code offset} points to a
2552 * character in the middle of such a formed ligature, but at a grapheme cluster boundary, the
2553 * return value will also reflect an advance in the middle of the ligature. See
2554 * {@link #getOffsetForAdvance} for more discussion of grapheme cluster boundaries.
2556 * <p>The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is
2557 * suitable only for runs of a single direction.
2559 * <p>All indices are relative to the start of {@code text}. Further, {@code 0 <= contextStart
2560 * <= start <= offset <= end <= contextEnd <= text.length} must hold on entry.
2562 * @param text the text to measure. Cannot be null.
2563 * @param start the index of the start of the range to measure
2564 * @param end the index + 1 of the end of the range to measure
2565 * @param contextStart the index of the start of the shaping context
2566 * @param contextEnd the index + 1 of the end of the shaping context
2567 * @param isRtl whether the run is in RTL direction
2568 * @param offset index of caret position
2569 * @return width measurement between start and offset
2571 public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd,
2572 boolean isRtl, int offset) {
2573 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2575 throw new IllegalArgumentException("text cannot be null");
2577 if ((contextStart | start | offset | end | contextEnd
2578 | start - contextStart | offset - start | end - offset
2579 | contextEnd - end | text.length - contextEnd) < 0) {
2580 throw new IndexOutOfBoundsException();
2585 // TODO: take mCompatScaling into account (or eliminate compat scaling)?
2586 return nGetRunAdvance(mNativePaint, mNativeTypeface, text, start, end,
2587 contextStart, contextEnd, isRtl, offset);
2591 * @see #getRunAdvance(char[], int, int, int, int, boolean, int)
2593 * @param text the text to measure. Cannot be null.
2594 * @param start the index of the start of the range to measure
2595 * @param end the index + 1 of the end of the range to measure
2596 * @param contextStart the index of the start of the shaping context
2597 * @param contextEnd the index + 1 of the end of the shaping context
2598 * @param isRtl whether the run is in RTL direction
2599 * @param offset index of caret position
2600 * @return width measurement between start and offset
2602 public float getRunAdvance(CharSequence text, int start, int end, int contextStart,
2603 int contextEnd, boolean isRtl, int offset) {
2604 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2606 throw new IllegalArgumentException("text cannot be null");
2608 if ((contextStart | start | offset | end | contextEnd
2609 | start - contextStart | offset - start | end - offset
2610 | contextEnd - end | text.length() - contextEnd) < 0) {
2611 throw new IndexOutOfBoundsException();
2616 // TODO performance: specialized alternatives to avoid buffer copy, if win is significant
2617 char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart);
2618 TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
2619 float result = getRunAdvance(buf, start - contextStart, end - contextStart, 0,
2620 contextEnd - contextStart, isRtl, offset - contextStart);
2621 TemporaryBuffer.recycle(buf);
2626 * Get the character offset within the string whose position is closest to the specified
2627 * horizontal position.
2629 * <p>The returned value is generally the value of {@code offset} for which
2630 * {@link #getRunAdvance} yields a result most closely approximating {@code advance},
2631 * and which is also on a grapheme cluster boundary. As such, it is the preferred method
2632 * for positioning a cursor in response to a touch or pointer event. The grapheme cluster
2633 * boundaries are based on
2634 * <a href="http://unicode.org/reports/tr29/">Unicode Standard Annex #29</a> but with some
2635 * tailoring for better user experience.
2637 * <p>Note that {@code advance} is a (generally positive) width measurement relative to the start
2638 * of the run. Thus, for RTL runs it the distance from the point to the right edge.
2640 * <p>All indices are relative to the start of {@code text}. Further, {@code 0 <= contextStart
2641 * <= start <= end <= contextEnd <= text.length} must hold on entry, and {@code start <= result
2642 * <= end} will hold on return.
2644 * @param text the text to measure. Cannot be null.
2645 * @param start the index of the start of the range to measure
2646 * @param end the index + 1 of the end of the range to measure
2647 * @param contextStart the index of the start of the shaping context
2648 * @param contextEnd the index + 1 of the end of the range to measure
2649 * @param isRtl whether the run is in RTL direction
2650 * @param advance width relative to start of run
2651 * @return index of offset
2653 public int getOffsetForAdvance(char[] text, int start, int end, int contextStart,
2654 int contextEnd, boolean isRtl, float advance) {
2655 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2657 throw new IllegalArgumentException("text cannot be null");
2659 if ((contextStart | start | end | contextEnd
2660 | start - contextStart | end - start | contextEnd - end
2661 | text.length - contextEnd) < 0) {
2662 throw new IndexOutOfBoundsException();
2664 // TODO: take mCompatScaling into account (or eliminate compat scaling)?
2665 return nGetOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end,
2666 contextStart, contextEnd, isRtl, advance);
2670 * @see #getOffsetForAdvance(char[], int, int, int, int, boolean, float)
2672 * @param text the text to measure. Cannot be null.
2673 * @param start the index of the start of the range to measure
2674 * @param end the index + 1 of the end of the range to measure
2675 * @param contextStart the index of the start of the shaping context
2676 * @param contextEnd the index + 1 of the end of the range to measure
2677 * @param isRtl whether the run is in RTL direction
2678 * @param advance width relative to start of run
2679 * @return index of offset
2681 public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart,
2682 int contextEnd, boolean isRtl, float advance) {
2683 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2685 throw new IllegalArgumentException("text cannot be null");
2687 if ((contextStart | start | end | contextEnd
2688 | start - contextStart | end - start | contextEnd - end
2689 | text.length() - contextEnd) < 0) {
2690 throw new IndexOutOfBoundsException();
2692 // TODO performance: specialized alternatives to avoid buffer copy, if win is significant
2693 char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart);
2694 TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
2695 int result = getOffsetForAdvance(buf, start - contextStart, end - contextStart, 0,
2696 contextEnd - contextStart, isRtl, advance) + contextStart;
2697 TemporaryBuffer.recycle(buf);
2702 protected void finalize() throws Throwable {
2704 if (mNativePaint != 0) {
2705 nFinalizer(mNativePaint);
2713 private static native long nInit();
2714 private static native long nInitWithPaint(long paint);
2715 private static native void nReset(long paintPtr);
2716 private static native void nSet(long paintPtrDest, long paintPtrSrc);
2717 private static native int nGetStyle(long paintPtr);
2718 private static native void nSetStyle(long paintPtr, int style);
2719 private static native int nGetStrokeCap(long paintPtr);
2720 private static native void nSetStrokeCap(long paintPtr, int cap);
2721 private static native int nGetStrokeJoin(long paintPtr);
2722 private static native void nSetStrokeJoin(long paintPtr,
2724 private static native boolean nGetFillPath(long paintPtr,
2725 long src, long dst);
2726 private static native long nSetShader(long paintPtr, long shader);
2727 private static native long nSetColorFilter(long paintPtr,
2729 private static native long nSetXfermode(long paintPtr,
2731 private static native long nSetPathEffect(long paintPtr,
2733 private static native long nSetMaskFilter(long paintPtr,
2735 private static native long nSetTypeface(long paintPtr,
2737 private static native long nSetRasterizer(long paintPtr,
2740 private static native int nGetTextAlign(long paintPtr);
2741 private static native void nSetTextAlign(long paintPtr,
2744 private static native int nSetTextLocales(long paintPtr, String locales);
2745 private static native void nSetTextLocalesByMinikinLangListId(long paintPtr,
2746 int mMinikinLangListId);
2748 private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
2749 char[] text, int index, int count, int contextIndex, int contextCount,
2750 int bidiFlags, float[] advances, int advancesIndex);
2751 private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
2752 String text, int start, int end, int contextStart, int contextEnd,
2753 int bidiFlags, float[] advances, int advancesIndex);
2755 private native int nGetTextRunCursor(long paintPtr, char[] text,
2756 int contextStart, int contextLength, int dir, int offset, int cursorOpt);
2757 private native int nGetTextRunCursor(long paintPtr, String text,
2758 int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
2760 private static native void nGetTextPath(long paintPtr, long typefacePtr,
2761 int bidiFlags, char[] text, int index, int count, float x, float y, long path);
2762 private static native void nGetTextPath(long paintPtr, long typefacePtr,
2763 int bidiFlags, String text, int start, int end, float x, float y, long path);
2764 private static native void nGetStringBounds(long nativePaint, long typefacePtr,
2765 String text, int start, int end, int bidiFlags, Rect bounds);
2766 private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
2767 char[] text, int index, int count, int bidiFlags, Rect bounds);
2768 private static native void nFinalizer(long nativePaint);
2770 private static native void nSetShadowLayer(long paintPtr,
2771 float radius, float dx, float dy, int color);
2772 private static native boolean nHasShadowLayer(long paintPtr);
2774 private static native float nGetLetterSpacing(long paintPtr);
2775 private static native void nSetLetterSpacing(long paintPtr,
2776 float letterSpacing);
2777 private static native void nSetFontFeatureSettings(long paintPtr,
2779 private static native int nGetHyphenEdit(long paintPtr);
2780 private static native void nSetHyphenEdit(long paintPtr, int hyphen);
2781 private static native boolean nHasGlyph(long paintPtr, long typefacePtr,
2782 int bidiFlags, String string);
2783 private static native float nGetRunAdvance(long paintPtr, long typefacePtr,
2784 char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl,
2786 private static native int nGetOffsetForAdvance(long paintPtr,
2787 long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd,
2788 boolean isRtl, float advance);