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 java.util.Locale;
31 * The Paint class holds the style and color information about how to draw
32 * geometries, text and bitmaps.
36 private long mNativePaint;
37 private long mNativeShader = 0;
42 public long mNativeTypeface;
44 private ColorFilter mColorFilter;
45 private MaskFilter mMaskFilter;
46 private PathEffect mPathEffect;
47 private Rasterizer mRasterizer;
48 private Shader mShader;
49 private Typeface mTypeface;
50 private Xfermode mXfermode;
52 private boolean mHasCompatScaling;
53 private float mCompatScaling;
54 private float mInvCompatScaling;
56 private LocaleList mLocales;
57 private String mFontFeatureSettings;
62 public int mBidiFlags = BIDI_DEFAULT_LTR;
64 static final Style[] sStyleArray = {
65 Style.FILL, Style.STROKE, Style.FILL_AND_STROKE
67 static final Cap[] sCapArray = {
68 Cap.BUTT, Cap.ROUND, Cap.SQUARE
70 static final Join[] sJoinArray = {
71 Join.MITER, Join.ROUND, Join.BEVEL
73 static final Align[] sAlignArray = {
74 Align.LEFT, Align.CENTER, Align.RIGHT
78 * Paint flag that enables antialiasing when drawing.
80 * <p>Enabling this flag will cause all draw operations that support
81 * antialiasing to use it.</p>
86 public static final int ANTI_ALIAS_FLAG = 0x01;
88 * Paint flag that enables bilinear sampling on scaled bitmaps.
90 * <p>If cleared, scaled bitmaps will be drawn with nearest neighbor
91 * sampling, likely resulting in artifacts. This should generally be on
92 * when drawing bitmaps, unless performance-bound (rendering to software
93 * canvas) or preferring pixelation artifacts to blurriness when scaling
96 * <p>If bitmaps are scaled for device density at creation time (as
97 * resource bitmaps often are) the filtering will already have been
101 * @see #setFlags(int)
103 public static final int FILTER_BITMAP_FLAG = 0x02;
105 * Paint flag that enables dithering when blitting.
107 * <p>Enabling this flag applies a dither to any blit operation where the
108 * target's colour space is more constrained than the source.
111 * @see #setFlags(int)
113 public static final int DITHER_FLAG = 0x04;
115 * Paint flag that applies an underline decoration to drawn text.
118 * @see #setFlags(int)
120 public static final int UNDERLINE_TEXT_FLAG = 0x08;
122 * Paint flag that applies a strike-through decoration to drawn text.
125 * @see #setFlags(int)
127 public static final int STRIKE_THRU_TEXT_FLAG = 0x10;
129 * Paint flag that applies a synthetic bolding effect to drawn text.
131 * <p>Enabling this flag will cause text draw operations to apply a
132 * simulated bold effect when drawing a {@link Typeface} that is not
136 * @see #setFlags(int)
138 public static final int FAKE_BOLD_TEXT_FLAG = 0x20;
140 * Paint flag that enables smooth linear scaling of text.
142 * <p>Enabling this flag does not actually scale text, but rather adjusts
143 * text draw operations to deal gracefully with smooth adjustment of scale.
144 * When this flag is enabled, font hinting is disabled to prevent shape
145 * deformation between scale factors, and glyph caching is disabled due to
146 * the large number of glyph images that will be generated.</p>
148 * <p>{@link #SUBPIXEL_TEXT_FLAG} should be used in conjunction with this
149 * flag to prevent glyph positions from snapping to whole pixel values as
150 * scale factor is adjusted.</p>
153 * @see #setFlags(int)
155 public static final int LINEAR_TEXT_FLAG = 0x40;
157 * Paint flag that enables subpixel positioning of text.
159 * <p>Enabling this flag causes glyph advances to be computed with subpixel
162 * <p>This can be used with {@link #LINEAR_TEXT_FLAG} to prevent text from
163 * jittering during smooth scale transitions.</p>
166 * @see #setFlags(int)
168 public static final int SUBPIXEL_TEXT_FLAG = 0x80;
169 /** Legacy Paint flag, no longer used. */
170 public static final int DEV_KERN_TEXT_FLAG = 0x100;
171 /** @hide bit mask for the flag enabling subpixel glyph rendering for text */
172 public static final int LCD_RENDER_TEXT_FLAG = 0x200;
174 * Paint flag that enables the use of bitmap fonts when drawing text.
176 * <p>Disabling this flag will prevent text draw operations from using
177 * embedded bitmap strikes in fonts, causing fonts with both scalable
178 * outlines and bitmap strikes to draw only the scalable outlines, and
179 * fonts with only bitmap strikes to not draw at all.</p>
182 * @see #setFlags(int)
184 public static final int EMBEDDED_BITMAP_TEXT_FLAG = 0x400;
185 /** @hide bit mask for the flag forcing freetype's autohinter on for text */
186 public static final int AUTO_HINTING_TEXT_FLAG = 0x800;
187 /** @hide bit mask for the flag enabling vertical rendering for text */
188 public static final int VERTICAL_TEXT_FLAG = 0x1000;
190 // These flags are always set on a new/reset paint, even if flags 0 is passed.
191 static final int HIDDEN_DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG;
194 * Font hinter option that disables font hinting.
196 * @see #setHinting(int)
198 public static final int HINTING_OFF = 0x0;
201 * Font hinter option that enables font hinting.
203 * @see #setHinting(int)
205 public static final int HINTING_ON = 0x1;
208 * Bidi flag to set LTR paragraph direction.
212 public static final int BIDI_LTR = 0x0;
215 * Bidi flag to set RTL paragraph direction.
219 public static final int BIDI_RTL = 0x1;
222 * Bidi flag to detect paragraph direction via heuristics, defaulting to
227 public static final int BIDI_DEFAULT_LTR = 0x2;
230 * Bidi flag to detect paragraph direction via heuristics, defaulting to
235 public static final int BIDI_DEFAULT_RTL = 0x3;
238 * Bidi flag to override direction to all LTR (ignore bidi).
242 public static final int BIDI_FORCE_LTR = 0x4;
245 * Bidi flag to override direction to all RTL (ignore bidi).
249 public static final int BIDI_FORCE_RTL = 0x5;
252 * Maximum Bidi flag value.
255 private static final int BIDI_MAX_FLAG_VALUE = BIDI_FORCE_RTL;
258 * Mask for bidi flags.
261 private static final int BIDI_FLAG_MASK = 0x7;
264 * Flag for getTextRunAdvances indicating left-to-right run direction.
267 public static final int DIRECTION_LTR = 0;
270 * Flag for getTextRunAdvances indicating right-to-left run direction.
273 public static final int DIRECTION_RTL = 1;
276 * Option for getTextRunCursor to compute the valid cursor after
277 * offset or the limit of the context, whichever is less.
280 public static final int CURSOR_AFTER = 0;
283 * Option for getTextRunCursor to compute the valid cursor at or after
284 * the offset or the limit of the context, whichever is less.
287 public static final int CURSOR_AT_OR_AFTER = 1;
290 * Option for getTextRunCursor to compute the valid cursor before
291 * offset or the start of the context, whichever is greater.
294 public static final int CURSOR_BEFORE = 2;
297 * Option for getTextRunCursor to compute the valid cursor at or before
298 * offset or the start of the context, whichever is greater.
301 public static final int CURSOR_AT_OR_BEFORE = 3;
304 * Option for getTextRunCursor to return offset if the cursor at offset
305 * is valid, or -1 if it isn't.
308 public static final int CURSOR_AT = 4;
311 * Maximum cursor option value.
313 private static final int CURSOR_OPT_MAX_VALUE = CURSOR_AT;
316 * The Style specifies if the primitive being drawn is filled, stroked, or
317 * both (in the same color). The default is FILL.
321 * Geometry and text drawn with this style will be filled, ignoring all
322 * stroke-related settings in the paint.
326 * Geometry and text drawn with this style will be stroked, respecting
327 * the stroke-related fields on the paint.
331 * Geometry and text drawn with this style will be both filled and
332 * stroked at the same time, respecting the stroke-related fields on
333 * the paint. This mode can give unexpected results if the geometry
334 * is oriented counter-clockwise. This restriction does not apply to
335 * either FILL or STROKE.
339 Style(int nativeInt) {
340 this.nativeInt = nativeInt;
346 * The Cap specifies the treatment for the beginning and ending of
347 * stroked lines and paths. The default is BUTT.
351 * The stroke ends with the path, and does not project beyond it.
355 * The stroke projects out as a semicircle, with the center at the
360 * The stroke projects out as a square, with the center at the end
365 private Cap(int nativeInt) {
366 this.nativeInt = nativeInt;
372 * The Join specifies the treatment where lines and curve segments
373 * join on a stroked path. The default is MITER.
377 * The outer edges of a join meet at a sharp angle
381 * The outer edges of a join meet in a circular arc.
385 * The outer edges of a join meet with a straight line
389 private Join(int nativeInt) {
390 this.nativeInt = nativeInt;
396 * Align specifies how drawText aligns its text relative to the
397 * [x,y] coordinates. The default is LEFT.
401 * The text is drawn to the right of the x,y origin
405 * The text is drawn centered horizontally on the x,y origin
409 * The text is drawn to the left of the x,y origin
413 private Align(int nativeInt) {
414 this.nativeInt = nativeInt;
420 * Create a new paint with default settings.
427 * Create a new paint with the specified flags. Use setFlags() to change
428 * these after the paint is created.
430 * @param flags initial flag bits, as if they were passed via setFlags().
432 public Paint(int flags) {
433 mNativePaint = nInit();
434 setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
435 // TODO: Turning off hinting has undesirable side effects, we need to
436 // revisit hinting once we add support for subpixel positioning
437 // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
438 // ? HINTING_OFF : HINTING_ON);
439 mCompatScaling = mInvCompatScaling = 1;
440 setTextLocales(LocaleList.getDefault());
444 * Create a new paint, initialized with the attributes in the specified
447 * @param paint Existing paint used to initialized the attributes of the
450 public Paint(Paint paint) {
451 mNativePaint = nInitWithPaint(paint.getNativeInstance());
452 setClassVariablesFrom(paint);
455 /** Restores the paint to its default settings. */
456 public void reset() {
457 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
458 nReset(mNativePaint);
459 setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
461 // TODO: Turning off hinting has undesirable side effects, we need to
462 // revisit hinting once we add support for subpixel positioning
463 // setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
464 // ? HINTING_OFF : HINTING_ON);
476 mHasCompatScaling = false;
478 mInvCompatScaling = 1;
480 mBidiFlags = BIDI_DEFAULT_LTR;
481 setTextLocales(LocaleList.getDefault());
482 setElegantTextHeight(false);
483 mFontFeatureSettings = null;
487 * Copy the fields from src into this paint. This is equivalent to calling
488 * get() on all of the src fields, and calling the corresponding set()
491 public void set(Paint src) {
492 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
493 if (src.mNativePaint == 0) throw new NullPointerException("Source is already finalized!");
495 // copy over the native settings
496 nSet(mNativePaint, src.mNativePaint);
497 setClassVariablesFrom(src);
502 * Set all class variables using current values from the given
505 private void setClassVariablesFrom(Paint paint) {
506 mColorFilter = paint.mColorFilter;
507 mMaskFilter = paint.mMaskFilter;
508 mPathEffect = paint.mPathEffect;
509 mRasterizer = paint.mRasterizer;
510 mShader = paint.mShader;
511 mNativeShader = paint.mNativeShader;
512 mTypeface = paint.mTypeface;
513 mNativeTypeface = paint.mNativeTypeface;
514 mXfermode = paint.mXfermode;
516 mHasCompatScaling = paint.mHasCompatScaling;
517 mCompatScaling = paint.mCompatScaling;
518 mInvCompatScaling = paint.mInvCompatScaling;
520 mBidiFlags = paint.mBidiFlags;
521 mLocales = paint.mLocales;
522 mFontFeatureSettings = paint.mFontFeatureSettings;
526 public void setCompatibilityScaling(float factor) {
528 mHasCompatScaling = false;
529 mCompatScaling = mInvCompatScaling = 1.0f;
531 mHasCompatScaling = true;
532 mCompatScaling = factor;
533 mInvCompatScaling = 1.0f/factor;
538 * Return the pointer to the native object while ensuring that any
539 * mutable objects that are attached to the paint are also up-to-date.
543 public long getNativeInstance() {
544 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
545 long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance();
546 if (newNativeShader != mNativeShader) {
547 mNativeShader = newNativeShader;
548 nSetShader(mNativePaint, mNativeShader);
554 * Return the bidi flags on the paint.
556 * @return the bidi flags on the paint
559 public int getBidiFlags() {
564 * Set the bidi flags on the paint.
567 public void setBidiFlags(int flags) {
568 // only flag value is the 3-bit BIDI control setting
569 flags &= BIDI_FLAG_MASK;
570 if (flags > BIDI_MAX_FLAG_VALUE) {
571 throw new IllegalArgumentException("unknown bidi flag: " + flags);
577 * Return the paint's flags. Use the Flag enum to test flag values.
579 * @return the paint's flags (see enums ending in _Flag for bit masks)
581 public int getFlags() {
582 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
583 return nGetFlags(mNativePaint);
586 private native int nGetFlags(long paintPtr);
589 * Set the paint's flags. Use the Flag enum to specific flag values.
591 * @param flags The new flag bits for the paint
593 public void setFlags(int flags) {
594 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
595 nSetFlags(mNativePaint, flags);
598 private native void nSetFlags(long paintPtr, int flags);
601 * Return the paint's hinting mode. Returns either
602 * {@link #HINTING_OFF} or {@link #HINTING_ON}.
604 public int getHinting() {
605 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
606 return nGetHinting(mNativePaint);
609 private native int nGetHinting(long paintPtr);
612 * Set the paint's hinting mode. May be either
613 * {@link #HINTING_OFF} or {@link #HINTING_ON}.
615 public void setHinting(int mode) {
616 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
617 nSetHinting(mNativePaint, mode);
620 private native void nSetHinting(long paintPtr, int mode);
623 * Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set
624 * AntiAliasing smooths out the edges of what is being drawn, but is has
625 * no impact on the interior of the shape. See setDither() and
626 * setFilterBitmap() to affect how colors are treated.
628 * @return true if the antialias bit is set in the paint's flags.
630 public final boolean isAntiAlias() {
631 return (getFlags() & ANTI_ALIAS_FLAG) != 0;
635 * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit
636 * AntiAliasing smooths out the edges of what is being drawn, but is has
637 * no impact on the interior of the shape. See setDither() and
638 * setFilterBitmap() to affect how colors are treated.
640 * @param aa true to set the antialias bit in the flags, false to clear it
642 public void setAntiAlias(boolean aa) {
643 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
644 nSetAntiAlias(mNativePaint, aa);
647 private native void nSetAntiAlias(long paintPtr, boolean aa);
650 * Helper for getFlags(), returning true if DITHER_FLAG bit is set
651 * Dithering affects how colors that are higher precision than the device
652 * are down-sampled. No dithering is generally faster, but higher precision
653 * colors are just truncated down (e.g. 8888 -> 565). Dithering tries to
654 * distribute the error inherent in this process, to reduce the visual
657 * @return true if the dithering bit is set in the paint's flags.
659 public final boolean isDither() {
660 return (getFlags() & DITHER_FLAG) != 0;
664 * Helper for setFlags(), setting or clearing the DITHER_FLAG bit
665 * Dithering affects how colors that are higher precision than the device
666 * are down-sampled. No dithering is generally faster, but higher precision
667 * colors are just truncated down (e.g. 8888 -> 565). Dithering tries to
668 * distribute the error inherent in this process, to reduce the visual
671 * @param dither true to set the dithering bit in flags, false to clear it
673 public void setDither(boolean dither) {
674 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
675 nSetDither(mNativePaint, dither);
678 private native void nSetDither(long paintPtr, boolean dither);
681 * Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set
683 * @return true if the lineartext bit is set in the paint's flags
685 public final boolean isLinearText() {
686 return (getFlags() & LINEAR_TEXT_FLAG) != 0;
690 * Helper for setFlags(), setting or clearing the LINEAR_TEXT_FLAG bit
692 * @param linearText true to set the linearText bit in the paint's flags,
695 public void setLinearText(boolean linearText) {
696 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
697 nSetLinearText(mNativePaint, linearText);
700 private native void nSetLinearText(long paintPtr, boolean linearText);
703 * Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set
705 * @return true if the subpixel bit is set in the paint's flags
707 public final boolean isSubpixelText() {
708 return (getFlags() & SUBPIXEL_TEXT_FLAG) != 0;
712 * Helper for setFlags(), setting or clearing the SUBPIXEL_TEXT_FLAG bit
714 * @param subpixelText true to set the subpixelText bit in the paint's
715 * flags, false to clear it.
717 public void setSubpixelText(boolean subpixelText) {
718 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
719 nSetSubpixelText(mNativePaint, subpixelText);
722 private native void nSetSubpixelText(long paintPtr, boolean subpixelText);
725 * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
727 * @return true if the underlineText bit is set in the paint's flags.
729 public final boolean isUnderlineText() {
730 return (getFlags() & UNDERLINE_TEXT_FLAG) != 0;
734 * Helper for setFlags(), setting or clearing the UNDERLINE_TEXT_FLAG bit
736 * @param underlineText true to set the underlineText bit in the paint's
737 * flags, false to clear it.
739 public void setUnderlineText(boolean underlineText) {
740 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
741 nSetUnderlineText(mNativePaint, underlineText);
744 private native void nSetUnderlineText(long paintPtr, boolean underlineText);
747 * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
749 * @return true if the strikeThruText bit is set in the paint's flags.
751 public final boolean isStrikeThruText() {
752 return (getFlags() & STRIKE_THRU_TEXT_FLAG) != 0;
756 * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit
758 * @param strikeThruText true to set the strikeThruText bit in the paint's
759 * flags, false to clear it.
761 public void setStrikeThruText(boolean strikeThruText) {
762 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
763 nSetStrikeThruText(mNativePaint, strikeThruText);
766 private native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
769 * Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set
771 * @return true if the fakeBoldText bit is set in the paint's flags.
773 public final boolean isFakeBoldText() {
774 return (getFlags() & FAKE_BOLD_TEXT_FLAG) != 0;
778 * Helper for setFlags(), setting or clearing the FAKE_BOLD_TEXT_FLAG bit
780 * @param fakeBoldText true to set the fakeBoldText bit in the paint's
781 * flags, false to clear it.
783 public void setFakeBoldText(boolean fakeBoldText) {
784 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
785 nSetFakeBoldText(mNativePaint, fakeBoldText);
788 private native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
791 * Whether or not the bitmap filter is activated.
792 * Filtering affects the sampling of bitmaps when they are transformed.
793 * Filtering does not affect how the colors in the bitmap are converted into
794 * device pixels. That is dependent on dithering and xfermodes.
796 * @see #setFilterBitmap(boolean) setFilterBitmap()
798 public final boolean isFilterBitmap() {
799 return (getFlags() & FILTER_BITMAP_FLAG) != 0;
803 * Helper for setFlags(), setting or clearing the FILTER_BITMAP_FLAG bit.
804 * Filtering affects the sampling of bitmaps when they are transformed.
805 * Filtering does not affect how the colors in the bitmap are converted into
806 * device pixels. That is dependent on dithering and xfermodes.
808 * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's
809 * flags, false to clear it.
811 public void setFilterBitmap(boolean filter) {
812 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
813 nSetFilterBitmap(mNativePaint, filter);
816 private native void nSetFilterBitmap(long paintPtr, boolean filter);
819 * Return the paint's style, used for controlling how primitives'
820 * geometries are interpreted (except for drawBitmap, which always assumes
823 * @return the paint's style setting (Fill, Stroke, StrokeAndFill)
825 public Style getStyle() {
826 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
827 return sStyleArray[nGetStyle(mNativePaint)];
831 * Set the paint's style, used for controlling how primitives'
832 * geometries are interpreted (except for drawBitmap, which always assumes
835 * @param style The new style to set in the paint
837 public void setStyle(Style style) {
838 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
839 nSetStyle(mNativePaint, style.nativeInt);
843 * Return the paint's color. Note that the color is a 32bit value
844 * containing alpha as well as r,g,b. This 32bit value is not premultiplied,
845 * meaning that its alpha can be any value, regardless of the values of
846 * r,g,b. See the Color class for more details.
848 * @return the paint's color (and alpha).
851 public int getColor() {
852 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
853 return nGetColor(mNativePaint);
856 private native int nGetColor(long paintPtr);
859 * Set the paint's color. Note that the color is an int containing alpha
860 * as well as r,g,b. This 32bit value is not premultiplied, meaning that
861 * its alpha can be any value, regardless of the values of r,g,b.
862 * See the Color class for more details.
864 * @param color The new color (including alpha) to set in the paint.
866 public void setColor(@ColorInt int color) {
867 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
868 nSetColor(mNativePaint, color);
871 private native void nSetColor(long paintPtr, @ColorInt int color);
874 * Helper to getColor() that just returns the color's alpha value. This is
875 * the same as calling getColor() >>> 24. It always returns a value between
876 * 0 (completely transparent) and 255 (completely opaque).
878 * @return the alpha component of the paint's color.
880 public int getAlpha() {
881 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
882 return nGetAlpha(mNativePaint);
885 private native int nGetAlpha(long paintPtr);
888 * Helper to setColor(), that only assigns the color's alpha value,
889 * leaving its r,g,b values unchanged. Results are undefined if the alpha
890 * value is outside of the range [0..255]
892 * @param a set the alpha component [0..255] of the paint's color.
894 public void setAlpha(int a) {
895 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
896 nSetAlpha(mNativePaint, a);
899 private native void nSetAlpha(long paintPtr, int a);
902 * Helper to setColor(), that takes a,r,g,b and constructs the color int
904 * @param a The new alpha component (0..255) of the paint's color.
905 * @param r The new red component (0..255) of the paint's color.
906 * @param g The new green component (0..255) of the paint's color.
907 * @param b The new blue component (0..255) of the paint's color.
909 public void setARGB(int a, int r, int g, int b) {
910 setColor((a << 24) | (r << 16) | (g << 8) | b);
914 * Return the width for stroking.
916 * A value of 0 strokes in hairline mode.
917 * Hairlines always draws a single pixel independent of the canva's matrix.
919 * @return the paint's stroke width, used whenever the paint's style is
920 * Stroke or StrokeAndFill.
922 public float getStrokeWidth() {
923 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
924 return nGetStrokeWidth(mNativePaint);
927 private native float nGetStrokeWidth(long paintPtr);
930 * Set the width for stroking.
931 * Pass 0 to stroke in hairline mode.
932 * Hairlines always draws a single pixel independent of the canva's matrix.
934 * @param width set the paint's stroke width, used whenever the paint's
935 * style is Stroke or StrokeAndFill.
937 public void setStrokeWidth(float width) {
938 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
939 nSetStrokeWidth(mNativePaint, width);
942 private native void nSetStrokeWidth(long paintPtr, float width);
945 * Return the paint's stroke miter value. Used to control the behavior
946 * of miter joins when the joins angle is sharp.
948 * @return the paint's miter limit, used whenever the paint's style is
949 * Stroke or StrokeAndFill.
951 public float getStrokeMiter() {
952 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
953 return nGetStrokeMiter(mNativePaint);
956 private native float nGetStrokeMiter(long paintPtr);
959 * Set the paint's stroke miter value. This is used to control the behavior
960 * of miter joins when the joins angle is sharp. This value must be >= 0.
962 * @param miter set the miter limit on the paint, used whenever the paint's
963 * style is Stroke or StrokeAndFill.
965 public void setStrokeMiter(float miter) {
966 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
967 nSetStrokeMiter(mNativePaint, miter);
970 private native void nSetStrokeMiter(long paintPtr, float miter);
973 * Return the paint's Cap, controlling how the start and end of stroked
974 * lines and paths are treated.
976 * @return the line cap style for the paint, used whenever the paint's
977 * style is Stroke or StrokeAndFill.
979 public Cap getStrokeCap() {
980 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
981 return sCapArray[nGetStrokeCap(mNativePaint)];
985 * Set the paint's Cap.
987 * @param cap set the paint's line cap style, used whenever the paint's
988 * style is Stroke or StrokeAndFill.
990 public void setStrokeCap(Cap cap) {
991 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
992 nSetStrokeCap(mNativePaint, cap.nativeInt);
996 * Return the paint's stroke join type.
998 * @return the paint's Join.
1000 public Join getStrokeJoin() {
1001 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1002 return sJoinArray[nGetStrokeJoin(mNativePaint)];
1006 * Set the paint's Join.
1008 * @param join set the paint's Join, used whenever the paint's style is
1009 * Stroke or StrokeAndFill.
1011 public void setStrokeJoin(Join join) {
1012 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1013 nSetStrokeJoin(mNativePaint, join.nativeInt);
1017 * Applies any/all effects (patheffect, stroking) to src, returning the
1018 * result in dst. The result is that drawing src with this paint will be
1019 * the same as drawing dst with a default paint (at least from the
1020 * geometric perspective).
1022 * @param src input path
1023 * @param dst output path (may be the same as src)
1024 * @return true if the path should be filled, or false if it should be
1025 * drawn with a hairline (width == 0)
1027 public boolean getFillPath(Path src, Path dst) {
1028 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1029 return nGetFillPath(mNativePaint, src.ni(), dst.ni());
1033 * Get the paint's shader object.
1035 * @return the paint's shader (or null)
1037 public Shader getShader() {
1042 * Set or clear the shader object.
1044 * Pass null to clear any previous shader.
1045 * As a convenience, the parameter passed is also returned.
1047 * @param shader May be null. the new shader to be installed in the paint
1050 public Shader setShader(Shader shader) {
1051 // Defer setting the shader natively until getNativeInstance() is called
1057 * Get the paint's colorfilter (maybe be null).
1059 * @return the paint's colorfilter (maybe be null)
1061 public ColorFilter getColorFilter() {
1062 return mColorFilter;
1066 * Set or clear the paint's colorfilter, returning the parameter.
1068 * @param filter May be null. The new filter to be installed in the paint
1071 public ColorFilter setColorFilter(ColorFilter filter) {
1072 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1073 long filterNative = 0;
1075 filterNative = filter.native_instance;
1076 nSetColorFilter(mNativePaint, filterNative);
1077 mColorFilter = filter;
1082 * Get the paint's xfermode object.
1084 * @return the paint's xfermode (or null)
1086 public Xfermode getXfermode() {
1091 * Set or clear the xfermode object.
1093 * Pass null to clear any previous xfermode.
1094 * As a convenience, the parameter passed is also returned.
1096 * @param xfermode May be null. The xfermode to be installed in the paint
1099 public Xfermode setXfermode(Xfermode xfermode) {
1100 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1101 long xfermodeNative = 0;
1102 if (xfermode != null)
1103 xfermodeNative = xfermode.native_instance;
1104 nSetXfermode(mNativePaint, xfermodeNative);
1105 mXfermode = xfermode;
1110 * Get the paint's patheffect object.
1112 * @return the paint's patheffect (or null)
1114 public PathEffect getPathEffect() {
1119 * Set or clear the patheffect object.
1121 * Pass null to clear any previous patheffect.
1122 * As a convenience, the parameter passed is also returned.
1124 * @param effect May be null. The patheffect to be installed in the paint
1127 public PathEffect setPathEffect(PathEffect effect) {
1128 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1129 long effectNative = 0;
1130 if (effect != null) {
1131 effectNative = effect.native_instance;
1133 nSetPathEffect(mNativePaint, effectNative);
1134 mPathEffect = effect;
1139 * Get the paint's maskfilter object.
1141 * @return the paint's maskfilter (or null)
1143 public MaskFilter getMaskFilter() {
1148 * Set or clear the maskfilter object.
1150 * Pass null to clear any previous maskfilter.
1151 * As a convenience, the parameter passed is also returned.
1153 * @param maskfilter May be null. The maskfilter to be installed in the
1155 * @return maskfilter
1157 public MaskFilter setMaskFilter(MaskFilter maskfilter) {
1158 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1159 long maskfilterNative = 0;
1160 if (maskfilter != null) {
1161 maskfilterNative = maskfilter.native_instance;
1163 nSetMaskFilter(mNativePaint, maskfilterNative);
1164 mMaskFilter = maskfilter;
1169 * Get the paint's typeface object.
1171 * The typeface object identifies which font to use when drawing or
1174 * @return the paint's typeface (or null)
1176 public Typeface getTypeface() {
1181 * Set or clear the typeface object.
1183 * Pass null to clear any previous typeface.
1184 * As a convenience, the parameter passed is also returned.
1186 * @param typeface May be null. The typeface to be installed in the paint
1189 public Typeface setTypeface(Typeface typeface) {
1190 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1191 long typefaceNative = 0;
1192 if (typeface != null) {
1193 typefaceNative = typeface.native_instance;
1195 nSetTypeface(mNativePaint, typefaceNative);
1196 mTypeface = typeface;
1197 mNativeTypeface = typefaceNative;
1202 * Get the paint's rasterizer (or null).
1204 * The raster controls/modifies how paths/text are turned into alpha masks.
1206 * @return the paint's rasterizer (or null)
1208 * @deprecated Rasterizer is not supported by either the HW or PDF backends.
1211 public Rasterizer getRasterizer() {
1216 * Set or clear the rasterizer object.
1218 * Pass null to clear any previous rasterizer.
1219 * As a convenience, the parameter passed is also returned.
1221 * @param rasterizer May be null. The new rasterizer to be installed in
1223 * @return rasterizer
1225 * @deprecated Rasterizer is not supported by either the HW or PDF backends.
1228 public Rasterizer setRasterizer(Rasterizer rasterizer) {
1229 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1230 long rasterizerNative = 0;
1231 if (rasterizer != null) {
1232 rasterizerNative = rasterizer.native_instance;
1234 nSetRasterizer(mNativePaint, rasterizerNative);
1235 mRasterizer = rasterizer;
1240 * This draws a shadow layer below the main layer, with the specified
1241 * offset and color, and blur radius. If radius is 0, then the shadow
1244 * Can be used to create a blurred shadow underneath text. Support for use
1245 * with other drawing operations is constrained to the software rendering
1248 * The alpha of the shadow will be the paint's alpha if the shadow color is
1249 * opaque, or the alpha from the shadow color if not.
1251 public void setShadowLayer(float radius, float dx, float dy, int shadowColor) {
1252 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1253 nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
1257 * Clear the shadow layer.
1259 public void clearShadowLayer() {
1260 setShadowLayer(0, 0, 0, 0);
1264 * Checks if the paint has a shadow layer attached
1266 * @return true if the paint has a shadow layer attached and false otherwise
1269 public boolean hasShadowLayer() {
1270 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1271 return nHasShadowLayer(mNativePaint);
1275 * Return the paint's Align value for drawing text. This controls how the
1276 * text is positioned relative to its origin. LEFT align means that all of
1277 * the text will be drawn to the right of its origin (i.e. the origin
1278 * specifieds the LEFT edge of the text) and so on.
1280 * @return the paint's Align value for drawing text.
1282 public Align getTextAlign() {
1283 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1284 return sAlignArray[nGetTextAlign(mNativePaint)];
1288 * Set the paint's text alignment. 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 * @param align set the paint's Align value for drawing text.
1295 public void setTextAlign(Align align) {
1296 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1297 nSetTextAlign(mNativePaint, align.nativeInt);
1301 * Get the text's primary Locale. Note that this is not all of the locale-related information
1302 * Paint has. Use {@link #getTextLocales()} to get the complete list.
1304 * @return the paint's primary Locale used for drawing text, never null.
1307 public Locale getTextLocale() {
1308 return mLocales.getPrimary();
1312 * Get the text locale list.
1314 * @return the paint's LocaleList used for drawing text, never null or empty.
1316 @NonNull @Size(min=1)
1317 public LocaleList getTextLocales() {
1322 * Set the text locale list to a one-member list consisting of just the locale.
1324 * See {@link #setTextLocales(LocaleList)} for how the locale list affects
1325 * the way the text is drawn for some languages.
1327 * @param locale the paint's locale value for drawing text, must not be null.
1329 public void setTextLocale(@NonNull Locale locale) {
1330 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1331 if (locale == null) {
1332 throw new IllegalArgumentException("locale cannot be null");
1334 if (mLocales != null && mLocales.size() == 1 && locale.equals(mLocales.getPrimary())) {
1337 mLocales = new LocaleList(locale);
1338 nSetTextLocales(mNativePaint, locale.toString());
1342 * Set the text locale list.
1344 * The text locale list affects how the text is drawn for some languages.
1346 * For example, if the locale list contains {@link Locale#CHINESE} or {@link Locale#CHINA},
1347 * then the text renderer will prefer to draw text using a Chinese font. Likewise,
1348 * if the locale list contains {@link Locale#JAPANESE} or {@link Locale#JAPAN}, then the text
1349 * renderer will prefer to draw text using a Japanese font. If the locale list contains both,
1350 * the order those locales appear in the list is considered for deciding the font.
1352 * This distinction is important because Chinese and Japanese text both use many
1353 * of the same Unicode code points but their appearance is subtly different for
1356 * By default, the text locale list is initialized to a one-member list just containing the
1357 * system locale (as returned by {@link LocaleList#getDefault()}). This assumes that the text to
1358 * be rendered will most likely be in the user's preferred language.
1360 * If the actual language or languages of the text is/are known, then they can be provided to
1361 * the text renderer using this method. The text renderer may attempt to guess the
1362 * language script based on the contents of the text to be drawn independent of
1363 * the text locale here. Specifying the text locales just helps it do a better
1364 * job in certain ambiguous cases.
1366 * @param locales the paint's locale list for drawing text, must not be null or empty.
1368 public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
1369 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1370 if (locales == null || locales.isEmpty()) {
1371 throw new IllegalArgumentException("locales cannot be null or empty");
1373 if (locales.equals(mLocales)) return;
1375 nSetTextLocales(mNativePaint, locales.toLanguageTags());
1379 * Get the elegant metrics flag.
1381 * @return true if elegant metrics are enabled for text drawing.
1383 public boolean isElegantTextHeight() {
1384 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1385 return nIsElegantTextHeight(mNativePaint);
1388 private native boolean nIsElegantTextHeight(long paintPtr);
1391 * Set the paint's elegant height metrics flag. This setting selects font
1392 * variants that have not been compacted to fit Latin-based vertical
1393 * metrics, and also increases top and bottom bounds to provide more space.
1395 * @param elegant set the paint's elegant metrics flag for drawing text.
1397 public void setElegantTextHeight(boolean elegant) {
1398 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1399 nSetElegantTextHeight(mNativePaint, elegant);
1402 private native void nSetElegantTextHeight(long paintPtr, boolean elegant);
1405 * Return the paint's text size.
1407 * @return the paint's text size.
1409 public float getTextSize() {
1410 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1411 return nGetTextSize(mNativePaint);
1414 private native float nGetTextSize(long paintPtr);
1417 * Set the paint's text size. This value must be > 0
1419 * @param textSize set the paint's text size.
1421 public void setTextSize(float textSize) {
1422 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1423 nSetTextSize(mNativePaint, textSize);
1426 private native void nSetTextSize(long paintPtr, float textSize);
1429 * Return the paint's horizontal scale factor for text. The default value
1432 * @return the paint's scale factor in X for drawing/measuring text
1434 public float getTextScaleX() {
1435 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1436 return nGetTextScaleX(mNativePaint);
1439 private native float nGetTextScaleX(long paintPtr);
1442 * Set the paint's horizontal scale factor for text. The default value
1443 * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
1444 * stretch the text narrower.
1446 * @param scaleX set the paint's scale in X for drawing/measuring text.
1448 public void setTextScaleX(float scaleX) {
1449 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1450 nSetTextScaleX(mNativePaint, scaleX);
1453 private native void nSetTextScaleX(long paintPtr, float scaleX);
1456 * Return the paint's horizontal skew factor for text. The default value
1459 * @return the paint's skew factor in X for drawing text.
1461 public float getTextSkewX() {
1462 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1463 return nGetTextSkewX(mNativePaint);
1466 private native float nGetTextSkewX(long paintPtr);
1469 * Set the paint's horizontal skew factor for text. The default value
1470 * is 0. For approximating oblique text, use values around -0.25.
1472 * @param skewX set the paint's skew factor in X for drawing text.
1474 public void setTextSkewX(float skewX) {
1475 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1476 nSetTextSkewX(mNativePaint, skewX);
1479 private native void nSetTextSkewX(long paintPtr, float skewX);
1482 * Return the paint's letter-spacing for text. The default value
1485 * @return the paint's letter-spacing for drawing text.
1487 public float getLetterSpacing() {
1488 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1489 return nGetLetterSpacing(mNativePaint);
1493 * Set the paint's letter-spacing for text. The default value
1494 * is 0. The value is in 'EM' units. Typical values for slight
1495 * expansion will be around 0.05. Negative values tighten text.
1497 * @param letterSpacing set the paint's letter-spacing for drawing text.
1499 public void setLetterSpacing(float letterSpacing) {
1500 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1501 nSetLetterSpacing(mNativePaint, letterSpacing);
1505 * Get font feature settings. Default is null.
1507 * @return the paint's currently set font feature settings.
1509 public String getFontFeatureSettings() {
1510 return mFontFeatureSettings;
1514 * Set font feature settings.
1516 * The format is the same as the CSS font-feature-settings attribute:
1517 * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings
1519 * @param settings the font feature settings string to use, may be null.
1521 public void setFontFeatureSettings(String settings) {
1522 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1523 if (settings != null && settings.equals("")) {
1526 if ((settings == null && mFontFeatureSettings == null)
1527 || (settings != null && settings.equals(mFontFeatureSettings))) {
1530 mFontFeatureSettings = settings;
1531 nSetFontFeatureSettings(mNativePaint, settings);
1535 * Get the current value of hyphen edit.
1537 * @return the current hyphen edit value
1541 public int getHyphenEdit() {
1542 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1543 return nGetHyphenEdit(mNativePaint);
1547 * Set a hyphen edit on the paint (causes a hyphen to be added to text when
1548 * measured or drawn).
1550 * @param hyphen 0 for no edit, 1 for adding a hyphen (other values in future)
1554 public void setHyphenEdit(int hyphen) {
1555 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1556 nSetHyphenEdit(mNativePaint, hyphen);
1560 * Return the distance above (negative) the baseline (ascent) based on the
1561 * current typeface and text size.
1563 * @return the distance above (negative) the baseline (ascent) based on the
1564 * current typeface and text size.
1566 public float ascent() {
1567 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1568 return nAscent(mNativePaint, mNativeTypeface);
1571 private native float nAscent(long paintPtr, long typefacePtr);
1574 * Return the distance below (positive) the baseline (descent) based on the
1575 * current typeface and text size.
1577 * @return the distance below (positive) the baseline (descent) based on
1578 * the current typeface and text size.
1580 public float descent() {
1581 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1582 return nDescent(mNativePaint, mNativeTypeface);
1585 private native float nDescent(long paintPtr, long typefacePtr);
1588 * Class that describes the various metrics for a font at a given text size.
1589 * Remember, Y values increase going down, so those values will be positive,
1590 * and values that measure distances going up will be negative. This class
1591 * is returned by getFontMetrics().
1593 public static class FontMetrics {
1595 * The maximum distance above the baseline for the tallest glyph in
1596 * the font at a given text size.
1600 * The recommended distance above the baseline for singled spaced text.
1602 public float ascent;
1604 * The recommended distance below the baseline for singled spaced text.
1606 public float descent;
1608 * The maximum distance below the baseline for the lowest glyph in
1609 * the font at a given text size.
1611 public float bottom;
1613 * The recommended additional space to add between lines of text.
1615 public float leading;
1619 * Return the font's recommended interline spacing, given the Paint's
1620 * settings for typeface, textSize, etc. If metrics is not null, return the
1621 * fontmetric values in it.
1623 * @param metrics If this object is not null, its fields are filled with
1624 * the appropriate values given the paint's text attributes.
1625 * @return the font's recommended interline spacing.
1627 public float getFontMetrics(FontMetrics metrics) {
1628 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1629 return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
1632 private native float nGetFontMetrics(long paintPtr,
1633 long typefacePtr, FontMetrics metrics);
1636 * Allocates a new FontMetrics object, and then calls getFontMetrics(fm)
1637 * with it, returning the object.
1639 public FontMetrics getFontMetrics() {
1640 FontMetrics fm = new FontMetrics();
1646 * Convenience method for callers that want to have FontMetrics values as
1649 public static class FontMetricsInt {
1656 @Override public String toString() {
1657 return "FontMetricsInt: top=" + top + " ascent=" + ascent +
1658 " descent=" + descent + " bottom=" + bottom +
1659 " leading=" + leading;
1664 * Return the font's interline spacing, given the Paint's settings for
1665 * typeface, textSize, etc. If metrics is not null, return the fontmetric
1666 * values in it. Note: all values have been converted to integers from
1667 * floats, in such a way has to make the answers useful for both spacing
1668 * and clipping. If you want more control over the rounding, call
1671 * @return the font's interline spacing.
1673 public int getFontMetricsInt(FontMetricsInt fmi) {
1674 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1675 return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
1678 private native int nGetFontMetricsInt(long paintPtr,
1679 long typefacePtr, FontMetricsInt fmi);
1681 public FontMetricsInt getFontMetricsInt() {
1682 FontMetricsInt fm = new FontMetricsInt();
1683 getFontMetricsInt(fm);
1688 * Return the recommend line spacing based on the current typeface and
1691 * @return recommend line spacing based on the current typeface and
1694 public float getFontSpacing() {
1695 return getFontMetrics(null);
1699 * Return the width of the text.
1701 * @param text The text to measure. Cannot be null.
1702 * @param index The index of the first character to start measuring
1703 * @param count THe number of characters to measure, beginning with start
1704 * @return The width of the text
1706 public float measureText(char[] text, int index, int count) {
1707 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1709 throw new IllegalArgumentException("text cannot be null");
1711 if ((index | count) < 0 || index + count > text.length) {
1712 throw new ArrayIndexOutOfBoundsException();
1715 if (text.length == 0 || count == 0) {
1718 if (!mHasCompatScaling) {
1719 return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
1720 index, count, index, count, mBidiFlags, null, 0));
1723 final float oldSize = getTextSize();
1724 setTextSize(oldSize * mCompatScaling);
1725 float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index,
1726 count, mBidiFlags, null, 0);
1727 setTextSize(oldSize);
1728 return (float) Math.ceil(w*mInvCompatScaling);
1732 * Return the width of the text.
1734 * @param text The text to measure. Cannot be null.
1735 * @param start The index of the first character to start measuring
1736 * @param end 1 beyond the index of the last character to measure
1737 * @return The width of the text
1739 public float measureText(String text, int start, int end) {
1740 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1742 throw new IllegalArgumentException("text cannot be null");
1744 if ((start | end | (end - start) | (text.length() - end)) < 0) {
1745 throw new IndexOutOfBoundsException();
1748 if (text.length() == 0 || start == end) {
1751 if (!mHasCompatScaling) {
1752 return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
1753 start, end, start, end, mBidiFlags, null, 0));
1755 final float oldSize = getTextSize();
1756 setTextSize(oldSize * mCompatScaling);
1757 float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start,
1758 end, mBidiFlags, null, 0);
1759 setTextSize(oldSize);
1760 return (float) Math.ceil(w * mInvCompatScaling);
1764 * Return the width of the text.
1766 * @param text The text to measure. Cannot be null.
1767 * @return The width of the text
1769 public float measureText(String text) {
1770 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1772 throw new IllegalArgumentException("text cannot be null");
1774 return measureText(text, 0, text.length());
1778 * Return the width of the text.
1780 * @param text The text to measure
1781 * @param start The index of the first character to start measuring
1782 * @param end 1 beyond the index of the last character to measure
1783 * @return The width of the text
1785 public float measureText(CharSequence text, int start, int end) {
1787 throw new IllegalArgumentException("text cannot be null");
1789 if ((start | end | (end - start) | (text.length() - end)) < 0) {
1790 throw new IndexOutOfBoundsException();
1793 if (text.length() == 0 || start == end) {
1796 if (text instanceof String) {
1797 return measureText((String)text, start, end);
1799 if (text instanceof SpannedString ||
1800 text instanceof SpannableString) {
1801 return measureText(text.toString(), start, end);
1803 if (text instanceof GraphicsOperations) {
1804 return ((GraphicsOperations)text).measureText(start, end, this);
1807 char[] buf = TemporaryBuffer.obtain(end - start);
1808 TextUtils.getChars(text, start, end, buf, 0);
1809 float result = measureText(buf, 0, end - start);
1810 TemporaryBuffer.recycle(buf);
1815 * Measure the text, stopping early if the measured width exceeds maxWidth.
1816 * Return the number of chars that were measured, and if measuredWidth is
1817 * not null, return in it the actual width measured.
1819 * @param text The text to measure. Cannot be null.
1820 * @param index The offset into text to begin measuring at
1821 * @param count The number of maximum number of entries to measure. If count
1822 * is negative, then the characters are measured in reverse order.
1823 * @param maxWidth The maximum width to accumulate.
1824 * @param measuredWidth Optional. If not null, returns the actual width
1826 * @return The number of chars that were measured. Will always be <=
1829 public int breakText(char[] text, int index, int count,
1830 float maxWidth, float[] measuredWidth) {
1831 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1833 throw new IllegalArgumentException("text cannot be null");
1835 if (index < 0 || text.length - index < Math.abs(count)) {
1836 throw new ArrayIndexOutOfBoundsException();
1839 if (text.length == 0 || count == 0) {
1842 if (!mHasCompatScaling) {
1843 return nBreakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
1844 mBidiFlags, measuredWidth);
1847 final float oldSize = getTextSize();
1848 setTextSize(oldSize * mCompatScaling);
1849 int res = nBreakText(mNativePaint, mNativeTypeface, text, index, count,
1850 maxWidth * mCompatScaling, mBidiFlags, measuredWidth);
1851 setTextSize(oldSize);
1852 if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
1856 private static native int nBreakText(long nObject, long nTypeface,
1857 char[] text, int index, int count,
1858 float maxWidth, int bidiFlags, float[] measuredWidth);
1861 * Measure the text, stopping early if the measured width exceeds maxWidth.
1862 * Return the number of chars that were measured, and if measuredWidth is
1863 * not null, return in it the actual width measured.
1865 * @param text The text to measure. Cannot be null.
1866 * @param start The offset into text to begin measuring at
1867 * @param end The end of the text slice to measure.
1868 * @param measureForwards If true, measure forwards, starting at start.
1869 * Otherwise, measure backwards, starting with end.
1870 * @param maxWidth The maximum width to accumulate.
1871 * @param measuredWidth Optional. If not null, returns the actual width
1873 * @return The number of chars that were measured. Will always be <=
1876 public int breakText(CharSequence text, int start, int end,
1877 boolean measureForwards,
1878 float maxWidth, float[] measuredWidth) {
1879 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1881 throw new IllegalArgumentException("text cannot be null");
1883 if ((start | end | (end - start) | (text.length() - end)) < 0) {
1884 throw new IndexOutOfBoundsException();
1887 if (text.length() == 0 || start == end) {
1890 if (start == 0 && text instanceof String && end == text.length()) {
1891 return breakText((String) text, measureForwards, maxWidth,
1895 char[] buf = TemporaryBuffer.obtain(end - start);
1898 TextUtils.getChars(text, start, end, buf, 0);
1900 if (measureForwards) {
1901 result = breakText(buf, 0, end - start, maxWidth, measuredWidth);
1903 result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth);
1906 TemporaryBuffer.recycle(buf);
1911 * Measure the text, stopping early if the measured width exceeds maxWidth.
1912 * Return the number of chars that were measured, and if measuredWidth is
1913 * not null, return in it the actual width measured.
1915 * @param text The text to measure. Cannot be null.
1916 * @param measureForwards If true, measure forwards, starting with the
1917 * first character in the string. Otherwise,
1918 * measure backwards, starting with the
1919 * last character in the string.
1920 * @param maxWidth The maximum width to accumulate.
1921 * @param measuredWidth Optional. If not null, returns the actual width
1923 * @return The number of chars that were measured. Will always be <=
1926 public int breakText(String text, boolean measureForwards,
1927 float maxWidth, float[] measuredWidth) {
1928 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1930 throw new IllegalArgumentException("text cannot be null");
1933 if (text.length() == 0) {
1936 if (!mHasCompatScaling) {
1937 return nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
1938 maxWidth, mBidiFlags, measuredWidth);
1941 final float oldSize = getTextSize();
1942 setTextSize(oldSize*mCompatScaling);
1943 int res = nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
1944 maxWidth*mCompatScaling, mBidiFlags, measuredWidth);
1945 setTextSize(oldSize);
1946 if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
1950 private static native int nBreakText(long nObject, long nTypeface,
1951 String text, boolean measureForwards,
1952 float maxWidth, int bidiFlags, float[] measuredWidth);
1955 * Return the advance widths for the characters in the string.
1957 * @param text The text to measure. Cannot be null.
1958 * @param index The index of the first char to to measure
1959 * @param count The number of chars starting with index to measure
1960 * @param widths array to receive the advance widths of the characters.
1961 * Must be at least a large as count.
1962 * @return the actual number of widths returned.
1964 public int getTextWidths(char[] text, int index, int count,
1966 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
1968 throw new IllegalArgumentException("text cannot be null");
1970 if ((index | count) < 0 || index + count > text.length
1971 || count > widths.length) {
1972 throw new ArrayIndexOutOfBoundsException();
1975 if (text.length == 0 || count == 0) {
1978 if (!mHasCompatScaling) {
1979 nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
1980 mBidiFlags, widths, 0);
1984 final float oldSize = getTextSize();
1985 setTextSize(oldSize * mCompatScaling);
1986 nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
1987 mBidiFlags, widths, 0);
1988 setTextSize(oldSize);
1989 for (int i = 0; i < count; i++) {
1990 widths[i] *= mInvCompatScaling;
1996 * Return the advance widths for the characters in the string.
1998 * @param text The text to measure. Cannot be null.
1999 * @param start The index of the first char to to measure
2000 * @param end The end of the text slice to measure
2001 * @param widths array to receive the advance widths of the characters.
2002 * Must be at least a large as (end - start).
2003 * @return the actual number of widths returned.
2005 public int getTextWidths(CharSequence text, int start, int end,
2008 throw new IllegalArgumentException("text cannot be null");
2010 if ((start | end | (end - start) | (text.length() - end)) < 0) {
2011 throw new IndexOutOfBoundsException();
2013 if (end - start > widths.length) {
2014 throw new ArrayIndexOutOfBoundsException();
2017 if (text.length() == 0 || start == end) {
2020 if (text instanceof String) {
2021 return getTextWidths((String) text, start, end, widths);
2023 if (text instanceof SpannedString ||
2024 text instanceof SpannableString) {
2025 return getTextWidths(text.toString(), start, end, widths);
2027 if (text instanceof GraphicsOperations) {
2028 return ((GraphicsOperations) text).getTextWidths(start, end,
2032 char[] buf = TemporaryBuffer.obtain(end - start);
2033 TextUtils.getChars(text, start, end, buf, 0);
2034 int result = getTextWidths(buf, 0, end - start, widths);
2035 TemporaryBuffer.recycle(buf);
2040 * Return the advance widths for the characters in the string.
2042 * @param text The text to measure. Cannot be null.
2043 * @param start The index of the first char to to measure
2044 * @param end The end of the text slice to measure
2045 * @param widths array to receive the advance widths of the characters.
2046 * Must be at least a large as the text.
2047 * @return the number of code units in the specified text.
2049 public int getTextWidths(String text, int start, int end, float[] widths) {
2050 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2052 throw new IllegalArgumentException("text cannot be null");
2054 if ((start | end | (end - start) | (text.length() - end)) < 0) {
2055 throw new IndexOutOfBoundsException();
2057 if (end - start > widths.length) {
2058 throw new ArrayIndexOutOfBoundsException();
2061 if (text.length() == 0 || start == end) {
2064 if (!mHasCompatScaling) {
2065 nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
2066 mBidiFlags, widths, 0);
2070 final float oldSize = getTextSize();
2071 setTextSize(oldSize * mCompatScaling);
2072 nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
2073 mBidiFlags, widths, 0);
2074 setTextSize(oldSize);
2075 for (int i = 0; i < end - start; i++) {
2076 widths[i] *= mInvCompatScaling;
2082 * Return the advance widths for the characters in the string.
2084 * @param text The text to measure
2085 * @param widths array to receive the advance widths of the characters.
2086 * Must be at least a large as the text.
2087 * @return the number of code units in the specified text.
2089 public int getTextWidths(String text, float[] widths) {
2090 return getTextWidths(text, 0, text.length(), widths);
2094 * Convenience overload that takes a char array instead of a
2097 * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
2100 public float getTextRunAdvances(char[] chars, int index, int count,
2101 int contextIndex, int contextCount, boolean isRtl, float[] advances,
2102 int advancesIndex) {
2104 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2105 if (chars == null) {
2106 throw new IllegalArgumentException("text cannot be null");
2108 if ((index | count | contextIndex | contextCount | advancesIndex
2109 | (index - contextIndex) | (contextCount - count)
2110 | ((contextIndex + contextCount) - (index + count))
2111 | (chars.length - (contextIndex + contextCount))
2112 | (advances == null ? 0 :
2113 (advances.length - (advancesIndex + count)))) < 0) {
2114 throw new IndexOutOfBoundsException();
2117 if (chars.length == 0 || count == 0){
2120 if (!mHasCompatScaling) {
2121 return nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
2122 contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
2126 final float oldSize = getTextSize();
2127 setTextSize(oldSize * mCompatScaling);
2128 float res = nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
2129 contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
2131 setTextSize(oldSize);
2133 if (advances != null) {
2134 for (int i = advancesIndex, e = i + count; i < e; i++) {
2135 advances[i] *= mInvCompatScaling;
2138 return res * mInvCompatScaling; // assume errors are not significant
2142 * Convenience overload that takes a CharSequence instead of a
2145 * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
2148 public float getTextRunAdvances(CharSequence text, int start, int end,
2149 int contextStart, int contextEnd, boolean isRtl, float[] advances,
2150 int advancesIndex) {
2151 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2153 throw new IllegalArgumentException("text cannot be null");
2155 if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
2156 | (start - contextStart) | (contextEnd - end)
2157 | (text.length() - contextEnd)
2158 | (advances == null ? 0 :
2159 (advances.length - advancesIndex - (end - start)))) < 0) {
2160 throw new IndexOutOfBoundsException();
2163 if (text instanceof String) {
2164 return getTextRunAdvances((String) text, start, end,
2165 contextStart, contextEnd, isRtl, advances, advancesIndex);
2167 if (text instanceof SpannedString ||
2168 text instanceof SpannableString) {
2169 return getTextRunAdvances(text.toString(), start, end,
2170 contextStart, contextEnd, isRtl, advances, advancesIndex);
2172 if (text instanceof GraphicsOperations) {
2173 return ((GraphicsOperations) text).getTextRunAdvances(start, end,
2174 contextStart, contextEnd, isRtl, advances, advancesIndex, this);
2176 if (text.length() == 0 || end == start) {
2180 int contextLen = contextEnd - contextStart;
2181 int len = end - start;
2182 char[] buf = TemporaryBuffer.obtain(contextLen);
2183 TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
2184 float result = getTextRunAdvances(buf, start - contextStart, len,
2185 0, contextLen, isRtl, advances, advancesIndex);
2186 TemporaryBuffer.recycle(buf);
2191 * Returns the total advance width for the characters in the run
2192 * between start and end, and if advances is not null, the advance
2193 * assigned to each of these characters (java chars).
2195 * <p>The trailing surrogate in a valid surrogate pair is assigned
2196 * an advance of 0. Thus the number of returned advances is
2197 * always equal to count, not to the number of unicode codepoints
2198 * represented by the run.
2200 * <p>In the case of conjuncts or combining marks, the total
2201 * advance is assigned to the first logical character, and the
2202 * following characters are assigned an advance of 0.
2204 * <p>This generates the sum of the advances of glyphs for
2205 * characters in a reordered cluster as the width of the first
2206 * logical character in the cluster, and 0 for the widths of all
2207 * other characters in the cluster. In effect, such clusters are
2208 * treated like conjuncts.
2210 * <p>The shaping bounds limit the amount of context available
2211 * outside start and end that can be used for shaping analysis.
2212 * These bounds typically reflect changes in bidi level or font
2213 * metrics across which shaping does not occur.
2215 * @param text the text to measure. Cannot be null.
2216 * @param start the index of the first character to measure
2217 * @param end the index past the last character to measure
2218 * @param contextStart the index of the first character to use for shaping context,
2220 * @param contextEnd the index past the last character to use for shaping context,
2222 * @param isRtl whether the run is in RTL direction
2223 * @param advances array to receive the advances, must have room for all advances,
2224 * can be null if only total advance is needed
2225 * @param advancesIndex the position in advances at which to put the
2226 * advance corresponding to the character at start
2227 * @return the total advance
2231 public float getTextRunAdvances(String text, int start, int end, int contextStart,
2232 int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
2233 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2235 throw new IllegalArgumentException("text cannot be null");
2237 if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
2238 | (start - contextStart) | (contextEnd - end)
2239 | (text.length() - contextEnd)
2240 | (advances == null ? 0 :
2241 (advances.length - advancesIndex - (end - start)))) < 0) {
2242 throw new IndexOutOfBoundsException();
2245 if (text.length() == 0 || start == end) {
2249 if (!mHasCompatScaling) {
2250 return nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end,
2251 contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
2255 final float oldSize = getTextSize();
2256 setTextSize(oldSize * mCompatScaling);
2257 float totalAdvance = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start,
2258 end, contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
2260 setTextSize(oldSize);
2262 if (advances != null) {
2263 for (int i = advancesIndex, e = i + (end - start); i < e; i++) {
2264 advances[i] *= mInvCompatScaling;
2267 return totalAdvance * mInvCompatScaling; // assume errors are insignificant
2271 * Returns the next cursor position in the run. This avoids placing the
2272 * cursor between surrogates, between characters that form conjuncts,
2273 * between base characters and combining marks, or within a reordering
2276 * <p>ContextStart and offset are relative to the start of text.
2277 * The context is the shaping context for cursor movement, generally
2278 * the bounds of the metric span enclosing the cursor in the direction of
2281 * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
2282 * cursor position, this returns -1. Otherwise this will never return a
2283 * value before contextStart or after contextStart + contextLength.
2285 * @param text the text
2286 * @param contextStart the start of the context
2287 * @param contextLength the length of the context
2288 * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
2289 * @param offset the cursor position to move from
2290 * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
2291 * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
2292 * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
2293 * @return the offset of the next position, or -1
2296 public int getTextRunCursor(char[] text, int contextStart, int contextLength,
2297 int dir, int offset, int cursorOpt) {
2298 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2299 int contextEnd = contextStart + contextLength;
2300 if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
2301 | (offset - contextStart) | (contextEnd - offset)
2302 | (text.length - contextEnd) | cursorOpt) < 0)
2303 || cursorOpt > CURSOR_OPT_MAX_VALUE) {
2304 throw new IndexOutOfBoundsException();
2307 return nGetTextRunCursor(mNativePaint, text,
2308 contextStart, contextLength, dir, offset, cursorOpt);
2312 * Returns the next cursor position in the run. This avoids placing the
2313 * cursor between surrogates, between characters that form conjuncts,
2314 * between base characters and combining marks, or within a reordering
2317 * <p>ContextStart, contextEnd, and offset are relative to the start of
2318 * text. The context is the shaping context for cursor movement, generally
2319 * the bounds of the metric span enclosing the cursor in the direction of
2322 * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
2323 * cursor position, this returns -1. Otherwise this will never return a
2324 * value before contextStart or after contextEnd.
2326 * @param text the text
2327 * @param contextStart the start of the context
2328 * @param contextEnd the end of the context
2329 * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
2330 * @param offset the cursor position to move from
2331 * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
2332 * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
2333 * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
2334 * @return the offset of the next position, or -1
2337 public int getTextRunCursor(CharSequence text, int contextStart,
2338 int contextEnd, int dir, int offset, int cursorOpt) {
2340 if (text instanceof String || text instanceof SpannedString ||
2341 text instanceof SpannableString) {
2342 return getTextRunCursor(text.toString(), contextStart, contextEnd,
2343 dir, offset, cursorOpt);
2345 if (text instanceof GraphicsOperations) {
2346 return ((GraphicsOperations) text).getTextRunCursor(
2347 contextStart, contextEnd, dir, offset, cursorOpt, this);
2350 int contextLen = contextEnd - contextStart;
2351 char[] buf = TemporaryBuffer.obtain(contextLen);
2352 TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
2353 int relPos = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt);
2354 TemporaryBuffer.recycle(buf);
2355 return (relPos == -1) ? -1 : relPos + contextStart;
2359 * Returns the next cursor position in the run. This avoids placing the
2360 * cursor between surrogates, between characters that form conjuncts,
2361 * between base characters and combining marks, or within a reordering
2364 * <p>ContextStart, contextEnd, and offset are relative to the start of
2365 * text. The context is the shaping context for cursor movement, generally
2366 * the bounds of the metric span enclosing the cursor in the direction of
2369 * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
2370 * cursor position, this returns -1. Otherwise this will never return a
2371 * value before contextStart or after contextEnd.
2373 * @param text the text
2374 * @param contextStart the start of the context
2375 * @param contextEnd the end of the context
2376 * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
2377 * @param offset the cursor position to move from
2378 * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
2379 * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
2380 * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
2381 * @return the offset of the next position, or -1
2384 public int getTextRunCursor(String text, int contextStart, int contextEnd,
2385 int dir, int offset, int cursorOpt) {
2386 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2387 if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
2388 | (offset - contextStart) | (contextEnd - offset)
2389 | (text.length() - contextEnd) | cursorOpt) < 0)
2390 || cursorOpt > CURSOR_OPT_MAX_VALUE) {
2391 throw new IndexOutOfBoundsException();
2394 return nGetTextRunCursor(mNativePaint, text,
2395 contextStart, contextEnd, dir, offset, cursorOpt);
2399 * Return the path (outline) for the specified text.
2400 * Note: just like Canvas.drawText, this will respect the Align setting in
2403 * @param text The text to retrieve the path from
2404 * @param index The index of the first character in text
2405 * @param count The number of characterss starting with index
2406 * @param x The x coordinate of the text's origin
2407 * @param y The y coordinate of the text's origin
2408 * @param path The path to receive the data describing the text. Must
2409 * be allocated by the caller.
2411 public void getTextPath(char[] text, int index, int count,
2412 float x, float y, Path path) {
2413 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2414 if ((index | count) < 0 || index + count > text.length) {
2415 throw new ArrayIndexOutOfBoundsException();
2417 nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
2422 * Return the path (outline) for the specified text.
2423 * Note: just like Canvas.drawText, this will respect the Align setting
2426 * @param text The text to retrieve the path from
2427 * @param start The first character in the text
2428 * @param end 1 past the last charcter in the text
2429 * @param x The x coordinate of the text's origin
2430 * @param y The y coordinate of the text's origin
2431 * @param path The path to receive the data describing the text. Must
2432 * be allocated by the caller.
2434 public void getTextPath(String text, int start, int end,
2435 float x, float y, Path path) {
2436 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2437 if ((start | end | (end - start) | (text.length() - end)) < 0) {
2438 throw new IndexOutOfBoundsException();
2440 nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
2445 * Return in bounds (allocated by the caller) the smallest rectangle that
2446 * encloses all of the characters, with an implied origin at (0,0).
2448 * @param text String to measure and return its bounds
2449 * @param start Index of the first char in the string to measure
2450 * @param end 1 past the last char in the string measure
2451 * @param bounds Returns the unioned bounds of all the text. Must be
2452 * allocated by the caller.
2454 public void getTextBounds(String text, int start, int end, Rect bounds) {
2455 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2456 if ((start | end | (end - start) | (text.length() - end)) < 0) {
2457 throw new IndexOutOfBoundsException();
2459 if (bounds == null) {
2460 throw new NullPointerException("need bounds Rect");
2462 nGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
2466 * Return in bounds (allocated by the caller) the smallest rectangle that
2467 * encloses all of the characters, with an implied origin at (0,0).
2469 * @param text Array of chars to measure and return their unioned bounds
2470 * @param index Index of the first char in the array to measure
2471 * @param count The number of chars, beginning at index, to measure
2472 * @param bounds Returns the unioned bounds of all the text. Must be
2473 * allocated by the caller.
2475 public void getTextBounds(char[] text, int index, int count, Rect bounds) {
2476 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2477 if ((index | count) < 0 || index + count > text.length) {
2478 throw new ArrayIndexOutOfBoundsException();
2480 if (bounds == null) {
2481 throw new NullPointerException("need bounds Rect");
2483 nGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
2488 * Determine whether the typeface set on the paint has a glyph supporting the string. The
2489 * simplest case is when the string contains a single character, in which this method
2490 * determines whether the font has the character. In the case of multiple characters, the
2491 * method returns true if there is a single glyph representing the ligature. For example, if
2492 * the input is a pair of regional indicator symbols, determine whether there is an emoji flag
2495 * <p>Finally, if the string contains a variation selector, the method only returns true if
2496 * the fonts contains a glyph specific to that variation.
2498 * <p>Checking is done on the entire fallback chain, not just the immediate font referenced.
2500 * @param string the string to test whether there is glyph support
2501 * @return true if the typeface has a glyph for the string
2503 public boolean hasGlyph(String string) {
2504 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2505 return nHasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
2509 * Measure cursor position within a run of text.
2511 * <p>The run of text includes the characters from {@code start} to {@code end} in the text. In
2512 * addition, the range {@code contextStart} to {@code contextEnd} is used as context for the
2513 * purpose of complex text shaping, such as Arabic text potentially shaped differently based on
2514 * the text next to it.
2516 * <p>All text outside the range {@code contextStart..contextEnd} is ignored. The text between
2517 * {@code start} and {@code end} will be laid out to be measured.
2519 * <p>The returned width measurement is the advance from {@code start} to {@code offset}. It is
2520 * generally a positive value, no matter the direction of the run. If {@code offset == end},
2521 * the return value is simply the width of the whole run from {@code start} to {@code end}.
2523 * <p>Ligatures are formed for characters in the range {@code start..end} (but not for
2524 * {@code start..contextStart} or {@code end..contextEnd}). If {@code offset} points to a
2525 * character in the middle of such a formed ligature, but at a grapheme cluster boundary, the
2526 * return value will also reflect an advance in the middle of the ligature. See
2527 * {@link #getOffsetForAdvance} for more discussion of grapheme cluster boundaries.
2529 * <p>The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is
2530 * suitable only for runs of a single direction.
2532 * <p>All indices are relative to the start of {@code text}. Further, {@code 0 <= contextStart
2533 * <= start <= offset <= end <= contextEnd <= text.length} must hold on entry.
2535 * @param text the text to measure. Cannot be null.
2536 * @param start the index of the start of the range to measure
2537 * @param end the index + 1 of the end of the range to measure
2538 * @param contextStart the index of the start of the shaping context
2539 * @param contextEnd the index + 1 of the end of the shaping context
2540 * @param isRtl whether the run is in RTL direction
2541 * @param offset index of caret position
2542 * @return width measurement between start and offset
2544 public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd,
2545 boolean isRtl, int offset) {
2546 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2548 throw new IllegalArgumentException("text cannot be null");
2550 if ((contextStart | start | offset | end | contextEnd
2551 | start - contextStart | offset - start | end - offset
2552 | contextEnd - end | text.length - contextEnd) < 0) {
2553 throw new IndexOutOfBoundsException();
2558 // TODO: take mCompatScaling into account (or eliminate compat scaling)?
2559 return nGetRunAdvance(mNativePaint, mNativeTypeface, text, start, end,
2560 contextStart, contextEnd, isRtl, offset);
2564 * @see #getRunAdvance(char[], int, int, int, int, boolean, int)
2566 * @param text the text to measure. Cannot be null.
2567 * @param start the index of the start of the range to measure
2568 * @param end the index + 1 of the end of the range to measure
2569 * @param contextStart the index of the start of the shaping context
2570 * @param contextEnd the index + 1 of the end of the shaping context
2571 * @param isRtl whether the run is in RTL direction
2572 * @param offset index of caret position
2573 * @return width measurement between start and offset
2575 public float getRunAdvance(CharSequence text, int start, int end, int contextStart,
2576 int contextEnd, boolean isRtl, int offset) {
2577 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2579 throw new IllegalArgumentException("text cannot be null");
2581 if ((contextStart | start | offset | end | contextEnd
2582 | start - contextStart | offset - start | end - offset
2583 | contextEnd - end | text.length() - contextEnd) < 0) {
2584 throw new IndexOutOfBoundsException();
2589 // TODO performance: specialized alternatives to avoid buffer copy, if win is significant
2590 char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart);
2591 TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
2592 float result = getRunAdvance(buf, start - contextStart, end - contextStart, 0,
2593 contextEnd - contextStart, isRtl, offset - contextStart);
2594 TemporaryBuffer.recycle(buf);
2599 * Get the character offset within the string whose position is closest to the specified
2600 * horizontal position.
2602 * <p>The returned value is generally the value of {@code offset} for which
2603 * {@link #getRunAdvance} yields a result most closely approximating {@code advance},
2604 * and which is also on a grapheme cluster boundary. As such, it is the preferred method
2605 * for positioning a cursor in response to a touch or pointer event. The grapheme cluster
2606 * boundaries are based on
2607 * <a href="http://unicode.org/reports/tr29/">Unicode Standard Annex #29</a> but with some
2608 * tailoring for better user experience.
2610 * <p>Note that {@code advance} is a (generally positive) width measurement relative to the start
2611 * of the run. Thus, for RTL runs it the distance from the point to the right edge.
2613 * <p>All indices are relative to the start of {@code text}. Further, {@code 0 <= contextStart
2614 * <= start <= end <= contextEnd <= text.length} must hold on entry, and {@code start <= result
2615 * <= end} will hold on return.
2617 * @param text the text to measure. Cannot be null.
2618 * @param start the index of the start of the range to measure
2619 * @param end the index + 1 of the end of the range to measure
2620 * @param contextStart the index of the start of the shaping context
2621 * @param contextEnd the index + 1 of the end of the range to measure
2622 * @param isRtl whether the run is in RTL direction
2623 * @param advance width relative to start of run
2624 * @return index of offset
2626 public int getOffsetForAdvance(char[] text, int start, int end, int contextStart,
2627 int contextEnd, boolean isRtl, float advance) {
2628 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2630 throw new IllegalArgumentException("text cannot be null");
2632 if ((contextStart | start | end | contextEnd
2633 | start - contextStart | end - start | contextEnd - end
2634 | text.length - contextEnd) < 0) {
2635 throw new IndexOutOfBoundsException();
2637 // TODO: take mCompatScaling into account (or eliminate compat scaling)?
2638 return nGetOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end,
2639 contextStart, contextEnd, isRtl, advance);
2643 * @see #getOffsetForAdvance(char[], int, int, int, int, boolean, float)
2645 * @param text the text to measure. Cannot be null.
2646 * @param start the index of the start of the range to measure
2647 * @param end the index + 1 of the end of the range to measure
2648 * @param contextStart the index of the start of the shaping context
2649 * @param contextEnd the index + 1 of the end of the range to measure
2650 * @param isRtl whether the run is in RTL direction
2651 * @param advance width relative to start of run
2652 * @return index of offset
2654 public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart,
2655 int contextEnd, boolean isRtl, float advance) {
2656 if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
2658 throw new IllegalArgumentException("text cannot be null");
2660 if ((contextStart | start | end | contextEnd
2661 | start - contextStart | end - start | contextEnd - end
2662 | text.length() - contextEnd) < 0) {
2663 throw new IndexOutOfBoundsException();
2665 // TODO performance: specialized alternatives to avoid buffer copy, if win is significant
2666 char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart);
2667 TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
2668 int result = getOffsetForAdvance(buf, start - contextStart, end - contextStart, 0,
2669 contextEnd - contextStart, isRtl, advance) + contextStart;
2670 TemporaryBuffer.recycle(buf);
2675 protected void finalize() throws Throwable {
2677 if (mNativePaint != 0) {
2678 nFinalizer(mNativePaint);
2686 private static native long nInit();
2687 private static native long nInitWithPaint(long paint);
2688 private static native void nReset(long paintPtr);
2689 private static native void nSet(long paintPtrDest, long paintPtrSrc);
2690 private static native int nGetStyle(long paintPtr);
2691 private static native void nSetStyle(long paintPtr, int style);
2692 private static native int nGetStrokeCap(long paintPtr);
2693 private static native void nSetStrokeCap(long paintPtr, int cap);
2694 private static native int nGetStrokeJoin(long paintPtr);
2695 private static native void nSetStrokeJoin(long paintPtr,
2697 private static native boolean nGetFillPath(long paintPtr,
2698 long src, long dst);
2699 private static native long nSetShader(long paintPtr, long shader);
2700 private static native long nSetColorFilter(long paintPtr,
2702 private static native long nSetXfermode(long paintPtr,
2704 private static native long nSetPathEffect(long paintPtr,
2706 private static native long nSetMaskFilter(long paintPtr,
2708 private static native long nSetTypeface(long paintPtr,
2710 private static native long nSetRasterizer(long paintPtr,
2713 private static native int nGetTextAlign(long paintPtr);
2714 private static native void nSetTextAlign(long paintPtr,
2717 private static native void nSetTextLocales(long paintPtr,
2720 private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
2721 char[] text, int index, int count, int contextIndex, int contextCount,
2722 int bidiFlags, float[] advances, int advancesIndex);
2723 private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
2724 String text, int start, int end, int contextStart, int contextEnd,
2725 int bidiFlags, float[] advances, int advancesIndex);
2727 private native int nGetTextRunCursor(long paintPtr, char[] text,
2728 int contextStart, int contextLength, int dir, int offset, int cursorOpt);
2729 private native int nGetTextRunCursor(long paintPtr, String text,
2730 int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
2732 private static native void nGetTextPath(long paintPtr, long typefacePtr,
2733 int bidiFlags, char[] text, int index, int count, float x, float y, long path);
2734 private static native void nGetTextPath(long paintPtr, long typefacePtr,
2735 int bidiFlags, String text, int start, int end, float x, float y, long path);
2736 private static native void nGetStringBounds(long nativePaint, long typefacePtr,
2737 String text, int start, int end, int bidiFlags, Rect bounds);
2738 private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
2739 char[] text, int index, int count, int bidiFlags, Rect bounds);
2740 private static native void nFinalizer(long nativePaint);
2742 private static native void nSetShadowLayer(long paintPtr,
2743 float radius, float dx, float dy, int color);
2744 private static native boolean nHasShadowLayer(long paintPtr);
2746 private static native float nGetLetterSpacing(long paintPtr);
2747 private static native void nSetLetterSpacing(long paintPtr,
2748 float letterSpacing);
2749 private static native void nSetFontFeatureSettings(long paintPtr,
2751 private static native int nGetHyphenEdit(long paintPtr);
2752 private static native void nSetHyphenEdit(long paintPtr, int hyphen);
2753 private static native boolean nHasGlyph(long paintPtr, long typefacePtr,
2754 int bidiFlags, String string);
2755 private static native float nGetRunAdvance(long paintPtr, long typefacePtr,
2756 char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl,
2758 private static native int nGetOffsetForAdvance(long paintPtr,
2759 long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd,
2760 boolean isRtl, float advance);