Stop creating PrecomputedText in StaticLayout
No performance regressions are expected
android.text.PrecomputedTextPerfTest:
create NoStyled Hyphenation : 17,849,966 -> 17,858,570: (+0.0%)
create NoStyled Hyphenation WidthOnly : 17,814,338 -> 17,801,205: (-0.1%)
create NoStyled NoHyphenation : 7,123,449 -> 7,068,308: (-0.8%)
create NoStyled NoHyphenation WidthOnly : 7,108,169 -> 7,074,908: (-0.5%)
create Styled Hyphenation : 12,179,203 -> 12,131,020: (-0.4%)
create Styled Hyphenation WidthOnly : 12,112,347 -> 12,241,311: (+1.1%)
create Styled NoHyphenation : 11,870,126 -> 11,880,442: (+0.1%)
create Styled NoHyphenation WidthOnly : 11,836,742 -> 11,860,292: (+0.2%)
android.text.StaticLayoutPerfTest:
create PrecomputedText Balanced Hyphenation : 697,713 -> 691,148: (-0.9%)
create PrecomputedText Balanced NoHyphenation: 517,113 -> 498,106: (-3.7%)
create PrecomputedText Greedy Hyphenation : 468,243 -> 455,015: (-2.8%)
create PrecomputedText Greedy NoHyphenation : 479,514 -> 461,617: (-3.7%)
create RandomText Balanced Hyphenation : 17,183,044 -> 17,049,811: (-0.8%)
create RandomText Balanced NoHyphenation : 7,183,745 -> 7,025,070: (-2.2%)
create RandomText Greedy Hyphenation : 7,130,841 -> 6,995,785: (-1.9%)
create RandomText Greedy NoHyphenation : 7,122,398 -> 7,037,074: (-1.2%)
draw PrecomputedText NoStyled : 520,306 -> 551,465: (+6.0%)
draw PrecomputedText NoStyled WithoutCache : 545,773 -> 566,956: (+3.9%)
draw PrecomputedText Styled : 826,044 -> 838,979: (+1.6%)
draw PrecomputedText Styled WithoutCache : 829,958 -> 841,749: (+1.4%)
draw RandomText NoStyled : 537,079 -> 545,428: (+1.6%)
draw RandomText NoStyled WithoutCache : 6,473,166 -> 6,445,194: (-0.4%)
draw RandomText Styled : 995,033 -> 1,015,913: (+2.1%)
draw RandomText Styled WithoutCache : 2,725,313 -> 2,770,604: (+1.7%)
android.widget.TextViewPrecomputedTextPerfTest:
newLayout PrecomputedText : 754,311 -> 718,130: (-4.8%)
newLayout PrecomputedText Selectable : 17,716,239 -> 17,484,046: (-1.3%)
newLayout RandomText : 16,657,952 -> 16,511,625: (-0.9%)
newLayout RandomText Selectable : 17,675,222 -> 17,520,653: (-0.9%)
onDraw PrecomputedText : 1,307,123 -> 1,280,009: (-2.1%)
onDraw PrecomputedText Selectable : 17,613,031 -> 17,404,379: (-1.2%)
onDraw RandomText : 17,369,256 -> 17,295,363: (-0.4%)
onDraw RandomText Selectable : 18,207,392 -> 18,077,660: (-0.7%)
onMeasure PrecomputedText : 748,537 -> 739,128: (-1.3%)
onMeasure PrecomputedText Selectable : 17,842,953 -> 17,784,459: (-0.3%)
onMeasure RandomText : 16,633,454 -> 16,549,182: (-0.5%)
onMeasure RandomText Selectable : 18,022,286 -> 17,873,919: (-0.8%)
setText PrecomputedText : 120,769 -> 119,496: (-1.1%)
setText PrecomputedText Selectable : 162,411 -> 150,809: (-7.1%)
setText RandomText : 11,096 -> 10,956: (-1.3%)
setText RandomText Selectable : 48,852 -> 48,593: (-0.5%)
Bug: 72998298
Test: atest CtsWidgetTestCases:EditTextTest
CtsWidgetTestCases:TextViewFadingEdgeTest
FrameworksCoreTests:TextViewFallbackLineSpacingTest
FrameworksCoreTests:TextViewTest FrameworksCoreTests:TypefaceTest
CtsGraphicsTestCases:TypefaceTest CtsWidgetTestCases:TextViewTest
CtsTextTestCases FrameworksCoreTests:android.text
CtsWidgetTestCases:TextViewPrecomputedTextTest
Change-Id: I3af758ecc5a15975c4e59c6378faf7c14c3bd65b
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index aafcf44..980f4704 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -675,7 +675,7 @@
/**
* This only works if the MeasuredParagraph is computed with buildForStaticLayout.
*/
- @IntRange(from = 0) int getMemoryUsage() {
+ public @IntRange(from = 0) int getMemoryUsage() {
return nGetMemoryUsage(mNativePtr);
}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index b740193..9458184 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -19,7 +19,6 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.util.IntArray;
import com.android.internal.util.Preconditions;
@@ -267,6 +266,22 @@
}
};
+ /** @hide */
+ public static class ParagraphInfo {
+ public final @IntRange(from = 0) int paragraphEnd;
+ public final @NonNull MeasuredParagraph measured;
+
+ /**
+ * @param paraEnd the end offset of this paragraph
+ * @param measured a measured paragraph
+ */
+ public ParagraphInfo(@IntRange(from = 0) int paraEnd, @NonNull MeasuredParagraph measured) {
+ this.paragraphEnd = paraEnd;
+ this.measured = measured;
+ }
+ };
+
+
// The original text.
private final @NonNull SpannedString mText;
@@ -278,11 +293,8 @@
private final @NonNull Params mParams;
- // The measured paragraph texts.
- private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
-
- // The sorted paragraph end offsets.
- private final @NonNull int[] mParagraphBreakPoints;
+ // The list of measured paragraph info.
+ private final @NonNull ParagraphInfo[] mParagraphInfo;
/**
* Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph
@@ -293,28 +305,25 @@
* </p>
*
* @param text the text to be measured
- * @param param parameters that define how text will be precomputed
+ * @param params parameters that define how text will be precomputed
* @return A {@link PrecomputedText}
*/
- public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params param) {
- return createInternal(text, param, 0, text.length(), true /* compute full Layout */);
+ public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) {
+ ParagraphInfo[] paraInfo = createMeasuredParagraphs(
+ text, params, 0, text.length(), true /* computeLayout */);
+ return new PrecomputedText(text, 0, text.length(), params, paraInfo);
}
/** @hide */
- public static PrecomputedText createWidthOnly(@NonNull CharSequence text, @NonNull Params param,
- @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
- return createInternal(text, param, start, end, false /* compute width only */);
- }
-
- private static PrecomputedText createInternal(@NonNull CharSequence text, @NonNull Params param,
+ public static ParagraphInfo[] createMeasuredParagraphs(
+ @NonNull CharSequence text, @NonNull Params params,
@IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) {
- Preconditions.checkNotNull(text);
- Preconditions.checkNotNull(param);
- final boolean needHyphenation = param.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
- && param.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
+ ArrayList<ParagraphInfo> result = new ArrayList<>();
- final IntArray paragraphEnds = new IntArray();
- final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(params);
+ final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
+ && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
int paraEnd = 0;
for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
@@ -327,27 +336,22 @@
paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
}
- paragraphEnds.add(paraEnd);
- measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
- param.getTextPaint(), text, paraStart, paraEnd, param.getTextDirection(),
- needHyphenation, computeLayout, null /* no recycle */));
+ result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
+ params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(),
+ needHyphenation, computeLayout, null /* no recycle */)));
}
-
- return new PrecomputedText(text, start, end, param,
- measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
- paragraphEnds.toArray());
+ return result.toArray(new ParagraphInfo[result.size()]);
}
// Use PrecomputedText.create instead.
private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
- @IntRange(from = 0) int end, @NonNull Params param,
- @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) {
+ @IntRange(from = 0) int end, @NonNull Params params,
+ @NonNull ParagraphInfo[] paraInfo) {
mText = new SpannedString(text);
mStart = start;
mEnd = end;
- mParams = param;
- mMeasuredParagraphs = measuredTexts;
- mParagraphBreakPoints = paragraphBreakPoints;
+ mParams = params;
+ mParagraphInfo = paraInfo;
}
/**
@@ -384,7 +388,7 @@
* Returns the count of paragraphs.
*/
public @IntRange(from = 0) int getParagraphCount() {
- return mParagraphBreakPoints.length;
+ return mParagraphInfo.length;
}
/**
@@ -392,7 +396,7 @@
*/
public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
- return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
+ return paraIndex == 0 ? mStart : getParagraphEnd(paraIndex - 1);
}
/**
@@ -400,12 +404,17 @@
*/
public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
- return mParagraphBreakPoints[paraIndex];
+ return mParagraphInfo[paraIndex].paragraphEnd;
}
/** @hide */
public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
- return mMeasuredParagraphs[paraIndex];
+ return mParagraphInfo[paraIndex].measured;
+ }
+
+ /** @hide */
+ public @NonNull ParagraphInfo[] getParagraphInfo() {
+ return mParagraphInfo;
}
/**
@@ -425,13 +434,13 @@
public int findParaIndex(@IntRange(from = 0) int pos) {
// TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring
// layout support to StaticLayout.
- for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
- if (pos < mParagraphBreakPoints[i]) {
+ for (int i = 0; i < mParagraphInfo.length; ++i) {
+ if (pos < mParagraphInfo[i].paragraphEnd) {
return i;
}
}
throw new IndexOutOfBoundsException(
- "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
+ "pos must be less than " + mParagraphInfo[mParagraphInfo.length - 1].paragraphEnd
+ ", gave " + pos);
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 299bde2..0899074 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -651,31 +651,29 @@
b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
indents, mLeftPaddings, mRightPaddings);
- PrecomputedText measured = null;
- final Spanned spanned;
+ PrecomputedText.ParagraphInfo[] paragraphInfo = null;
+ final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null;
if (source instanceof PrecomputedText) {
- measured = (PrecomputedText) source;
- if (!measured.canUseMeasuredResult(bufStart, bufEnd, textDir, paint, b.mBreakStrategy,
- b.mHyphenationFrequency)) {
+ PrecomputedText precomputed = (PrecomputedText) source;
+ if (precomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint,
+ b.mBreakStrategy, b.mHyphenationFrequency)) {
// Some parameters are different from the ones when measured text is created.
- measured = null;
+ paragraphInfo = precomputed.getParagraphInfo();
}
}
- if (measured == null) {
+ if (paragraphInfo == null) {
final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir,
b.mBreakStrategy, b.mHyphenationFrequency);
- measured = PrecomputedText.createWidthOnly(source, param, bufStart, bufEnd);
- spanned = (source instanceof Spanned) ? (Spanned) source : null;
- } else {
- final CharSequence original = measured.getText();
- spanned = (original instanceof Spanned) ? (Spanned) original : null;
+ paragraphInfo = PrecomputedText.createMeasuredParagraphs(source, param, bufStart,
+ bufEnd, false /* computeLayout */);
}
try {
- for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
- final int paraStart = measured.getParagraphStart(paraIndex);
- final int paraEnd = measured.getParagraphEnd(paraIndex);
+ for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
+ final int paraStart = paraIndex == 0
+ ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
+ final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;
int firstWidthLineCount = 1;
int firstWidth = outerWidth;
@@ -741,7 +739,7 @@
}
}
- final MeasuredParagraph measuredPara = measured.getMeasuredParagraph(paraIndex);
+ final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
final char[] chs = measuredPara.getChars();
final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
final int[] fmCache = measuredPara.getFontMetrics().getRawArray();