blob: 4311c2b3262fe07b6fee2cf380f1524b14d8f4dd [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.SpannedString;
import android.text.TextUtils;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A paint implementation overridden by the LayoutLib bridge.
*/
public class Paint extends _Original_Paint {
private int mColor = 0xFFFFFFFF;
private float mTextSize = 20;
private float mScaleX = 1;
private float mSkewX = 0;
private Align mAlign = Align.LEFT;
private Style mStyle = Style.FILL;
private int mFlags = 0;
/**
* Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
*/
public static final class FontInfo {
Font mFont;
java.awt.FontMetrics mMetrics;
}
private List<FontInfo> mFonts;
private final FontRenderContext mFontContext = new FontRenderContext(
new AffineTransform(), true, true);
@SuppressWarnings("hiding")
public static final int ANTI_ALIAS_FLAG = _Original_Paint.ANTI_ALIAS_FLAG;
@SuppressWarnings("hiding")
public static final int FILTER_BITMAP_FLAG = _Original_Paint.FILTER_BITMAP_FLAG;
@SuppressWarnings("hiding")
public static final int DITHER_FLAG = _Original_Paint.DITHER_FLAG;
@SuppressWarnings("hiding")
public static final int UNDERLINE_TEXT_FLAG = _Original_Paint.UNDERLINE_TEXT_FLAG;
@SuppressWarnings("hiding")
public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG;
@SuppressWarnings("hiding")
public static final int FAKE_BOLD_TEXT_FLAG = _Original_Paint.FAKE_BOLD_TEXT_FLAG;
@SuppressWarnings("hiding")
public static final int LINEAR_TEXT_FLAG = _Original_Paint.LINEAR_TEXT_FLAG;
@SuppressWarnings("hiding")
public static final int SUBPIXEL_TEXT_FLAG = _Original_Paint.SUBPIXEL_TEXT_FLAG;
@SuppressWarnings("hiding")
public static final int DEV_KERN_TEXT_FLAG = _Original_Paint.DEV_KERN_TEXT_FLAG;
public static class FontMetrics extends _Original_Paint.FontMetrics {
}
public static class FontMetricsInt extends _Original_Paint.FontMetricsInt {
}
/**
* The Style specifies if the primitive being drawn is filled,
* stroked, or both (in the same color). The default is FILL.
*/
public enum Style {
/**
* Geometry and text drawn with this style will be filled, ignoring all
* stroke-related settings in the paint.
*/
FILL (0),
/**
* Geometry and text drawn with this style will be stroked, respecting
* the stroke-related fields on the paint.
*/
STROKE (1),
/**
* Geometry and text drawn with this style will be both filled and
* stroked at the same time, respecting the stroke-related fields on
* the paint.
*/
FILL_AND_STROKE (2);
Style(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
/**
* The Cap specifies the treatment for the beginning and ending of
* stroked lines and paths. The default is BUTT.
*/
public enum Cap {
/**
* The stroke ends with the path, and does not project beyond it.
*/
BUTT (0),
/**
* The stroke projects out as a square, with the center at the end
* of the path.
*/
ROUND (1),
/**
* The stroke projects out as a semicircle, with the center at the
* end of the path.
*/
SQUARE (2);
private Cap(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
/**
* The Join specifies the treatment where lines and curve segments
* join on a stroked path. The default is MITER.
*/
public enum Join {
/**
* The outer edges of a join meet at a sharp angle
*/
MITER (0),
/**
* The outer edges of a join meet in a circular arc.
*/
ROUND (1),
/**
* The outer edges of a join meet with a straight line
*/
BEVEL (2);
private Join(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
/**
* Align specifies how drawText aligns its text relative to the
* [x,y] coordinates. The default is LEFT.
*/
public enum Align {
/**
* The text is drawn to the right of the x,y origin
*/
LEFT (0),
/**
* The text is drawn centered horizontally on the x,y origin
*/
CENTER (1),
/**
* The text is drawn to the left of the x,y origin
*/
RIGHT (2);
private Align(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
public Paint() {
this(0);
}
public Paint(int flags) {
setFlags(flags | DEFAULT_PAINT_FLAGS);
initFont();
}
public Paint(Paint paint) {
set(paint);
initFont();
}
@Override
public void finalize() throws Throwable {
// pass
}
/**
* Returns the list of {@link Font} objects. The first item is the main font, the rest
* are fall backs for characters not present in the main font.
*/
public List<FontInfo> getFonts() {
return mFonts;
}
private void initFont() {
mTypeface = Typeface.DEFAULT;
updateFontObject();
}
/**
* Update the {@link Font} object from the typeface, text size and scaling
*/
@SuppressWarnings("deprecation")
private void updateFontObject() {
if (mTypeface != null) {
// Get the fonts from the TypeFace object.
List<Font> fonts = mTypeface.getFonts();
// create new font objects as well as FontMetrics, based on the current text size
// and skew info.
ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
for (Font font : fonts) {
FontInfo info = new FontInfo();
info.mFont = font.deriveFont(mTextSize);
if (mScaleX != 1.0 || mSkewX != 0) {
// TODO: support skew
info.mFont = info.mFont.deriveFont(new AffineTransform(
mScaleX, mSkewX, 0, 0, 1, 0));
}
info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
infoList.add(info);
}
mFonts = Collections.unmodifiableList(infoList);
}
}
//----------------------------------------
public void set(Paint src) {
if (this != src) {
mColor = src.mColor;
mTextSize = src.mTextSize;
mScaleX = src.mScaleX;
mSkewX = src.mSkewX;
mAlign = src.mAlign;
mStyle = src.mStyle;
mFlags = src.mFlags;
super.set(src);
}
}
@Override
public int getFlags() {
return mFlags;
}
@Override
public void setFlags(int flags) {
mFlags = flags;
}
/**
* Return the font's recommended interline spacing, given the Paint's
* settings for typeface, textSize, etc. If metrics is not null, return the
* fontmetric values in it.
*
* @param metrics If this object is not null, its fields are filled with
* the appropriate values given the paint's text attributes.
* @return the font's recommended interline spacing.
*/
public float getFontMetrics(FontMetrics metrics) {
if (mFonts.size() > 0) {
java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
if (metrics != null) {
// Android expects negative ascent so we invert the value from Java.
metrics.top = - javaMetrics.getMaxAscent();
metrics.ascent = - javaMetrics.getAscent();
metrics.descent = javaMetrics.getDescent();
metrics.bottom = javaMetrics.getMaxDescent();
metrics.leading = javaMetrics.getLeading();
}
return javaMetrics.getHeight();
}
return 0;
}
public int getFontMetricsInt(FontMetricsInt metrics) {
if (mFonts.size() > 0) {
java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
if (metrics != null) {
// Android expects negative ascent so we invert the value from Java.
metrics.top = - javaMetrics.getMaxAscent();
metrics.ascent = - javaMetrics.getAscent();
metrics.descent = javaMetrics.getDescent();
metrics.bottom = javaMetrics.getMaxDescent();
metrics.leading = javaMetrics.getLeading();
}
return javaMetrics.getHeight();
}
return 0;
}
/**
* Reimplemented to return Paint.FontMetrics instead of _Original_Paint.FontMetrics
*/
public FontMetrics getFontMetrics() {
FontMetrics fm = new FontMetrics();
getFontMetrics(fm);
return fm;
}
/**
* Reimplemented to return Paint.FontMetricsInt instead of _Original_Paint.FontMetricsInt
*/
public FontMetricsInt getFontMetricsInt() {
FontMetricsInt fm = new FontMetricsInt();
getFontMetricsInt(fm);
return fm;
}
@Override
public float getFontMetrics(_Original_Paint.FontMetrics metrics) {
// TODO implement if needed
throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
}
@Override
public int getFontMetricsInt(_Original_Paint.FontMetricsInt metrics) {
// TODO implement if needed
throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
}
@Override
public Typeface setTypeface(Typeface typeface) {
if (typeface != null) {
mTypeface = typeface;
} else {
mTypeface = Typeface.DEFAULT;
}
updateFontObject();
return typeface;
}
@Override
public int getColor() {
return mColor;
}
@Override
public void setColor(int color) {
mColor = color;
}
@Override
public void setAlpha(int alpha) {
mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
}
@Override
public int getAlpha() {
return mColor >>> 24;
}
/**
* Set or clear the shader object.
* <p />
* Pass null to clear any previous shader.
* As a convenience, the parameter passed is also returned.
*
* @param shader May be null. the new shader to be installed in the paint
* @return shader
*/
@Override
public Shader setShader(Shader shader) {
return mShader = shader;
}
/**
* Set or clear the paint's colorfilter, returning the parameter.
*
* @param filter May be null. The new filter to be installed in the paint
* @return filter
*/
@Override
public ColorFilter setColorFilter(ColorFilter filter) {
int filterNative = 0;
if (filter != null)
filterNative = filter.native_instance;
mColorFilter = filter;
return filter;
}
/**
* Set or clear the xfermode object.
* <p />
* Pass null to clear any previous xfermode.
* As a convenience, the parameter passed is also returned.
*
* @param xfermode May be null. The xfermode to be installed in the paint
* @return xfermode
*/
@Override
public Xfermode setXfermode(Xfermode xfermode) {
return mXfermode = xfermode;
}
public void setTextAlign(Align align) {
mAlign = align;
}
@Override
public void setTextAlign(android.graphics._Original_Paint.Align align) {
// TODO implement if needed
throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
}
public Align getTextAlign() {
return mAlign;
}
public void setStyle(Style style) {
mStyle = style;
}
@Override
public void setStyle(android.graphics._Original_Paint.Style style) {
// TODO implement if needed
throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
}
public Style getStyle() {
return mStyle;
}
@Override
public void setDither(boolean dither) {
mFlags |= dither ? DITHER_FLAG : ~DITHER_FLAG;
}
@Override
public void setAntiAlias(boolean aa) {
mFlags |= aa ? ANTI_ALIAS_FLAG : ~ANTI_ALIAS_FLAG;
}
@Override
public void setFakeBoldText(boolean flag) {
mFlags |= flag ? FAKE_BOLD_TEXT_FLAG : ~FAKE_BOLD_TEXT_FLAG;
}
/**
* Return the paint's text size.
*
* @return the paint's text size.
*/
@Override
public float getTextSize() {
return mTextSize;
}
/**
* Set the paint's text size. This value must be > 0
*
* @param textSize set the paint's text size.
*/
@Override
public void setTextSize(float textSize) {
mTextSize = textSize;
updateFontObject();
}
/**
* Return the paint's horizontal scale factor for text. The default value
* is 1.0.
*
* @return the paint's scale factor in X for drawing/measuring text
*/
@Override
public float getTextScaleX() {
return mScaleX;
}
/**
* Set the paint's horizontal scale factor for text. The default value
* is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
* stretch the text narrower.
*
* @param scaleX set the paint's scale in X for drawing/measuring text.
*/
@Override
public void setTextScaleX(float scaleX) {
mScaleX = scaleX;
updateFontObject();
}
/**
* Return the paint's horizontal skew factor for text. The default value
* is 0.
*
* @return the paint's skew factor in X for drawing text.
*/
@Override
public float getTextSkewX() {
return mSkewX;
}
/**
* Set the paint's horizontal skew factor for text. The default value
* is 0. For approximating oblique text, use values around -0.25.
*
* @param skewX set the paint's skew factor in X for drawing text.
*/
@Override
public void setTextSkewX(float skewX) {
mSkewX = skewX;
updateFontObject();
}
/**
* Return the distance above (negative) the baseline (ascent) based on the
* current typeface and text size.
*
* @return the distance above (negative) the baseline (ascent) based on the
* current typeface and text size.
*/
@Override
public float ascent() {
if (mFonts.size() > 0) {
java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
// Android expects negative ascent so we invert the value from Java.
return - javaMetrics.getAscent();
}
return 0;
}
/**
* Return the distance below (positive) the baseline (descent) based on the
* current typeface and text size.
*
* @return the distance below (positive) the baseline (descent) based on
* the current typeface and text size.
*/
@Override
public float descent() {
if (mFonts.size() > 0) {
java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
return javaMetrics.getDescent();
}
return 0;
}
/**
* Return the width of the text.
*
* @param text The text to measure
* @param index The index of the first character to start measuring
* @param count THe number of characters to measure, beginning with start
* @return The width of the text
*/
@Override
public float measureText(char[] text, int index, int count) {
// WARNING: the logic in this method is similar to Canvas.drawText.
// Any change to this method should be reflected in Canvas.drawText
if (mFonts.size() > 0) {
FontInfo mainFont = mFonts.get(0);
int i = index;
int lastIndex = index + count;
float total = 0f;
while (i < lastIndex) {
// always start with the main font.
int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
if (upTo == -1) {
// shortcut to exit
return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
} else if (upTo > 0) {
total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
i = upTo;
// don't call continue at this point. Since it is certain the main font
// cannot display the font a index upTo (now ==i), we move on to the
// fallback fonts directly.
}
// no char supported, attempt to read the next char(s) with the
// fallback font. In this case we only test the first character
// and then go back to test with the main font.
// Special test for 2-char characters.
boolean foundFont = false;
for (int f = 1 ; f < mFonts.size() ; f++) {
FontInfo fontInfo = mFonts.get(f);
// need to check that the font can display the character. We test
// differently if the char is a high surrogate.
int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
if (upTo == -1) {
total += fontInfo.mMetrics.charsWidth(text, i, charCount);
i += charCount;
foundFont = true;
break;
}
}
// in case no font can display the char, measure it with the main font.
if (foundFont == false) {
int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
total += mainFont.mMetrics.charsWidth(text, i, size);
i += size;
}
}
}
return 0;
}
/**
* Return the width of the text.
*
* @param text The text to measure
* @param start The index of the first character to start measuring
* @param end 1 beyond the index of the last character to measure
* @return The width of the text
*/
@Override
public float measureText(String text, int start, int end) {
return measureText(text.toCharArray(), start, end - start);
}
/**
* Return the width of the text.
*
* @param text The text to measure
* @return The width of the text
*/
@Override
public float measureText(String text) {
return measureText(text.toCharArray(), 0, text.length());
}
/*
* re-implement to call SpannableStringBuilder.measureText with a Paint object
* instead of an _Original_Paint
*/
@Override
public float measureText(CharSequence text, int start, int end) {
if (text instanceof String) {
return measureText((String)text, start, end);
}
if (text instanceof SpannedString ||
text instanceof SpannableString) {
return measureText(text.toString(), start, end);
}
if (text instanceof SpannableStringBuilder) {
return ((SpannableStringBuilder)text).measureText(start, end, this);
}
char[] buf = TemporaryBuffer.obtain(end - start);
TextUtils.getChars(text, start, end, buf, 0);
float result = measureText(buf, 0, end - start);
TemporaryBuffer.recycle(buf);
return result;
}
/**
* Measure the text, stopping early if the measured width exceeds maxWidth.
* Return the number of chars that were measured, and if measuredWidth is
* not null, return in it the actual width measured.
*
* @param text The text to measure
* @param index The offset into text to begin measuring at
* @param count The number of maximum number of entries to measure. If count
* is negative, then the characters before index are measured
* in reverse order. This allows for measuring the end of
* string.
* @param maxWidth The maximum width to accumulate.
* @param measuredWidth Optional. If not null, returns the actual width
* measured.
* @return The number of chars that were measured. Will always be <=
* abs(count).
*/
@Override
public int breakText(char[] text, int index, int count,
float maxWidth, float[] measuredWidth) {
int inc = count > 0 ? 1 : -1;
int measureIndex = 0;
float measureAcc = 0;
for (int i = index ; i != index + count ; i += inc, measureIndex++) {
int start, end;
if (i < index) {
start = i;
end = index;
} else {
start = index;
end = i;
}
// measure from start to end
float res = measureText(text, start, end - start + 1);
if (measuredWidth != null) {
measuredWidth[measureIndex] = res;
}
measureAcc += res;
if (res > maxWidth) {
// we should not return this char index, but since it's 0-based and we need
// to return a count, we simply return measureIndex;
return measureIndex;
}
}
return measureIndex;
}
/**
* Measure the text, stopping early if the measured width exceeds maxWidth.
* Return the number of chars that were measured, and if measuredWidth is
* not null, return in it the actual width measured.
*
* @param text The text to measure
* @param measureForwards If true, measure forwards, starting at index.
* Otherwise, measure backwards, starting with the
* last character in the string.
* @param maxWidth The maximum width to accumulate.
* @param measuredWidth Optional. If not null, returns the actual width
* measured.
* @return The number of chars that were measured. Will always be <=
* abs(count).
*/
@Override
public int breakText(String text, boolean measureForwards,
float maxWidth, float[] measuredWidth) {
// NOTE: javadoc doesn't match. Just a guess.
return breakText(text,
0 /* start */, text.length() /* end */,
measureForwards, maxWidth, measuredWidth);
}
/**
* Return the advance widths for the characters in the string.
*
* @param text The text to measure
* @param index The index of the first char to to measure
* @param count The number of chars starting with index to measure
* @param widths array to receive the advance widths of the characters.
* Must be at least a large as count.
* @return the actual number of widths returned.
*/
@Override
public int getTextWidths(char[] text, int index, int count,
float[] widths) {
if (mFonts.size() > 0) {
if ((index | count) < 0 || index + count > text.length
|| count > widths.length) {
throw new ArrayIndexOutOfBoundsException();
}
// FIXME: handle multi-char characters.
// Need to figure out if the lengths of the width array takes into account
// multi-char characters.
for (int i = 0; i < count; i++) {
char c = text[i + index];
boolean found = false;
for (FontInfo info : mFonts) {
if (info.mFont.canDisplay(c)) {
widths[i] = info.mMetrics.charWidth(c);
found = true;
break;
}
}
if (found == false) {
// we stop there.
return i;
}
}
return count;
}
return 0;
}
/**
* Return the advance widths for the characters in the string.
*
* @param text The text to measure
* @param start The index of the first char to to measure
* @param end The end of the text slice to measure
* @param widths array to receive the advance widths of the characters.
* Must be at least a large as the text.
* @return the number of unichars in the specified text.
*/
@Override
public int getTextWidths(String text, int start, int end, float[] widths) {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
if (end - start > widths.length) {
throw new ArrayIndexOutOfBoundsException();
}
return getTextWidths(text.toCharArray(), start, end - start, widths);
}
/*
* re-implement to call SpannableStringBuilder.getTextWidths with a Paint object
* instead of an _Original_Paint
*/
@Override
public int getTextWidths(CharSequence text, int start, int end, float[] widths) {
if (text instanceof String) {
return getTextWidths((String)text, start, end, widths);
}
if (text instanceof SpannedString || text instanceof SpannableString) {
return getTextWidths(text.toString(), start, end, widths);
}
if (text instanceof SpannableStringBuilder) {
return ((SpannableStringBuilder)text).getTextWidths(start, end, widths, this);
}
char[] buf = TemporaryBuffer.obtain(end - start);
TextUtils.getChars(text, start, end, buf, 0);
int result = getTextWidths(buf, 0, end - start, widths);
TemporaryBuffer.recycle(buf);
return result;
}
/**
* Return the path (outline) for the specified text.
* Note: just like Canvas.drawText, this will respect the Align setting in
* the paint.
*
* @param text The text to retrieve the path from
* @param index The index of the first character in text
* @param count The number of characterss starting with index
* @param x The x coordinate of the text's origin
* @param y The y coordinate of the text's origin
* @param path The path to receive the data describing the text. Must
* be allocated by the caller.
*/
@Override
public void getTextPath(char[] text, int index, int count,
float x, float y, Path path) {
// TODO this is the ORIGINAL implementation. REPLACE AS NEEDED OR REMOVE
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
// TODO native_getTextPath(mNativePaint, text, index, count, x, y, path.ni());
throw new UnsupportedOperationException("IMPLEMENT AS NEEDED");
}
/**
* Return the path (outline) for the specified text.
* Note: just like Canvas.drawText, this will respect the Align setting
* in the paint.
*
* @param text The text to retrieve the path from
* @param start The first character in the text
* @param end 1 past the last charcter in the text
* @param x The x coordinate of the text's origin
* @param y The y coordinate of the text's origin
* @param path The path to receive the data describing the text. Must
* be allocated by the caller.
*/
@Override
public void getTextPath(String text, int start, int end,
float x, float y, Path path) {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
getTextPath(text.toCharArray(), start, end - start, x, y, path);
}
/**
* Return in bounds (allocated by the caller) the smallest rectangle that
* encloses all of the characters, with an implied origin at (0,0).
*
* @param text String to measure and return its bounds
* @param start Index of the first char in the string to measure
* @param end 1 past the last char in the string measure
* @param bounds Returns the unioned bounds of all the text. Must be
* allocated by the caller.
*/
@Override
public void getTextBounds(String text, int start, int end, Rect bounds) {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
if (bounds == null) {
throw new NullPointerException("need bounds Rect");
}
getTextBounds(text.toCharArray(), start, end - start, bounds);
}
/**
* Return in bounds (allocated by the caller) the smallest rectangle that
* encloses all of the characters, with an implied origin at (0,0).
*
* @param text Array of chars to measure and return their unioned bounds
* @param index Index of the first char in the array to measure
* @param count The number of chars, beginning at index, to measure
* @param bounds Returns the unioned bounds of all the text. Must be
* allocated by the caller.
*/
@Override
public void getTextBounds(char[] text, int index, int count, Rect bounds) {
// FIXME
if (mFonts.size() > 0) {
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
if (bounds == null) {
throw new NullPointerException("need bounds Rect");
}
FontInfo mainInfo = mFonts.get(0);
Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, mFontContext);
bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight());
}
}
}