Skip to content

Commit c1f4483

Browse files
author
Gilles Debunne
committed
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
1 parent 97ff789 commit c1f4483

File tree

1 file changed

+48
-57
lines changed

1 file changed

+48
-57
lines changed

core/java/android/text/TextLine.java

Lines changed: 48 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ class TextLine {
5959
private boolean mCharsValid;
6060
private Spanned mSpanned;
6161
private final TextPaint mWorkPaint = new TextPaint();
62+
private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet =
63+
new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class);
64+
private final SpanSet<CharacterStyle> mCharacterStyleSpanSet =
65+
new SpanSet<CharacterStyle>(CharacterStyle.class);
66+
private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet =
67+
new SpanSet<ReplacementSpan>(ReplacementSpan.class);
6268

6369
private static final TextLine[] sCached = new TextLine[3];
6470

@@ -119,7 +125,6 @@ static TextLine recycle(TextLine tl) {
119125
* @param hasTabs true if the line might contain tabs or emoji
120126
* @param tabStops the tabStops. Can be null.
121127
*/
122-
@SuppressWarnings("null")
123128
void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
124129
Directions directions, boolean hasTabs, TabStops tabStops) {
125130
mPaint = paint;
@@ -135,12 +140,10 @@ void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
135140
mSpanned = null;
136141

137142
boolean hasReplacement = false;
138-
SpanSet<ReplacementSpan> replacementSpans = null;
139143
if (text instanceof Spanned) {
140144
mSpanned = (Spanned) text;
141-
replacementSpans = new SpanSet<ReplacementSpan>(mSpanned, start, limit,
142-
ReplacementSpan.class);
143-
hasReplacement = replacementSpans.numberOfSpans > 0;
145+
mReplacementSpanSpanSet.init(mSpanned, start, limit);
146+
hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
144147
}
145148

146149
mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
@@ -158,9 +161,8 @@ void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
158161
// zero-width characters.
159162
char[] chars = mChars;
160163
for (int i = start, inext; i < limit; i = inext) {
161-
// replacementSpans cannot be null if hasReplacement is true
162-
inext = replacementSpans.getNextTransition(i, limit);
163-
if (replacementSpans.hasSpansIntersecting(i, inext)) {
164+
inext = mReplacementSpanSpanSet.getNextTransition(i, limit);
165+
if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) {
164166
// transition into a span
165167
chars[i - start] = '\ufffc';
166168
for (int j = i - start + 1, e = inext - start; j < e; ++j) {
@@ -854,21 +856,30 @@ private float handleReplacement(ReplacementSpan replacement, TextPaint wp,
854856
}
855857

856858
private static class SpanSet<E> {
857-
final int numberOfSpans;
858-
final E[] spans;
859-
final int[] spanStarts;
860-
final int[] spanEnds;
861-
final int[] spanFlags;
859+
int numberOfSpans;
860+
E[] spans;
861+
int[] spanStarts;
862+
int[] spanEnds;
863+
int[] spanFlags;
864+
final Class<? extends E> classType;
865+
866+
SpanSet(Class<? extends E> type) {
867+
classType = type;
868+
numberOfSpans = 0;
869+
}
862870

863871
@SuppressWarnings("unchecked")
864-
SpanSet(Spanned spanned, int start, int limit, Class<? extends E> type) {
865-
final E[] allSpans = spanned.getSpans(start, limit, type);
872+
public void init(Spanned spanned, int start, int limit) {
873+
final E[] allSpans = spanned.getSpans(start, limit, classType);
866874
final int length = allSpans.length;
867-
// These arrays may end up being too large because of empty spans
868-
spans = (E[]) Array.newInstance(type, length);
869-
spanStarts = new int[length];
870-
spanEnds = new int[length];
871-
spanFlags = new int[length];
875+
876+
if (length > 0 && (spans == null || spans.length < length)) {
877+
// These arrays may end up being too large because of empty spans
878+
spans = (E[]) Array.newInstance(classType, length);
879+
spanStarts = new int[length];
880+
spanEnds = new int[length];
881+
spanFlags = new int[length];
882+
}
872883

873884
int count = 0;
874885
for (int i = 0; i < length; i++) {
@@ -879,30 +890,11 @@ private static class SpanSet<E> {
879890
if (spanStart == spanEnd) continue;
880891

881892
final int spanFlag = spanned.getSpanFlags(span);
882-
final int priority = spanFlag & Spanned.SPAN_PRIORITY;
883-
if (priority != 0 && count != 0) {
884-
int j;
885-
886-
for (j = 0; j < count; j++) {
887-
final int otherPriority = spanFlags[j] & Spanned.SPAN_PRIORITY;
888-
if (priority > otherPriority) break;
889-
}
890-
891-
System.arraycopy(spans, j, spans, j + 1, count - j);
892-
System.arraycopy(spanStarts, j, spanStarts, j + 1, count - j);
893-
System.arraycopy(spanEnds, j, spanEnds, j + 1, count - j);
894-
System.arraycopy(spanFlags, j, spanFlags, j + 1, count - j);
895893

896-
spans[j] = span;
897-
spanStarts[j] = spanStart;
898-
spanEnds[j] = spanEnd;
899-
spanFlags[j] = spanFlag;
900-
} else {
901-
spans[i] = span;
902-
spanStarts[i] = spanStart;
903-
spanEnds[i] = spanEnd;
904-
spanFlags[i] = spanFlag;
905-
}
894+
spans[i] = span;
895+
spanStarts[i] = spanStart;
896+
spanEnds[i] = spanEnd;
897+
spanFlags[i] = spanFlag;
906898

907899
count++;
908900
}
@@ -970,10 +962,8 @@ private float handleRun(int start, int measureLimit,
970962
y, bottom, fmi, needWidth || mlimit < measureLimit);
971963
}
972964

973-
final SpanSet<MetricAffectingSpan> metricAffectingSpans = new SpanSet<MetricAffectingSpan>(
974-
mSpanned, mStart + start, mStart + limit, MetricAffectingSpan.class);
975-
final SpanSet<CharacterStyle> characterStyleSpans = new SpanSet<CharacterStyle>(
976-
mSpanned, mStart + start, mStart + limit, CharacterStyle.class);
965+
mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit);
966+
mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit);
977967

978968
// Shaping needs to take into account context up to metric boundaries,
979969
// but rendering needs to take into account character style boundaries.
@@ -985,17 +975,18 @@ private float handleRun(int start, int measureLimit,
985975
TextPaint wp = mWorkPaint;
986976
wp.set(mPaint);
987977

988-
inext = metricAffectingSpans.getNextTransition(mStart + i, mStart + limit) - mStart;
978+
inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) -
979+
mStart;
989980
int mlimit = Math.min(inext, measureLimit);
990981

991982
ReplacementSpan replacement = null;
992983

993-
for (int j = 0; j < metricAffectingSpans.numberOfSpans; j++) {
984+
for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) {
994985
// Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT
995986
// empty by construction. This special case in getSpans() explains the >= & <= tests
996-
if ((metricAffectingSpans.spanStarts[j] >= mStart + mlimit) ||
997-
(metricAffectingSpans.spanEnds[j] <= mStart + i)) continue;
998-
MetricAffectingSpan span = metricAffectingSpans.spans[j];
987+
if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) ||
988+
(mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue;
989+
MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j];
999990
if (span instanceof ReplacementSpan) {
1000991
replacement = (ReplacementSpan)span;
1001992
} else {
@@ -1016,16 +1007,16 @@ private float handleRun(int start, int measureLimit,
10161007
y, bottom, fmi, needWidth || mlimit < measureLimit);
10171008
} else {
10181009
for (int j = i, jnext; j < mlimit; j = jnext) {
1019-
jnext = characterStyleSpans.getNextTransition(mStart + j, mStart + mlimit) -
1010+
jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) -
10201011
mStart;
10211012

10221013
wp.set(mPaint);
1023-
for (int k = 0; k < characterStyleSpans.numberOfSpans; k++) {
1014+
for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
10241015
// Intentionally using >= and <= as explained above
1025-
if ((characterStyleSpans.spanStarts[k] >= mStart + jnext) ||
1026-
(characterStyleSpans.spanEnds[k] <= mStart + j)) continue;
1016+
if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) ||
1017+
(mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;
10271018

1028-
CharacterStyle span = characterStyleSpans.spans[k];
1019+
CharacterStyle span = mCharacterStyleSpanSet.spans[k];
10291020
span.updateDrawState(wp);
10301021
}
10311022

0 commit comments

Comments
 (0)