diff options
Diffstat (limited to 'core/java/android/text/StaticLayout.java')
| -rw-r--r-- | core/java/android/text/StaticLayout.java | 110 |
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); |
