* @param limit the limit of the line relative to the text
* @param dir the paragraph direction of this line
* @param directions the directions information of this line
- * @param hasTabs true if the line might contain tabs or emoji
+ * @param hasTabs true if the line might contain tabs
* @param tabStops the tabStops. Can be null.
*/
void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
float h = 0;
int[] runs = mDirections.mDirections;
- RectF emojiRect = null;
int lastRunIndex = runs.length - 2;
for (int i = 0; i < runs.length; i += 2) {
int segstart = runStart;
for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
int codept = 0;
- Bitmap bm = null;
-
if (mHasTabs && j < runLimit) {
codept = mChars[j];
- if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
+ if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
codept = Character.codePointAt(mChars, j);
- if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
- bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
- } else if (codept > 0xffff) {
+ if (codept > 0xFFFF) {
++j;
continue;
}
}
}
- if (j == runLimit || codept == '\t' || bm != null) {
+ if (j == runLimit || codept == '\t') {
h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom,
i != lastRunIndex || j != mLen);
if (codept == '\t') {
h = mDir * nextTab(h * mDir);
- } else if (bm != null) {
- float bmAscent = ascent(j);
- float bitmapHeight = bm.getHeight();
- float scale = -bmAscent / bitmapHeight;
- float width = bm.getWidth() * scale;
-
- if (emojiRect == null) {
- emojiRect = new RectF();
- }
- emojiRect.set(x + h, y + bmAscent,
- x + h + width, y);
- c.drawBitmap(bm, null, emojiRect, mPaint);
- h += width;
- j++;
}
segstart = j + 1;
}
int segstart = runStart;
for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
int codept = 0;
- Bitmap bm = null;
-
if (mHasTabs && j < runLimit) {
codept = chars[j];
- if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
+ if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
codept = Character.codePointAt(chars, j);
- if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
- bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
- } else if (codept > 0xffff) {
+ if (codept > 0xFFFF) {
++j;
continue;
}
}
}
- if (j == runLimit || codept == '\t' || bm != null) {
+ if (j == runLimit || codept == '\t') {
boolean inSegment = target >= segstart && target < j;
boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
}
}
- if (bm != null) {
- float bmAscent = ascent(j);
- float wid = bm.getWidth() * -bmAscent / bm.getHeight();
- h += mDir * wid;
- j++;
+ segstart = j + 1;
+ }
+ }
+ }
+
+ return h;
+ }
+
+ /**
+ * @see #measure(int, boolean, FontMetricsInt)
+ * @return The measure results for all possible offsets
+ */
+ float[] measureAllOffsets(boolean[] trailing, FontMetricsInt fmi) {
+ float[] measurement = new float[mLen + 1];
+
+ int[] target = new int[mLen + 1];
+ for (int offset = 0; offset < target.length; ++offset) {
+ target[offset] = trailing[offset] ? offset - 1 : offset;
+ }
+ if (target[0] < 0) {
+ measurement[0] = 0;
+ }
+
+ float h = 0;
+
+ if (!mHasTabs) {
+ if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
+ for (int offset = 0; offset <= mLen; ++offset) {
+ measurement[offset] = measureRun(0, offset, mLen, false, fmi);
+ }
+ return measurement;
+ }
+ if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
+ for (int offset = 0; offset <= mLen; ++offset) {
+ measurement[offset] = measureRun(0, offset, mLen, true, fmi);
+ }
+ return measurement;
+ }
+ }
+
+ char[] chars = mChars;
+ int[] runs = mDirections.mDirections;
+ for (int i = 0; i < runs.length; i += 2) {
+ int runStart = runs[i];
+ int runLimit = runStart + (runs[i + 1] & Layout.RUN_LENGTH_MASK);
+ if (runLimit > mLen) {
+ runLimit = mLen;
+ }
+ boolean runIsRtl = (runs[i + 1] & Layout.RUN_RTL_FLAG) != 0;
+
+ int segstart = runStart;
+ for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; ++j) {
+ int codept = 0;
+ if (mHasTabs && j < runLimit) {
+ codept = chars[j];
+ if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
+ codept = Character.codePointAt(chars, j);
+ if (codept > 0xFFFF) {
+ ++j;
+ continue;
+ }
+ }
+ }
+
+ if (j == runLimit || codept == '\t') {
+ float oldh = h;
+ boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
+ float w = measureRun(segstart, j, j, runIsRtl, fmi);
+ h += advance ? w : -w;
+
+ float baseh = advance ? oldh : h;
+ FontMetricsInt crtfmi = advance ? fmi : null;
+ for (int offset = segstart; offset <= j && offset <= mLen; ++offset) {
+ if (target[offset] >= segstart && target[offset] < j) {
+ measurement[offset] =
+ baseh + measureRun(segstart, offset, j, runIsRtl, crtfmi);
+ }
+ }
+
+ if (codept == '\t') {
+ if (target[j] == j) {
+ measurement[j] = h;
+ }
+ h = mDir * nextTab(h * mDir);
+ if (target[j + 1] == j) {
+ measurement[j + 1] = h;
+ }
}
segstart = j + 1;
}
}
}
+ if (target[mLen] == mLen) {
+ measurement[mLen] = h;
+ }
- return h;
+ return measurement;
}
/**
/**
* Utility function for measuring and rendering text. The text must
- * not include a tab or emoji.
+ * not include a tab.
*
* @param wp the working paint
* @param start the start of the text
* @param bottom the bottom of the line
* @param fmi receives metrics information, can be null
* @param needWidth true if the width of the run is needed
+ * @param offset the offset for the purpose of measuring
* @return the signed width of the run based on the run direction; only
* valid if needWidth is true
*/
private float handleText(TextPaint wp, int start, int end,
int contextStart, int contextEnd, boolean runIsRtl,
Canvas c, float x, int top, int y, int bottom,
- FontMetricsInt fmi, boolean needWidth) {
+ FontMetricsInt fmi, boolean needWidth, int offset) {
// Get metrics first (even for empty strings or "0" width runs)
if (fmi != null) {
if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) {
if (mCharsValid) {
ret = wp.getRunAdvance(mChars, start, end, contextStart, contextEnd,
- runIsRtl, end);
+ runIsRtl, offset);
} else {
int delta = mStart;
ret = wp.getRunAdvance(mText, delta + start, delta + end,
- delta + contextStart, delta + contextEnd, runIsRtl, delta + end);
+ delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
}
}
/**
* Utility function for handling a unidirectional run. The run must not
- * contain tabs or emoji but can contain styles.
+ * contain tabs but can contain styles.
*
*
* @param start the line-relative start of the run
TextPaint wp = mWorkPaint;
wp.set(mPaint);
final int mlimit = measureLimit;
- return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top,
- y, bottom, fmi, needWidth || mlimit < measureLimit);
+ return handleText(wp, start, limit, start, limit, runIsRtl, c, x, top,
+ y, bottom, fmi, needWidth || mlimit < measureLimit, mlimit);
}
mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit);
}
for (int j = i, jnext; j < mlimit; j = jnext) {
- jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) -
+ jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) -
mStart;
+ int offset = Math.min(jnext, mlimit);
wp.set(mPaint);
for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
// Intentionally using >= and <= as explained above
- if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) ||
+ if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) ||
(mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;
CharacterStyle span = mCharacterStyleSpanSet.spans[k];
wp.setHyphenEdit(0);
}
x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
- top, y, bottom, fmi, needWidth || jnext < measureLimit);
+ top, y, bottom, fmi, needWidth || jnext < measureLimit, offset);
}
}
}
/**
- * Returns the ascent of the text at start. This is used for scaling
- * emoji.
- *
- * @param pos the line-relative position
- * @return the ascent of the text at start
- */
- float ascent(int pos) {
- if (mSpanned == null) {
- return mPaint.ascent();
- }
-
- pos += mStart;
- MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1, MetricAffectingSpan.class);
- if (spans.length == 0) {
- return mPaint.ascent();
- }
-
- TextPaint wp = mWorkPaint;
- wp.set(mPaint);
- for (MetricAffectingSpan span : spans) {
- span.updateMeasureState(wp);
- }
- return wp.ascent();
- }
-
- /**
* Returns the next tab position.
*
* @param h the (unsigned) offset from the leading margin