Reorganize MeasuredText API (2nd)
This is 2nd attempt of I7db9e2ca4db68a16648cfb8fcf63555f501304c2
This CL changes the MeasuredText API:
- Rename MeasuredText to PrecomputedText.
- Introduce PrecomputedText.Param which holds all text layout parameters.
- Add API to get PrecomputedText.Param from TextView.
- Remove MeasuredText.Builder and add PrecomputedText.create method instead.
- Remove setRange from MeasuredText since it is not for normal use case.
(It can not be used for TextView)
Bug: 67504091
Bug: 72861572
Test: bit FrameworksCoreTests:android.text.
Test: atest CtsWidgetTestCases:EditTextTest \
CtsWidgetTestCases:TextViewFadingEdgeTest \
FrameworksCoreTests:TextViewFallbackLineSpacingTest \
FrameworksCoreTests:TextViewTest FrameworksCoreTests:TypefaceTest \
CtsGraphicsTestCases:TypefaceTest CtsWidgetTestCases:TextViewTest \
CtsTextTestCases
Change-Id: Ie73bce52c6c673cda58973ddad04627a7cf2e5e9
diff --git a/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
similarity index 73%
rename from apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
rename to apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
index fc6302e..73e1724 100644
--- a/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
+++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
@@ -45,7 +45,7 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class MeasuredTextMemoryUsageTest {
+public class PrecomputedTextMemoryUsageTest {
private static final int WORD_LENGTH = 9; // Random word has 9 characters.
private static final boolean NO_STYLE_TEXT = false;
@@ -53,7 +53,7 @@
private static int TRIAL_COUNT = 100;
- public MeasuredTextMemoryUsageTest() {}
+ public PrecomputedTextMemoryUsageTest() {}
private TextPerfUtils mTextUtil = new TextPerfUtils();
@@ -77,13 +77,16 @@
@Test
public void testMemoryUsage_NoHyphenation() {
int[] memories = new int[TRIAL_COUNT];
- // Report median of randomly generated MeasuredText.
- for (int i = 0; i < TRIAL_COUNT; ++i) {
- memories[i] = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build().getMemoryUsage();
+ .build();
+
+ // Report median of randomly generated PrecomputedText.
+ for (int i = 0; i < TRIAL_COUNT; ++i) {
+ memories[i] = PrecomputedText.create(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
+ .getMemoryUsage();
}
reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation");
}
@@ -91,13 +94,16 @@
@Test
public void testMemoryUsage_Hyphenation() {
int[] memories = new int[TRIAL_COUNT];
- // Report median of randomly generated MeasuredText.
- for (int i = 0; i < TRIAL_COUNT; ++i) {
- memories[i] = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build().getMemoryUsage();
+ .build();
+
+ // Report median of randomly generated PrecomputedText.
+ for (int i = 0; i < TRIAL_COUNT; ++i) {
+ memories[i] = PrecomputedText.create(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
+ .getMemoryUsage();
}
reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation");
}
@@ -105,13 +111,16 @@
@Test
public void testMemoryUsage_NoHyphenation_WidthOnly() {
int[] memories = new int[TRIAL_COUNT];
- // Report median of randomly generated MeasuredText.
- for (int i = 0; i < TRIAL_COUNT; ++i) {
- memories[i] = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build(false /* width only */).getMemoryUsage();
+ .build();
+
+ // Report median of randomly generated PrecomputedText.
+ for (int i = 0; i < TRIAL_COUNT; ++i) {
+ CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+ memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
+ .getMemoryUsage();
}
reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly");
}
@@ -119,13 +128,16 @@
@Test
public void testMemoryUsage_Hyphenatation_WidthOnly() {
int[] memories = new int[TRIAL_COUNT];
- // Report median of randomly generated MeasuredText.
- for (int i = 0; i < TRIAL_COUNT; ++i) {
- memories[i] = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build(false /* width only */).getMemoryUsage();
+ .build();
+
+ // Report median of randomly generated PrecomputedText.
+ for (int i = 0; i < TRIAL_COUNT; ++i) {
+ CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+ memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
+ .getMemoryUsage();
}
reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly");
}
diff --git a/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
similarity index 66%
rename from apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
rename to apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
index 98f2bd5..1cd0ae1 100644
--- a/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
@@ -42,7 +42,7 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class MeasuredTextPerfTest {
+public class PrecomputedTextPerfTest {
private static final int WORD_LENGTH = 9; // Random word has 9 characters.
private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line.
private static final boolean NO_STYLE_TEXT = false;
@@ -51,7 +51,7 @@
private static TextPaint PAINT = new TextPaint();
private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
- public MeasuredTextPerfTest() {}
+ public PrecomputedTextPerfTest() {}
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -66,120 +66,136 @@
@Test
public void testCreate_NoStyled_Hyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build();
+
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
state.resumeTiming();
- new MeasuredText.Builder(text, PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build(true /* do full layout */);
+ PrecomputedText.create(text, param);
}
}
@Test
public void testCreate_NoStyled_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build();
+
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
state.resumeTiming();
- new MeasuredText.Builder(text, PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build(true /* do full layout */);
+ PrecomputedText.create(text, param);
}
}
@Test
public void testCreate_NoStyled_Hyphenation_WidthOnly() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build();
+
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
state.resumeTiming();
- new MeasuredText.Builder(text, PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build(false /* width only */);
+ PrecomputedText.create(text, param);
}
}
@Test
public void testCreate_NoStyled_NoHyphenation_WidthOnly() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build();
+
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
state.resumeTiming();
- new MeasuredText.Builder(text, PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build(false /* width only */);
+ PrecomputedText.create(text, param);
}
}
@Test
public void testCreate_Styled_Hyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build();
+
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
state.resumeTiming();
- new MeasuredText.Builder(text, PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build(true /* do full layout */);
+ PrecomputedText.create(text, param);
}
}
@Test
public void testCreate_Styled_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build();
+
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
state.resumeTiming();
- new MeasuredText.Builder(text, PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build(true /* do full layout */);
+ PrecomputedText.create(text, param);
}
}
@Test
public void testCreate_Styled_Hyphenation_WidthOnly() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build();
+
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
state.resumeTiming();
- new MeasuredText.Builder(text, PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build(false /* width only */);
+ PrecomputedText.create(text, param);
}
}
@Test
public void testCreate_Styled_NoHyphenation_WidthOnly() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build();
+
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
state.resumeTiming();
- new MeasuredText.Builder(text, PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build(false /* width only */);
+ PrecomputedText.create(text, param);
}
}
}
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 231aaf2..e1a38a0 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -63,6 +63,18 @@
mTextUtil.resetRandom(0 /* seed */);
}
+ private PrecomputedText makeMeasured(CharSequence text, TextPaint paint) {
+ PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint).build();
+ return PrecomputedText.create(text, param);
+ }
+
+ private PrecomputedText makeMeasured(CharSequence text, TextPaint paint, int strategy,
+ int frequency) {
+ PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint)
+ .setHyphenationFrequency(frequency).setBreakStrategy(strategy).build();
+ return PrecomputedText.create(text, param);
+ }
+
@Test
public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -151,15 +163,13 @@
}
@Test
- public void testCreate_MeasuredText_NoStyled_Greedy_NoHyphenation() {
+ public void testCreate_PrecomputedText_NoStyled_Greedy_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build();
+ final PrecomputedText text = makeMeasured(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
+ Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
state.resumeTiming();
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -170,15 +180,13 @@
}
@Test
- public void testCreate_MeasuredText_NoStyled_Greedy_Hyphenation() {
+ public void testCreate_PrecomputedText_NoStyled_Greedy_Hyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build();
+ final PrecomputedText text = makeMeasured(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
+ Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NORMAL);
state.resumeTiming();
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -189,15 +197,13 @@
}
@Test
- public void testCreate_MeasuredText_NoStyled_Balanced_NoHyphenation() {
+ public void testCreate_PrecomputedText_NoStyled_Balanced_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build();
+ final PrecomputedText text = makeMeasured(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
+ Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NONE);
state.resumeTiming();
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -208,15 +214,13 @@
}
@Test
- public void testCreate_MeasuredText_NoStyled_Balanced_Hyphenation() {
+ public void testCreate_PrecomputedText_NoStyled_Balanced_Hyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build();
+ final PrecomputedText text = makeMeasured(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
+ Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NORMAL);
state.resumeTiming();
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -227,15 +231,13 @@
}
@Test
- public void testCreate_MeasuredText_Styled_Greedy_NoHyphenation() {
+ public void testCreate_PrecomputedText_Styled_Greedy_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build();
+ final PrecomputedText text = makeMeasured(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT,
+ Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
state.resumeTiming();
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -328,13 +330,13 @@
}
@Test
- public void testDraw_MeasuredText_Styled() {
+ public void testDraw_PrecomputedText_Styled() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final RenderNode node = RenderNode.create("benchmark", null);
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+ final PrecomputedText text = makeMeasured(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
final DisplayListCanvas c = node.start(1200, 200);
@@ -345,13 +347,13 @@
}
@Test
- public void testDraw_MeasuredText_NoStyled() {
+ public void testDraw_PrecomputedText_NoStyled() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final RenderNode node = RenderNode.create("benchmark", null);
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+ final PrecomputedText text = makeMeasured(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
final DisplayListCanvas c = node.start(1200, 200);
@@ -362,13 +364,13 @@
}
@Test
- public void testDraw_MeasuredText_Styled_WithoutCache() {
+ public void testDraw_PrecomputedText_Styled_WithoutCache() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final RenderNode node = RenderNode.create("benchmark", null);
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+ final PrecomputedText text = makeMeasured(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
final DisplayListCanvas c = node.start(1200, 200);
@@ -380,13 +382,13 @@
}
@Test
- public void testDraw_MeasuredText_NoStyled_WithoutCache() {
+ public void testDraw_PrecomputedText_NoStyled_WithoutCache() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final RenderNode node = RenderNode.create("benchmark", null);
while (state.keepRunning()) {
state.pauseTiming();
- final MeasuredText text = new MeasuredText.Builder(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+ final PrecomputedText text = makeMeasured(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
final StaticLayout layout =
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
final DisplayListCanvas c = node.start(1200, 200);
diff --git a/api/current.txt b/api/current.txt
index a70363d..5f48f96 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13839,6 +13839,7 @@
method public int breakText(java.lang.String, boolean, float, float[]);
method public void clearShadowLayer();
method public float descent();
+ method public boolean equalsForTextMeasurement(android.graphics.Paint);
method public int getAlpha();
method public int getColor();
method public android.graphics.ColorFilter getColorFilter();
@@ -43241,36 +43242,6 @@
method public boolean isAllowed(char);
}
- public class MeasuredText implements android.text.Spanned {
- method public char charAt(int);
- method public int getBreakStrategy();
- method public int getEnd();
- method public int getHyphenationFrequency();
- method public android.text.TextPaint getPaint();
- method public int getParagraphCount();
- method public int getParagraphEnd(int);
- method public int getParagraphStart(int);
- method public int getSpanEnd(java.lang.Object);
- method public int getSpanFlags(java.lang.Object);
- method public int getSpanStart(java.lang.Object);
- method public <T> T[] getSpans(int, int, java.lang.Class<T>);
- method public int getStart();
- method public java.lang.CharSequence getText();
- method public android.text.TextDirectionHeuristic getTextDir();
- method public int length();
- method public int nextSpanTransition(int, int, java.lang.Class);
- method public java.lang.CharSequence subSequence(int, int);
- }
-
- public static final class MeasuredText.Builder {
- ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint);
- method public android.text.MeasuredText build();
- method public android.text.MeasuredText.Builder setBreakStrategy(int);
- method public android.text.MeasuredText.Builder setHyphenationFrequency(int);
- method public android.text.MeasuredText.Builder setRange(int, int);
- method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic);
- }
-
public abstract interface NoCopySpan {
}
@@ -43282,6 +43253,38 @@
method public abstract int getSpanTypeId();
}
+ public class PrecomputedText implements android.text.Spanned {
+ method public char charAt(int);
+ method public static android.text.PrecomputedText create(java.lang.CharSequence, android.text.PrecomputedText.Params);
+ method public int getParagraphCount();
+ method public int getParagraphEnd(int);
+ method public int getParagraphStart(int);
+ method public android.text.PrecomputedText.Params getParams();
+ method public int getSpanEnd(java.lang.Object);
+ method public int getSpanFlags(java.lang.Object);
+ method public int getSpanStart(java.lang.Object);
+ method public <T> T[] getSpans(int, int, java.lang.Class<T>);
+ method public java.lang.CharSequence getText();
+ method public int length();
+ method public int nextSpanTransition(int, int, java.lang.Class);
+ method public java.lang.CharSequence subSequence(int, int);
+ }
+
+ public static final class PrecomputedText.Params {
+ method public int getBreakStrategy();
+ method public int getHyphenationFrequency();
+ method public android.text.TextDirectionHeuristic getTextDirection();
+ method public android.text.TextPaint getTextPaint();
+ }
+
+ public static class PrecomputedText.Params.Builder {
+ ctor public PrecomputedText.Params.Builder(android.text.TextPaint);
+ method public android.text.PrecomputedText.Params build();
+ method public android.text.PrecomputedText.Params.Builder setBreakStrategy(int);
+ method public android.text.PrecomputedText.Params.Builder setHyphenationFrequency(int);
+ method public android.text.PrecomputedText.Params.Builder setTextDirection(android.text.TextDirectionHeuristic);
+ }
+
public class Selection {
method public static boolean extendDown(android.text.Spannable, android.text.Layout);
method public static boolean extendLeft(android.text.Spannable, android.text.Layout);
@@ -53665,6 +53668,7 @@
method public final android.content.res.ColorStateList getTextColors();
method public java.util.Locale getTextLocale();
method public android.os.LocaleList getTextLocales();
+ method public android.text.PrecomputedText.Params getTextMetricsParams();
method public float getTextScaleX();
method public float getTextSize();
method public int getTotalPaddingBottom();
@@ -53794,6 +53798,7 @@
method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType);
method public void setTextLocale(java.util.Locale);
method public void setTextLocales(android.os.LocaleList);
+ method public void setTextMetricsParams(android.text.PrecomputedText.Params);
method public void setTextScaleX(float);
method public void setTextSize(float);
method public void setTextSize(int, float);
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 6fa5312..ae59bfa 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -347,8 +347,8 @@
TextLine line = TextLine.obtain();
line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
- if (text instanceof MeasuredText) {
- MeasuredText mt = (MeasuredText) text;
+ if (text instanceof PrecomputedText) {
+ PrecomputedText mt = (PrecomputedText) text;
// Reaching here means there is only one paragraph.
MeasuredParagraph mp = mt.getMeasuredParagraph(0);
fm.width = (int) Math.ceil(mp.getWidth(0, mp.getTextLength()));
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
deleted file mode 100644
index bb7a9e0..0000000
--- a/core/java/android/text/MeasuredText.java
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.util.IntArray;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-
-/**
- * A text which has already been measured.
- */
-public class MeasuredText implements Spanned {
- private static final char LINE_FEED = '\n';
-
- // The original text.
- private final @NonNull CharSequence mText;
-
- // The inclusive start offset of the measuring target.
- private final @IntRange(from = 0) int mStart;
-
- // The exclusive end offset of the measuring target.
- private final @IntRange(from = 0) int mEnd;
-
- // The TextPaint used for measurement.
- private final @NonNull TextPaint mPaint;
-
- // The requested text direction.
- private final @NonNull TextDirectionHeuristic mTextDir;
-
- // The measured paragraph texts.
- private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
-
- // The sorted paragraph end offsets.
- private final @NonNull int[] mParagraphBreakPoints;
-
- // The break strategy for this measured text.
- private final @Layout.BreakStrategy int mBreakStrategy;
-
- // The hyphenation frequency for this measured text.
- private final @Layout.HyphenationFrequency int mHyphenationFrequency;
-
- /**
- * A Builder for MeasuredText
- */
- public static final class Builder {
- // Mandatory parameters.
- private final @NonNull CharSequence mText;
- private final @NonNull TextPaint mPaint;
-
- // Members to be updated by setters.
- private @IntRange(from = 0) int mStart;
- private @IntRange(from = 0) int mEnd;
- private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
- private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
- private @Layout.HyphenationFrequency int mHyphenationFrequency =
- Layout.HYPHENATION_FREQUENCY_NORMAL;
-
-
- /**
- * Builder constructor
- *
- * @param text The text to be measured.
- * @param paint The paint to be used for drawing.
- */
- public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) {
- Preconditions.checkNotNull(text);
- Preconditions.checkNotNull(paint);
-
- mText = text;
- mPaint = paint;
- mStart = 0;
- mEnd = text.length();
- }
-
- /**
- * Set the range of measuring target.
- *
- * @param start The measuring target start offset in the text.
- * @param end The measuring target end offset in the text.
- */
- public @NonNull Builder setRange(@IntRange(from = 0) int start,
- @IntRange(from = 0) int end) {
- Preconditions.checkArgumentInRange(start, 0, mText.length(), "start");
- Preconditions.checkArgumentInRange(end, 0, mText.length(), "end");
- Preconditions.checkArgument(start <= end, "The range is reversed.");
-
- mStart = start;
- mEnd = end;
- return this;
- }
-
- /**
- * Set the text direction heuristic
- *
- * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
- *
- * @param textDir The text direction heuristic for resolving bidi behavior.
- * @return this builder, useful for chaining.
- */
- public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
- Preconditions.checkNotNull(textDir);
- mTextDir = textDir;
- return this;
- }
-
- /**
- * Set the break strategy
- *
- * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
- *
- * @param breakStrategy The break strategy.
- * @return this builder, useful for chaining.
- */
- public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
- mBreakStrategy = breakStrategy;
- return this;
- }
-
- /**
- * Set the hyphenation frequency
- *
- * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
- *
- * @param hyphenationFrequency The hyphenation frequency.
- * @return this builder, useful for chaining.
- */
- public @NonNull Builder setHyphenationFrequency(
- @Layout.HyphenationFrequency int hyphenationFrequency) {
- mHyphenationFrequency = hyphenationFrequency;
- return this;
- }
-
- /**
- * Build the measured text
- *
- * @return the measured text.
- */
- public @NonNull MeasuredText build() {
- return build(true /* build full layout result */);
- }
-
- /** @hide */
- public @NonNull MeasuredText build(boolean computeLayout) {
- final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE
- && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE;
-
- final IntArray paragraphEnds = new IntArray();
- final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
-
- int paraEnd = 0;
- for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) {
- paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd);
- if (paraEnd < 0) {
- // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
- // end.
- paraEnd = mEnd;
- } else {
- paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
- }
-
- paragraphEnds.add(paraEnd);
- measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
- mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation,
- computeLayout, null /* no recycle */));
- }
-
- return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy,
- mHyphenationFrequency, measuredTexts.toArray(
- new MeasuredParagraph[measuredTexts.size()]),
- paragraphEnds.toArray());
- }
- };
-
- // Use MeasuredText.Builder instead.
- private MeasuredText(@NonNull CharSequence text,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @NonNull TextPaint paint,
- @NonNull TextDirectionHeuristic textDir,
- @Layout.BreakStrategy int breakStrategy,
- @Layout.HyphenationFrequency int frequency,
- @NonNull MeasuredParagraph[] measuredTexts,
- @NonNull int[] paragraphBreakPoints) {
- mText = text;
- mStart = start;
- mEnd = end;
- // Copy the paint so that we can keep the reference of typeface in native layout result.
- mPaint = new TextPaint(paint);
- mMeasuredParagraphs = measuredTexts;
- mParagraphBreakPoints = paragraphBreakPoints;
- mTextDir = textDir;
- mBreakStrategy = breakStrategy;
- mHyphenationFrequency = frequency;
- }
-
- /**
- * Return the underlying text.
- */
- public @NonNull CharSequence getText() {
- return mText;
- }
-
- /**
- * Returns the inclusive start offset of measured region.
- */
- public @IntRange(from = 0) int getStart() {
- return mStart;
- }
-
- /**
- * Returns the exclusive end offset of measured region.
- */
- public @IntRange(from = 0) int getEnd() {
- return mEnd;
- }
-
- /**
- * Returns the text direction associated with char sequence.
- */
- public @NonNull TextDirectionHeuristic getTextDir() {
- return mTextDir;
- }
-
- /**
- * Returns the paint used to measure this text.
- */
- public @NonNull TextPaint getPaint() {
- return mPaint;
- }
-
- /**
- * Returns the length of the paragraph of this text.
- */
- public @IntRange(from = 0) int getParagraphCount() {
- return mParagraphBreakPoints.length;
- }
-
- /**
- * Returns the paragraph start offset of the text.
- */
- public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
- Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
- return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
- }
-
- /**
- * Returns the paragraph end offset of the text.
- */
- public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
- Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
- return mParagraphBreakPoints[paraIndex];
- }
-
- /** @hide */
- public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
- return mMeasuredParagraphs[paraIndex];
- }
-
- /**
- * Returns the break strategy for this text.
- */
- public @Layout.BreakStrategy int getBreakStrategy() {
- return mBreakStrategy;
- }
-
- /**
- * Returns the hyphenation frequency for this text.
- */
- public @Layout.HyphenationFrequency int getHyphenationFrequency() {
- return mHyphenationFrequency;
- }
-
- /**
- * Returns true if the given TextPaint gives the same result of text layout for this text.
- * @hide
- */
- public boolean canUseMeasuredResult(@NonNull TextPaint paint) {
- return mPaint.getTextSize() == paint.getTextSize()
- && mPaint.getTextSkewX() == paint.getTextSkewX()
- && mPaint.getTextScaleX() == paint.getTextScaleX()
- && mPaint.getLetterSpacing() == paint.getLetterSpacing()
- && mPaint.getWordSpacing() == paint.getWordSpacing()
- && mPaint.getFlags() == paint.getFlags() // Maybe not all flag affects text layout.
- && mPaint.getTextLocales() == paint.getTextLocales() // need to be equals?
- && mPaint.getFontVariationSettings() == paint.getFontVariationSettings()
- && mPaint.getTypeface() == paint.getTypeface()
- && TextUtils.equals(mPaint.getFontFeatureSettings(), paint.getFontFeatureSettings());
- }
-
- /** @hide */
- public int findParaIndex(@IntRange(from = 0) int pos) {
- // TODO: Maybe good to remove paragraph concept from MeasuredText and add substring layout
- // support to StaticLayout.
- for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
- if (pos < mParagraphBreakPoints[i]) {
- return i;
- }
- }
- throw new IndexOutOfBoundsException(
- "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
- + ", gave " + pos);
- }
-
- /** @hide */
- public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
- final int paraIndex = findParaIndex(start);
- final int paraStart = getParagraphStart(paraIndex);
- final int paraEnd = getParagraphEnd(paraIndex);
- if (start < paraStart || paraEnd < end) {
- throw new RuntimeException("Cannot measured across the paragraph:"
- + "para: (" + paraStart + ", " + paraEnd + "), "
- + "request: (" + start + ", " + end + ")");
- }
- return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
- }
-
- /**
- * Returns the size of native MeasuredText memory usage
- *
- * Note that this may not be aculate. Must be used only for testing purposes.
- * @hide
- */
- public int getMemoryUsage() {
- int r = 0;
- for (int i = 0; i < getParagraphCount(); ++i) {
- r += getMeasuredParagraph(i).getMemoryUsage();
- }
- return r;
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Spanned overrides
- //
- // Just proxy for underlying mText if appropriate.
-
- @Override
- public <T> T[] getSpans(int start, int end, Class<T> type) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpans(start, end, type);
- } else {
- return ArrayUtils.emptyArray(type);
- }
- }
-
- @Override
- public int getSpanStart(Object tag) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpanStart(tag);
- } else {
- return -1;
- }
- }
-
- @Override
- public int getSpanEnd(Object tag) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpanEnd(tag);
- } else {
- return -1;
- }
- }
-
- @Override
- public int getSpanFlags(Object tag) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).getSpanFlags(tag);
- } else {
- return 0;
- }
- }
-
- @Override
- public int nextSpanTransition(int start, int limit, Class type) {
- if (mText instanceof Spanned) {
- return ((Spanned) mText).nextSpanTransition(start, limit, type);
- } else {
- return mText.length();
- }
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // CharSequence overrides.
- //
- // Just proxy for underlying mText.
-
- @Override
- public int length() {
- return mText.length();
- }
-
- @Override
- public char charAt(int index) {
- // TODO: Should this be index + mStart ?
- return mText.charAt(index);
- }
-
- @Override
- public CharSequence subSequence(int start, int end) {
- // TODO: return MeasuredText.
- // TODO: Should this be index + mStart, end + mStart ?
- return mText.subSequence(start, end);
- }
-
- @Override
- public String toString() {
- return mText.toString();
- }
-}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
new file mode 100644
index 0000000..c211e2d
--- /dev/null
+++ b/core/java/android/text/PrecomputedText.java
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IntArray;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+/**
+ * A text which has the character metrics data.
+ *
+ * A text object that contains the character metrics data and can be used to improve the performance
+ * of text layout operations. When a PrecomputedText is created with a given {@link CharSequence},
+ * it will measure the text metrics during the creation. This PrecomputedText instance can be set on
+ * {@link android.widget.TextView} or {@link StaticLayout}. Since the text layout information will
+ * be included in this instance, {@link android.widget.TextView} or {@link StaticLayout} will not
+ * have to recalculate this information.
+ *
+ * Note that the {@link PrecomputedText} created from different parameters of the target {@link
+ * android.widget.TextView} will be rejected internally and compute the text layout again with the
+ * current {@link android.widget.TextView} parameters.
+ *
+ * <pre>
+ * An example usage is:
+ * <code>
+ * void asyncSetText(final TextView textView, final String longString, Handler bgThreadHandler) {
+ * // construct precompute related parameters using the TextView that we will set the text on.
+ * final PrecomputedText.Params params = textView.getTextParams();
+ * bgThreadHandler.post(() -> {
+ * final PrecomputedText precomputedText =
+ * PrecomputedText.create(expensiveLongString, params);
+ * textView.post(() -> {
+ * textView.setText(precomputedText);
+ * });
+ * });
+ * }
+ * </code>
+ * </pre>
+ *
+ * Note that the {@link PrecomputedText} created from different parameters of the target
+ * {@link android.widget.TextView} will be rejected internally and compute the text layout again
+ * with the current {@link android.widget.TextView} parameters.
+ */
+public class PrecomputedText implements Spanned {
+ private static final char LINE_FEED = '\n';
+
+ /**
+ * The information required for building {@link PrecomputedText}.
+ *
+ * Contains information required for precomputing text measurement metadata, so it can be done
+ * in isolation of a {@link android.widget.TextView} or {@link StaticLayout}, when final layout
+ * constraints are not known.
+ */
+ public static final class Params {
+ // The TextPaint used for measurement.
+ private final @NonNull TextPaint mPaint;
+
+ // The requested text direction.
+ private final @NonNull TextDirectionHeuristic mTextDir;
+
+ // The break strategy for this measured text.
+ private final @Layout.BreakStrategy int mBreakStrategy;
+
+ // The hyphenation frequency for this measured text.
+ private final @Layout.HyphenationFrequency int mHyphenationFrequency;
+
+ /**
+ * A builder for creating {@link Params}.
+ */
+ public static class Builder {
+ // The TextPaint used for measurement.
+ private final @NonNull TextPaint mPaint;
+
+ // The requested text direction.
+ private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
+
+ // The break strategy for this measured text.
+ private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
+
+ // The hyphenation frequency for this measured text.
+ private @Layout.HyphenationFrequency int mHyphenationFrequency =
+ Layout.HYPHENATION_FREQUENCY_NORMAL;
+
+ /**
+ * Builder constructor.
+ *
+ * @param paint the paint to be used for drawing
+ */
+ public Builder(@NonNull TextPaint paint) {
+ mPaint = paint;
+ }
+
+ /**
+ * Set the line break strategy.
+ *
+ * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
+ *
+ * @param strategy the break strategy
+ * @return this builder, useful for chaining
+ * @see StaticLayout.Builder#setBreakStrategy
+ * @see android.widget.TextView#setBreakStrategy
+ */
+ public Builder setBreakStrategy(@Layout.BreakStrategy int strategy) {
+ mBreakStrategy = strategy;
+ return this;
+ }
+
+ /**
+ * Set the hyphenation frequency.
+ *
+ * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
+ *
+ * @param frequency the hyphenation frequency
+ * @return this builder, useful for chaining
+ * @see StaticLayout.Builder#setHyphenationFrequency
+ * @see android.widget.TextView#setHyphenationFrequency
+ */
+ public Builder setHyphenationFrequency(@Layout.HyphenationFrequency int frequency) {
+ mHyphenationFrequency = frequency;
+ return this;
+ }
+
+ /**
+ * Set the text direction heuristic.
+ *
+ * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
+ *
+ * @param textDir the text direction heuristic for resolving bidi behavior
+ * @return this builder, useful for chaining
+ * @see StaticLayout.Builder#setTextDirection
+ */
+ public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
+ mTextDir = textDir;
+ return this;
+ }
+
+ /**
+ * Build the {@link Params}.
+ *
+ * @return the layout parameter
+ */
+ public @NonNull Params build() {
+ return new Params(mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency);
+ }
+ }
+
+ // This is public hidden for internal use.
+ // For the external developers, use Builder instead.
+ /** @hide */
+ public Params(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir,
+ @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
+ mPaint = paint;
+ mTextDir = textDir;
+ mBreakStrategy = strategy;
+ mHyphenationFrequency = frequency;
+ }
+
+ /**
+ * Returns the {@link TextPaint} for this text.
+ *
+ * @return A {@link TextPaint}
+ */
+ public @NonNull TextPaint getTextPaint() {
+ return mPaint;
+ }
+
+ /**
+ * Returns the {@link TextDirectionHeuristic} for this text.
+ *
+ * @return A {@link TextDirectionHeuristic}
+ */
+ public @NonNull TextDirectionHeuristic getTextDirection() {
+ return mTextDir;
+ }
+
+ /**
+ * Returns the break strategy for this text.
+ *
+ * @return A line break strategy
+ */
+ public @Layout.BreakStrategy int getBreakStrategy() {
+ return mBreakStrategy;
+ }
+
+ /**
+ * Returns the hyphenation frequency for this text.
+ *
+ * @return A hyphenation frequency
+ */
+ public @Layout.HyphenationFrequency int getHyphenationFrequency() {
+ return mHyphenationFrequency;
+ }
+
+ private boolean isSameTextMetricsInternal(@NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy,
+ @Layout.HyphenationFrequency int frequency) {
+ return mTextDir == textDir
+ && mBreakStrategy == strategy
+ && mHyphenationFrequency == frequency
+ && mPaint.equalsForTextMeasurement(paint);
+ }
+
+ /**
+ * Check if the same text layout.
+ *
+ * @return true if this and the given param result in the same text layout
+ */
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o == null || !(o instanceof Params)) {
+ return false;
+ }
+ Params param = (Params) o;
+ return isSameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy,
+ param.mHyphenationFrequency);
+ }
+
+ @Override
+ public int hashCode() {
+ // TODO: implement MinikinPaint::hashCode and use it to keep consistency with equals.
+ return Objects.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), mPaint.getTextSkewX(),
+ mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(),
+ mPaint.getTextLocales(), mPaint.getTypeface(),
+ mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir,
+ mBreakStrategy, mHyphenationFrequency);
+ }
+ };
+
+ // The original text.
+ private final @NonNull SpannedString mText;
+
+ // The inclusive start offset of the measuring target.
+ private final @IntRange(from = 0) int mStart;
+
+ // The exclusive end offset of the measuring target.
+ private final @IntRange(from = 0) int mEnd;
+
+ private final @NonNull Params mParams;
+
+ // The measured paragraph texts.
+ private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
+
+ // The sorted paragraph end offsets.
+ private final @NonNull int[] mParagraphBreakPoints;
+
+ /**
+ * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph
+ * positioning information.
+ * <p>
+ * This can be expensive, so computing this on a background thread before your text will be
+ * presented can save work on the UI thread.
+ * </p>
+ *
+ * @param text the text to be measured
+ * @param param 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 */);
+ }
+
+ /** @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,
+ @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;
+
+ final IntArray paragraphEnds = new IntArray();
+ final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+
+ int paraEnd = 0;
+ for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
+ paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
+ if (paraEnd < 0) {
+ // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
+ // end.
+ paraEnd = end;
+ } else {
+ 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 */));
+ }
+
+ return new PrecomputedText(text, start, end, param,
+ measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
+ paragraphEnds.toArray());
+ }
+
+ // 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) {
+ mText = new SpannedString(text);
+ mStart = start;
+ mEnd = end;
+ mParams = param;
+ mMeasuredParagraphs = measuredTexts;
+ mParagraphBreakPoints = paragraphBreakPoints;
+ }
+
+ /**
+ * Return the underlying text.
+ */
+ public @NonNull CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Returns the inclusive start offset of measured region.
+ * @hide
+ */
+ public @IntRange(from = 0) int getStart() {
+ return mStart;
+ }
+
+ /**
+ * Returns the exclusive end offset of measured region.
+ * @hide
+ */
+ public @IntRange(from = 0) int getEnd() {
+ return mEnd;
+ }
+
+ /**
+ * Returns the layout parameters used to measure this text.
+ */
+ public @NonNull Params getParams() {
+ return mParams;
+ }
+
+ /**
+ * Returns the count of paragraphs.
+ */
+ public @IntRange(from = 0) int getParagraphCount() {
+ return mParagraphBreakPoints.length;
+ }
+
+ /**
+ * Returns the paragraph start offset of the text.
+ */
+ public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
+ Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+ return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
+ }
+
+ /**
+ * Returns the paragraph end offset of the text.
+ */
+ public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
+ Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+ return mParagraphBreakPoints[paraIndex];
+ }
+
+ /** @hide */
+ public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
+ return mMeasuredParagraphs[paraIndex];
+ }
+
+ /**
+ * Returns true if the given TextPaint gives the same result of text layout for this text.
+ * @hide
+ */
+ public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint,
+ @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
+ final TextPaint mtPaint = mParams.getTextPaint();
+ return mStart == start
+ && mEnd == end
+ && mParams.isSameTextMetricsInternal(paint, textDir, strategy, frequency);
+ }
+
+ /** @hide */
+ 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]) {
+ return i;
+ }
+ }
+ throw new IndexOutOfBoundsException(
+ "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
+ + ", gave " + pos);
+ }
+
+ /** @hide */
+ public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+ final int paraIndex = findParaIndex(start);
+ final int paraStart = getParagraphStart(paraIndex);
+ final int paraEnd = getParagraphEnd(paraIndex);
+ if (start < paraStart || paraEnd < end) {
+ throw new RuntimeException("Cannot measured across the paragraph:"
+ + "para: (" + paraStart + ", " + paraEnd + "), "
+ + "request: (" + start + ", " + end + ")");
+ }
+ return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
+ }
+
+ /**
+ * Returns the size of native PrecomputedText memory usage.
+ *
+ * Note that this is not guaranteed to be accurate. Must be used only for testing purposes.
+ * @hide
+ */
+ public int getMemoryUsage() {
+ int r = 0;
+ for (int i = 0; i < getParagraphCount(); ++i) {
+ r += getMeasuredParagraph(i).getMemoryUsage();
+ }
+ return r;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Spanned overrides
+ //
+ // Just proxy for underlying mText if appropriate.
+
+ @Override
+ public <T> T[] getSpans(int start, int end, Class<T> type) {
+ return mText.getSpans(start, end, type);
+ }
+
+ @Override
+ public int getSpanStart(Object tag) {
+ return mText.getSpanStart(tag);
+ }
+
+ @Override
+ public int getSpanEnd(Object tag) {
+ return mText.getSpanEnd(tag);
+ }
+
+ @Override
+ public int getSpanFlags(Object tag) {
+ return mText.getSpanFlags(tag);
+ }
+
+ @Override
+ public int nextSpanTransition(int start, int limit, Class type) {
+ return mText.nextSpanTransition(start, limit, type);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // CharSequence overrides.
+ //
+ // Just proxy for underlying mText.
+
+ @Override
+ public int length() {
+ return mText.length();
+ }
+
+ @Override
+ public char charAt(int index) {
+ return mText.charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ return PrecomputedText.create(mText.subSequence(start, end), mParams);
+ }
+
+ @Override
+ public String toString() {
+ return mText.toString();
+ }
+}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index e62f421..299bde2 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -651,42 +651,25 @@
b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
indents, mLeftPaddings, mRightPaddings);
- MeasuredText measured = null;
+ PrecomputedText measured = null;
final Spanned spanned;
- final boolean canUseMeasuredText;
- if (source instanceof MeasuredText) {
- measured = (MeasuredText) source;
-
- if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) {
- // The buffer position has changed. Re-measure here.
- canUseMeasuredText = false;
- } else if (b.mBreakStrategy != measured.getBreakStrategy()
- || b.mHyphenationFrequency != measured.getHyphenationFrequency()) {
- // The computed hyphenation pieces may not be able to used. Re-measure it.
- canUseMeasuredText = false;
- } else {
- // We can use measured information.
- canUseMeasuredText = true;
+ if (source instanceof PrecomputedText) {
+ measured = (PrecomputedText) source;
+ if (!measured.canUseMeasuredResult(bufStart, bufEnd, textDir, paint, b.mBreakStrategy,
+ b.mHyphenationFrequency)) {
+ // Some parameters are different from the ones when measured text is created.
+ measured = null;
}
- } else {
- canUseMeasuredText = false;
}
- if (!canUseMeasuredText) {
- measured = new MeasuredText.Builder(source, paint)
- .setRange(bufStart, bufEnd)
- .setTextDirection(textDir)
- .setBreakStrategy(b.mBreakStrategy)
- .setHyphenationFrequency(b.mHyphenationFrequency)
- .build(false /* full layout is not necessary for line breaking */);
+ if (measured == 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;
- // Overwrite with the one when measured.
- // TODO: Give an option for developer not to overwrite and measure again here?
- textDir = measured.getTextDir();
- paint = measured.getPaint();
}
try {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 55367dc..117a77d 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -60,7 +60,7 @@
private char[] mChars;
private boolean mCharsValid;
private Spanned mSpanned;
- private MeasuredText mMeasured;
+ private PrecomputedText mComputed;
// Additional width of whitespace for justification. This value is per whitespace, thus
// the line width will increase by mAddedWidth x (number of stretchable whitespaces).
@@ -119,7 +119,7 @@
tl.mSpanned = null;
tl.mTabs = null;
tl.mChars = null;
- tl.mMeasured = null;
+ tl.mComputed = null;
tl.mMetricAffectingSpanSpanSet.recycle();
tl.mCharacterStyleSpanSet.recycle();
@@ -170,12 +170,9 @@
hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
}
- mMeasured = null;
- if (text instanceof MeasuredText) {
- MeasuredText mt = (MeasuredText) text;
- if (mt.canUseMeasuredResult(paint)) {
- mMeasured = mt;
- }
+ mComputed = null;
+ if (text instanceof PrecomputedText) {
+ mComputed = (PrecomputedText) text;
}
mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
@@ -746,12 +743,12 @@
return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset);
} else {
final int delta = mStart;
- if (mMeasured == null) {
+ if (mComputed == null) {
// TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text.
return wp.getRunAdvance(mText, delta + start, delta + end,
delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
} else {
- return mMeasured.getWidth(start + delta, end + delta);
+ return mComputed.getWidth(start + delta, end + delta);
}
}
}
diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java
index fbb862b..fc7d828 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/core/java/android/view/RecordingCanvas.java
@@ -34,7 +34,7 @@
import android.graphics.RectF;
import android.graphics.TemporaryBuffer;
import android.text.GraphicsOperations;
-import android.text.MeasuredText;
+import android.text.PrecomputedText;
import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
@@ -507,8 +507,8 @@
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
long measuredTextPtr = 0;
int measuredTextOffset = 0;
- if (text instanceof MeasuredText) {
- MeasuredText mt = (MeasuredText) text;
+ if (text instanceof PrecomputedText) {
+ PrecomputedText mt = (PrecomputedText) text;
int paraIndex = mt.findParaIndex(start);
if (end <= mt.getParagraphEnd(paraIndex)) {
// Only support if the target is in the same paragraph.
@@ -641,7 +641,7 @@
@FastNative
private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
- long nativeMeasuredText, int measuredTextOffset);
+ long nativePrecomputedText, int measuredTextOffset);
@FastNative
private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1e02c30..8aae0d8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -80,8 +80,8 @@
import android.text.InputFilter;
import android.text.InputType;
import android.text.Layout;
-import android.text.MeasuredText;
import android.text.ParcelableSpan;
+import android.text.PrecomputedText;
import android.text.Selection;
import android.text.SpanWatcher;
import android.text.Spannable;
@@ -4092,6 +4092,35 @@
}
/**
+ * Gets the parameters for text layout precomputation, for use with {@link PrecomputedText}.
+ *
+ * @return a current {@link PrecomputedText.Params}
+ * @see PrecomputedText
+ */
+ public @NonNull PrecomputedText.Params getTextMetricsParams() {
+ return new PrecomputedText.Params(new TextPaint(mTextPaint), getTextDirectionHeuristic(),
+ mBreakStrategy, mHyphenationFrequency);
+ }
+
+ /**
+ * Apply the text layout parameter.
+ *
+ * Update the TextView parameters to be compatible with {@link PrecomputedText.Params}.
+ * @see PrecomputedText
+ */
+ public void setTextMetricsParams(@NonNull PrecomputedText.Params params) {
+ mTextPaint.set(params.getTextPaint());
+ mTextDir = params.getTextDirection();
+ mBreakStrategy = params.getBreakStrategy();
+ mHyphenationFrequency = params.getHyphenationFrequency();
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
+ }
+
+ /**
* Set justification mode. The default value is {@link Layout#JUSTIFICATION_MODE_NONE}. If the
* last line is too short for justification, the last line will be displayed with the
* alignment set by {@link android.view.View#setTextAlignment}.
@@ -5584,7 +5613,7 @@
if (imm != null) imm.restartInput(this);
} else if (type == BufferType.SPANNABLE || mMovement != null) {
text = mSpannableFactory.newSpannable(text);
- } else if (!(text instanceof MeasuredText || text instanceof CharWrapper)) {
+ } else if (!(text instanceof PrecomputedText || text instanceof CharWrapper)) {
text = TextUtils.stringOrSpannedString(text);
}
@@ -11712,6 +11741,9 @@
}
/**
+ * Returns the current {@link TextDirectionHeuristic}.
+ *
+ * @return the current {@link TextDirectionHeuristic}.
* @hide
*/
protected TextDirectionHeuristic getTextDirectionHeuristic() {
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 482d028..2c05d0b 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -1008,6 +1008,23 @@
return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
}
+ static jboolean equalsForTextMeasurement(jlong lPaint, jlong rPaint) {
+ if (lPaint == rPaint) {
+ return true;
+ }
+ Paint* leftPaint = reinterpret_cast<Paint*>(lPaint);
+ Paint* rightPaint = reinterpret_cast<Paint*>(rPaint);
+
+ const Typeface* leftTypeface = Typeface::resolveDefault(leftPaint->getAndroidTypeface());
+ const Typeface* rightTypeface = Typeface::resolveDefault(rightPaint->getAndroidTypeface());
+ minikin::MinikinPaint leftMinikinPaint
+ = MinikinUtils::prepareMinikinPaint(leftPaint, leftTypeface);
+ minikin::MinikinPaint rightMinikinPaint
+ = MinikinUtils::prepareMinikinPaint(rightPaint, rightTypeface);
+
+ return leftMinikinPaint == rightMinikinPaint;
+ }
+
}; // namespace PaintGlue
static const JNINativeMethod methods[] = {
@@ -1107,7 +1124,8 @@
{"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
{"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
{"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer},
- {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}
+ {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
+ {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
};
int register_android_graphics_Paint(JNIEnv* env) {
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index eacb727..07df045 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -22,7 +22,7 @@
import android.annotation.Size;
import android.graphics.Canvas.VertexMode;
import android.text.GraphicsOperations;
-import android.text.MeasuredText;
+import android.text.PrecomputedText;
import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
@@ -487,8 +487,8 @@
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
long measuredTextPtr = 0;
int measuredTextOffset = 0;
- if (text instanceof MeasuredText) {
- MeasuredText mt = (MeasuredText) text;
+ if (text instanceof PrecomputedText) {
+ PrecomputedText mt = (PrecomputedText) text;
int paraIndex = mt.findParaIndex(start);
if (end <= mt.getParagraphEnd(paraIndex)) {
// Only suppor the same paragraph.
@@ -647,7 +647,7 @@
private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
- long nativeMeasuredText, int measuredTextOffset);
+ long nativePrecomputedText, int measuredTextOffset);
private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index ed147e9..42dac38 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2835,6 +2835,16 @@
return result;
}
+ /**
+ * Returns true of the passed {@link Paint} will have the same effect on text measurement
+ *
+ * @param other A {@link Paint} object.
+ * @return true if the other {@link Paint} has the same effect on text measurement.
+ */
+ public boolean equalsForTextMeasurement(@NonNull Paint other) {
+ return nEqualsForTextMeasurement(mNativePaint, other.mNativePaint);
+ }
+
// regular JNI
private static native long nGetNativeFinalizer();
private static native long nInit();
@@ -3002,4 +3012,6 @@
private static native float nGetStrikeThruThickness(long paintPtr);
@CriticalNative
private static native void nSetTextSize(long paintPtr, float textSize);
+ @CriticalNative
+ private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
}