From c1f44830809f0a8526855f13822702ea756214fa Mon Sep 17 00:00:00 2001 From: Gilles Debunne Date: Thu, 8 Dec 2011 16:03:00 -0800 Subject: [PATCH] Re-use SpanSets in TextLine TextLine objects are already stored in a pool to limit allocation. Associate SpanSet objects to each TextLine to further limit the allocation of these and re-use their internal arrays (re-sized if needed). Drastically reduces new objects allocation during rendering. Priority code removed in init: priorities are already handled by getSpans. This is a duplicate of the already accepted https://android-git.corp.google.com/g/#/c/153970/ but without the dependency on an other CL. Change-Id: Iaa7e2f7a8ea4337c8d60c3a9a620e9e3e60caf12 --- core/java/android/text/TextLine.java | 105 ++++++++++++--------------- 1 file changed, 48 insertions(+), 57 deletions(-) diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index b73d90046303f..b3df8ff5fbbca 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -59,6 +59,12 @@ class TextLine { private boolean mCharsValid; private Spanned mSpanned; private final TextPaint mWorkPaint = new TextPaint(); + private final SpanSet mMetricAffectingSpanSpanSet = + new SpanSet(MetricAffectingSpan.class); + private final SpanSet mCharacterStyleSpanSet = + new SpanSet(CharacterStyle.class); + private final SpanSet mReplacementSpanSpanSet = + new SpanSet(ReplacementSpan.class); private static final TextLine[] sCached = new TextLine[3]; @@ -119,7 +125,6 @@ class TextLine { * @param hasTabs true if the line might contain tabs or emoji * @param tabStops the tabStops. Can be null. */ - @SuppressWarnings("null") void set(TextPaint paint, CharSequence text, int start, int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) { mPaint = paint; @@ -135,12 +140,10 @@ class TextLine { mSpanned = null; boolean hasReplacement = false; - SpanSet replacementSpans = null; if (text instanceof Spanned) { mSpanned = (Spanned) text; - replacementSpans = new SpanSet(mSpanned, start, limit, - ReplacementSpan.class); - hasReplacement = replacementSpans.numberOfSpans > 0; + mReplacementSpanSpanSet.init(mSpanned, start, limit); + hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; } mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; @@ -158,9 +161,8 @@ class TextLine { // zero-width characters. char[] chars = mChars; for (int i = start, inext; i < limit; i = inext) { - // replacementSpans cannot be null if hasReplacement is true - inext = replacementSpans.getNextTransition(i, limit); - if (replacementSpans.hasSpansIntersecting(i, inext)) { + inext = mReplacementSpanSpanSet.getNextTransition(i, limit); + if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) { // transition into a span chars[i - start] = '\ufffc'; for (int j = i - start + 1, e = inext - start; j < e; ++j) { @@ -854,21 +856,30 @@ class TextLine { } private static class SpanSet { - final int numberOfSpans; - final E[] spans; - final int[] spanStarts; - final int[] spanEnds; - final int[] spanFlags; + int numberOfSpans; + E[] spans; + int[] spanStarts; + int[] spanEnds; + int[] spanFlags; + final Class classType; + + SpanSet(Class type) { + classType = type; + numberOfSpans = 0; + } @SuppressWarnings("unchecked") - SpanSet(Spanned spanned, int start, int limit, Class type) { - final E[] allSpans = spanned.getSpans(start, limit, type); + public void init(Spanned spanned, int start, int limit) { + final E[] allSpans = spanned.getSpans(start, limit, classType); final int length = allSpans.length; - // These arrays may end up being too large because of empty spans - spans = (E[]) Array.newInstance(type, length); - spanStarts = new int[length]; - spanEnds = new int[length]; - spanFlags = new int[length]; + + if (length > 0 && (spans == null || spans.length < length)) { + // These arrays may end up being too large because of empty spans + spans = (E[]) Array.newInstance(classType, length); + spanStarts = new int[length]; + spanEnds = new int[length]; + spanFlags = new int[length]; + } int count = 0; for (int i = 0; i < length; i++) { @@ -879,30 +890,11 @@ class TextLine { if (spanStart == spanEnd) continue; final int spanFlag = spanned.getSpanFlags(span); - final int priority = spanFlag & Spanned.SPAN_PRIORITY; - if (priority != 0 && count != 0) { - int j; - for (j = 0; j < count; j++) { - final int otherPriority = spanFlags[j] & Spanned.SPAN_PRIORITY; - if (priority > otherPriority) break; - } - - System.arraycopy(spans, j, spans, j + 1, count - j); - System.arraycopy(spanStarts, j, spanStarts, j + 1, count - j); - System.arraycopy(spanEnds, j, spanEnds, j + 1, count - j); - System.arraycopy(spanFlags, j, spanFlags, j + 1, count - j); - - spans[j] = span; - spanStarts[j] = spanStart; - spanEnds[j] = spanEnd; - spanFlags[j] = spanFlag; - } else { - spans[i] = span; - spanStarts[i] = spanStart; - spanEnds[i] = spanEnd; - spanFlags[i] = spanFlag; - } + spans[i] = span; + spanStarts[i] = spanStart; + spanEnds[i] = spanEnd; + spanFlags[i] = spanFlag; count++; } @@ -970,10 +962,8 @@ class TextLine { y, bottom, fmi, needWidth || mlimit < measureLimit); } - final SpanSet metricAffectingSpans = new SpanSet( - mSpanned, mStart + start, mStart + limit, MetricAffectingSpan.class); - final SpanSet characterStyleSpans = new SpanSet( - mSpanned, mStart + start, mStart + limit, CharacterStyle.class); + mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); + mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); // Shaping needs to take into account context up to metric boundaries, // but rendering needs to take into account character style boundaries. @@ -985,17 +975,18 @@ class TextLine { TextPaint wp = mWorkPaint; wp.set(mPaint); - inext = metricAffectingSpans.getNextTransition(mStart + i, mStart + limit) - mStart; + inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) - + mStart; int mlimit = Math.min(inext, measureLimit); ReplacementSpan replacement = null; - for (int j = 0; j < metricAffectingSpans.numberOfSpans; j++) { + for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) { // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT // empty by construction. This special case in getSpans() explains the >= & <= tests - if ((metricAffectingSpans.spanStarts[j] >= mStart + mlimit) || - (metricAffectingSpans.spanEnds[j] <= mStart + i)) continue; - MetricAffectingSpan span = metricAffectingSpans.spans[j]; + if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) || + (mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue; + MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j]; if (span instanceof ReplacementSpan) { replacement = (ReplacementSpan)span; } else { @@ -1016,16 +1007,16 @@ class TextLine { y, bottom, fmi, needWidth || mlimit < measureLimit); } else { for (int j = i, jnext; j < mlimit; j = jnext) { - jnext = characterStyleSpans.getNextTransition(mStart + j, mStart + mlimit) - + jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) - mStart; wp.set(mPaint); - for (int k = 0; k < characterStyleSpans.numberOfSpans; k++) { + for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) { // Intentionally using >= and <= as explained above - if ((characterStyleSpans.spanStarts[k] >= mStart + jnext) || - (characterStyleSpans.spanEnds[k] <= mStart + j)) continue; + if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) || + (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue; - CharacterStyle span = characterStyleSpans.spans[k]; + CharacterStyle span = mCharacterStyleSpanSet.spans[k]; span.updateDrawState(wp); }