summaryrefslogtreecommitdiff
path: root/core/java/android/text/StaticLayout.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/text/StaticLayout.java')
-rw-r--r--core/java/android/text/StaticLayout.java110
1 files changed, 84 insertions, 26 deletions
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 81c82c994ef4..c0fc44fd8ee1 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -21,10 +21,10 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Paint;
-import android.text.AutoGrowArray.FloatArray;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
import android.text.style.LineHeightSpan;
+import android.text.style.MetricAffectingSpan;
import android.text.style.TabStopSpan;
import android.util.Log;
import android.util.Pools.SynchronizedPool;
@@ -99,6 +99,8 @@ public class StaticLayout extends Layout {
b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
+
+ b.mMeasuredText = MeasuredText.obtain();
return b;
}
@@ -109,6 +111,8 @@ public class StaticLayout extends Layout {
private static void recycle(@NonNull Builder b) {
b.mPaint = null;
b.mText = null;
+ MeasuredText.recycle(b.mMeasuredText);
+ b.mMeasuredText = null;
b.mLeftIndents = null;
b.mRightIndents = null;
b.mLeftPaddings = null;
@@ -124,6 +128,7 @@ public class StaticLayout extends Layout {
mRightIndents = null;
mLeftPaddings = null;
mRightPaddings = null;
+ mMeasuredText.finish();
}
public Builder setText(CharSequence source) {
@@ -439,6 +444,9 @@ public class StaticLayout extends Layout {
private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
+ // This will go away and be subsumed by native builder code
+ private MeasuredText mMeasuredText;
+
private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
}
@@ -610,7 +618,11 @@ public class StaticLayout extends Layout {
TextUtils.TruncateAt ellipsize = b.mEllipsize;
final boolean addLastLineSpacing = b.mAddLastLineLineSpacing;
LineBreaks lineBreaks = new LineBreaks(); // TODO: move to builder to avoid allocation costs
- FloatArray widths = new FloatArray();
+ // store span end locations
+ int[] spanEndCache = new int[4];
+ // store fontMetrics per span range
+ // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
+ int[] fmCache = new int[4 * 4];
mLineCount = 0;
mEllipsized = false;
@@ -622,6 +634,8 @@ public class StaticLayout extends Layout {
Paint.FontMetricsInt fm = b.mFontMetricsInt;
int[] chooseHtv = null;
+ MeasuredText measured = b.mMeasuredText;
+
Spanned spanned = null;
if (source instanceof Spanned)
spanned = (Spanned) source;
@@ -648,7 +662,6 @@ public class StaticLayout extends Layout {
b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
indents, mLeftPaddings, mRightPaddings);
- MeasuredText measured = null;
try {
int paraEnd;
for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
@@ -708,6 +721,13 @@ public class StaticLayout extends Layout {
}
}
+ measured.setPara(source, paraStart, paraEnd, textDir);
+ char[] chs = measured.mChars;
+ float[] widths = measured.mWidths;
+ byte[] chdirs = measured.mLevels;
+ int dir = measured.mDir;
+ boolean easy = measured.mEasy;
+
// tab stop locations
int[] variableTabStops = null;
if (spanned != null) {
@@ -723,16 +743,50 @@ public class StaticLayout extends Layout {
}
}
- measured = MeasuredText.buildForStaticLayout(
- paint, source, paraStart, paraEnd, textDir, nativePtr, measured);
- final char[] chs = measured.getChars();
- final int[] spanEndCache = measured.getSpanEndCache().getRawArray();
- final int[] fmCache = measured.getFontMetrics().getRawArray();
- widths.resize(chs.length);
-
// measurement has to be done before performing line breaking
// but we don't want to recompute fontmetrics or span ranges the
// second time, so we cache those and then use those stored values
+ int fmCacheCount = 0;
+ int spanEndCacheCount = 0;
+ for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
+ if (fmCacheCount * 4 >= fmCache.length) {
+ int[] grow = new int[fmCacheCount * 4 * 2];
+ System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
+ fmCache = grow;
+ }
+
+ if (spanEndCacheCount >= spanEndCache.length) {
+ int[] grow = new int[spanEndCacheCount * 2];
+ System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
+ spanEndCache = grow;
+ }
+
+ if (spanned == null) {
+ spanEnd = paraEnd;
+ int spanLen = spanEnd - spanStart;
+ measured.addStyleRun(paint, spanLen, fm, nativePtr);
+ } else {
+ spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
+ MetricAffectingSpan.class);
+ int spanLen = spanEnd - spanStart;
+ MetricAffectingSpan[] spans =
+ spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, spanned,
+ MetricAffectingSpan.class);
+ measured.addStyleRun(paint, spans, spanLen, fm, nativePtr);
+ }
+
+ // the order of storage here (top, bottom, ascent, descent) has to match the
+ // code below where these values are retrieved
+ fmCache[fmCacheCount * 4 + 0] = fm.top;
+ fmCache[fmCacheCount * 4 + 1] = fm.bottom;
+ fmCache[fmCacheCount * 4 + 2] = fm.ascent;
+ fmCache[fmCacheCount * 4 + 3] = fm.descent;
+ fmCacheCount++;
+
+ spanEndCache[spanEndCacheCount] = spanEnd;
+ spanEndCacheCount++;
+ }
int breakCount = nComputeLineBreaks(
nativePtr,
@@ -755,7 +809,7 @@ public class StaticLayout extends Layout {
lineBreaks.ascents,
lineBreaks.descents,
lineBreaks.flags,
- widths.getRawArray());
+ widths);
final int[] breaks = lineBreaks.breaks;
final float[] lineWidths = lineBreaks.widths;
@@ -778,7 +832,7 @@ public class StaticLayout extends Layout {
width += lineWidths[i];
} else {
for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
- width += widths.get(j);
+ width += widths[j];
}
}
flag |= flags[i] & TAB_MASK;
@@ -842,10 +896,10 @@ public class StaticLayout extends Layout {
v = out(source, here, endPos,
ascent, descent, fmTop, fmBottom,
v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
- flags[breakIndex], needMultiply, measured, bufEnd,
- includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(),
- paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
- paint, moreChars);
+ flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd,
+ includepad, trackpad, addLastLineSpacing, chs, widths, paraStart,
+ ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint,
+ moreChars);
if (endPos < spanEnd) {
// preserve metrics for current span
@@ -873,8 +927,7 @@ public class StaticLayout extends Layout {
if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
&& mLineCount < mMaximumVisibleLineCount) {
- measured = MeasuredText.buildForStaticLayout(
- paint, source, bufEnd, bufEnd, textDir, nativePtr, measured);
+ measured.setPara(source, bufEnd, bufEnd, textDir);
paint.getFontMetricsInt(fm);
@@ -884,15 +937,12 @@ public class StaticLayout extends Layout {
v,
spacingmult, spacingadd, null,
null, fm, 0,
- needMultiply, measured, bufEnd,
+ needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
includepad, trackpad, addLastLineSpacing, null,
null, bufStart, ellipsize,
ellipsizedWidth, 0, paint, false);
}
} finally {
- if (measured != null) {
- measured.recycle();
- }
nFinish(nativePtr);
}
}
@@ -902,8 +952,8 @@ public class StaticLayout extends Layout {
private int out(final CharSequence text, final int start, final int end, int above, int below,
int top, int bottom, int v, final float spacingmult, final float spacingadd,
final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
- final int flags, final boolean needMultiply, final MeasuredText measured,
- final int bufEnd, final boolean includePad, final boolean trackPad,
+ final int flags, final boolean needMultiply, final byte[] chdirs, final int dir,
+ final boolean easy, final int bufEnd, final boolean includePad, final boolean trackPad,
final boolean addLastLineLineSpacing, final char[] chs, final float[] widths,
final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
final float textWidth, final TextPaint paint, final boolean moreChars) {
@@ -911,7 +961,6 @@ public class StaticLayout extends Layout {
final int off = j * mColumns;
final int want = off + mColumns + TOP;
int[] lines = mLines;
- final int dir = measured.getParagraphDir();
if (want >= lines.length) {
final int[] grow = ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(want));
@@ -937,8 +986,17 @@ public class StaticLayout extends Layout {
// one bit for start field
lines[off + TAB] |= flags & TAB_MASK;
lines[off + HYPHEN] = flags;
+
lines[off + DIR] |= dir << DIR_SHIFT;
- mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);
+ // easy means all chars < the first RTL, so no emoji, no nothing
+ // XXX a run with no text or all spaces is easy but might be an empty
+ // RTL paragraph. Make sure easy is false if this is the case.
+ if (easy) {
+ mLineDirections[j] = DIRS_ALL_LEFT_TO_RIGHT;
+ } else {
+ mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
+ start - widthStart, end - start);
+ }
final boolean firstLine = (j == 0);
final boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);