summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Seigo Nonaka <nona@google.com> 2018-12-12 11:34:13 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-12-12 11:34:13 +0000
commitab11e004490229e41cbd3cc31361bb67bb8a3e07 (patch)
treeb993f2fad594681002a0ee30ead458f7d52c51bc
parent745bdd02cda76324cccf14fb8aa9c24af87f22cb (diff)
parent291ef0536d2ed154f5c559dbcdbfc92b10a21e66 (diff)
Merge "Recompute PcT with existing PcT for different direction"
-rw-r--r--api/current.txt2
-rw-r--r--core/java/android/text/MeasuredParagraph.java15
-rw-r--r--core/java/android/text/PrecomputedText.java121
-rw-r--r--core/java/android/text/StaticLayout.java24
-rw-r--r--core/java/android/widget/TextView.java14
-rw-r--r--core/jni/android/graphics/text/MeasuredText.cpp10
-rw-r--r--core/tests/coretests/src/android/text/MeasuredParagraphTest.java7
-rw-r--r--graphics/java/android/graphics/text/MeasuredText.java41
8 files changed, 197 insertions, 37 deletions
diff --git a/api/current.txt b/api/current.txt
index c893840fe0ac..4804136173f1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15724,6 +15724,7 @@ package android.graphics.text {
public static class MeasuredText.Builder {
ctor public MeasuredText.Builder(char[]);
+ ctor public MeasuredText.Builder(android.graphics.text.MeasuredText);
method public android.graphics.text.MeasuredText.Builder appendReplacementRun(android.graphics.Paint, int, float);
method public android.graphics.text.MeasuredText.Builder appendStyleRun(android.graphics.Paint, int, boolean);
method public android.graphics.text.MeasuredText build();
@@ -45266,6 +45267,7 @@ package android.text {
public static class PrecomputedText.Params.Builder {
ctor public PrecomputedText.Params.Builder(android.text.TextPaint);
+ ctor public PrecomputedText.Params.Builder(android.text.PrecomputedText.Params);
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);
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index f9370a8aa6af..7e41878b3d4e 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -377,6 +377,9 @@ public class MeasuredParagraph {
* @param start the inclusive start offset of the target region in the text
* @param end the exclusive end offset of the target region in the text
* @param textDir the text direction
+ * @param computeHyphenation true if need to compute hyphenation, otherwise false
+ * @param computeLayout true if need to compute full layout, otherwise false.
+ * @param hint pass if you already have measured paragraph.
* @param recycle pass existing MeasuredParagraph if you want to recycle it.
*
* @return measured text
@@ -389,12 +392,18 @@ public class MeasuredParagraph {
@NonNull TextDirectionHeuristic textDir,
boolean computeHyphenation,
boolean computeLayout,
+ @Nullable MeasuredParagraph hint,
@Nullable MeasuredParagraph recycle) {
final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
mt.resetAndAnalyzeBidi(text, start, end, textDir);
- final MeasuredText.Builder builder = new MeasuredText.Builder(mt.mCopiedBuffer);
- builder.setComputeHyphenation(computeHyphenation);
- builder.setComputeLayout(computeLayout);
+ final MeasuredText.Builder builder;
+ if (hint == null) {
+ builder = new MeasuredText.Builder(mt.mCopiedBuffer)
+ .setComputeHyphenation(computeHyphenation)
+ .setComputeLayout(computeLayout);
+ } else {
+ builder = new MeasuredText.Builder(hint.mMeasuredText);
+ }
if (mt.mTextLength == 0) {
// Need to build empty native measured text for StaticLayout.
// TODO: Stop creating empty measured text for empty lines.
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index b7ea0122cb06..08741d6a7d88 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -17,6 +17,7 @@
package android.text;
import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,6 +26,8 @@ import android.text.style.MetricAffectingSpan;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
@@ -119,6 +122,16 @@ public class PrecomputedText implements Spannable {
}
/**
+ * Builder constructor from existing params.
+ */
+ public Builder(@NonNull Params params) {
+ mPaint = params.mPaint;
+ mTextDir = params.mTextDir;
+ mBreakStrategy = params.mBreakStrategy;
+ mHyphenationFrequency = params.mHyphenationFrequency;
+ }
+
+ /**
* Set the line break strategy.
*
* The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
@@ -220,13 +233,41 @@ public class PrecomputedText implements Spannable {
}
/** @hide */
- public boolean isSameTextMetricsInternal(@NonNull TextPaint paint,
+ @IntDef(value = { UNUSABLE, NEED_RECOMPUTE, USABLE })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CheckResultUsableResult {}
+
+ /**
+ * Constant for returning value of checkResultUsable indicating that given parameter is not
+ * compatible.
+ * @hide
+ */
+ public static final int UNUSABLE = 0;
+
+ /**
+ * Constant for returning value of checkResultUsable indicating that given parameter is not
+ * compatible but partially usable for creating new PrecomputedText.
+ * @hide
+ */
+ public static final int NEED_RECOMPUTE = 1;
+
+ /**
+ * Constant for returning value of checkResultUsable indicating that given parameter is
+ * compatible.
+ * @hide
+ */
+ public static final int USABLE = 2;
+
+ /** @hide */
+ public @CheckResultUsableResult int checkResultUsable(@NonNull TextPaint paint,
@NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy,
@Layout.HyphenationFrequency int frequency) {
- return mTextDir == textDir
- && mBreakStrategy == strategy
- && mHyphenationFrequency == frequency
- && mPaint.equalsForTextMeasurement(paint);
+ if (mBreakStrategy == strategy && mHyphenationFrequency == frequency
+ && mPaint.equalsForTextMeasurement(paint)) {
+ return mTextDir == textDir ? USABLE : NEED_RECOMPUTE;
+ } else {
+ return UNUSABLE;
+ }
}
/**
@@ -243,8 +284,8 @@ public class PrecomputedText implements Spannable {
return false;
}
Params param = (Params) o;
- return isSameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy,
- param.mHyphenationFrequency);
+ return checkResultUsable(param.mPaint, param.mTextDir, param.mBreakStrategy,
+ param.mHyphenationFrequency) == Params.USABLE;
}
@Override
@@ -321,11 +362,55 @@ public class PrecomputedText implements Spannable {
* @return A {@link PrecomputedText}
*/
public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) {
- ParagraphInfo[] paraInfo = createMeasuredParagraphs(
- text, params, 0, text.length(), true /* computeLayout */);
+ ParagraphInfo[] paraInfo = null;
+ if (text instanceof PrecomputedText) {
+ final PrecomputedText hintPct = (PrecomputedText) text;
+ final PrecomputedText.Params hintParams = hintPct.getParams();
+ final @Params.CheckResultUsableResult int checkResult =
+ hintParams.checkResultUsable(params.mPaint, params.mTextDir,
+ params.mBreakStrategy, params.mHyphenationFrequency);
+ switch (checkResult) {
+ case Params.USABLE:
+ return hintPct;
+ case Params.NEED_RECOMPUTE:
+ // To be able to use PrecomputedText for new params, at least break strategy and
+ // hyphenation frequency must be the same.
+ if (params.getBreakStrategy() == hintParams.getBreakStrategy()
+ && params.getHyphenationFrequency()
+ == hintParams.getHyphenationFrequency()) {
+ paraInfo = createMeasuredParagraphsFromPrecomputedText(
+ hintPct, params, true /* compute layout */);
+ }
+ break;
+ case Params.UNUSABLE:
+ // Unable to use anything in PrecomputedText. Create PrecomputedText as the
+ // normal text input.
+ }
+
+ }
+ if (paraInfo == null) {
+ paraInfo = createMeasuredParagraphs(
+ text, params, 0, text.length(), true /* computeLayout */);
+ }
return new PrecomputedText(text, 0, text.length(), params, paraInfo);
}
+ private static ParagraphInfo[] createMeasuredParagraphsFromPrecomputedText(
+ @NonNull PrecomputedText pct, @NonNull Params params, boolean computeLayout) {
+ final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
+ && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
+ ArrayList<ParagraphInfo> result = new ArrayList<>();
+ for (int i = 0; i < pct.getParagraphCount(); ++i) {
+ final int paraStart = pct.getParagraphStart(i);
+ final int paraEnd = pct.getParagraphEnd(i);
+ result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
+ params.getTextPaint(), pct, paraStart, paraEnd, params.getTextDirection(),
+ needHyphenation, computeLayout, pct.getMeasuredParagraph(i),
+ null /* no recycle */)));
+ }
+ return result.toArray(new ParagraphInfo[result.size()]);
+ }
+
/** @hide */
public static ParagraphInfo[] createMeasuredParagraphs(
@NonNull CharSequence text, @NonNull Params params,
@@ -350,7 +435,8 @@ public class PrecomputedText implements Spannable {
result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(),
- needHyphenation, computeLayout, null /* no recycle */)));
+ needHyphenation, computeLayout, null /* no hint */,
+ null /* no recycle */)));
}
return result.toArray(new ParagraphInfo[result.size()]);
}
@@ -434,12 +520,15 @@ public class PrecomputedText implements Spannable {
* 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) {
- return mStart == start
- && mEnd == end
- && mParams.isSameTextMetricsInternal(paint, textDir, strategy, frequency);
+ public @Params.CheckResultUsableResult int checkResultUsable(@IntRange(from = 0) int start,
+ @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir,
+ @NonNull TextPaint paint, @Layout.BreakStrategy int strategy,
+ @Layout.HyphenationFrequency int frequency) {
+ if (mStart != start || mEnd != end) {
+ return Params.UNUSABLE;
+ } else {
+ return mParams.checkResultUsable(paint, textDir, strategy, frequency);
+ }
}
/** @hide */
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 8cb18b255589..3d0c6622d8af 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -650,10 +650,26 @@ public class StaticLayout extends Layout {
final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null;
if (source instanceof PrecomputedText) {
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.
- paragraphInfo = precomputed.getParagraphInfo();
+ final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
+ precomputed.checkResultUsable(bufStart, bufEnd, textDir, paint,
+ b.mBreakStrategy, b.mHyphenationFrequency);
+ switch (checkResult) {
+ case PrecomputedText.Params.UNUSABLE:
+ break;
+ case PrecomputedText.Params.NEED_RECOMPUTE:
+ final PrecomputedText.Params newParams =
+ new PrecomputedText.Params.Builder(paint)
+ .setBreakStrategy(b.mBreakStrategy)
+ .setHyphenationFrequency(b.mHyphenationFrequency)
+ .setTextDirection(textDir)
+ .build();
+ precomputed = PrecomputedText.create(precomputed, newParams);
+ paragraphInfo = precomputed.getParagraphInfo();
+ break;
+ case PrecomputedText.Params.USABLE:
+ // Some parameters are different from the ones when measured text is created.
+ paragraphInfo = precomputed.getParagraphInfo();
+ break;
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 522be81091fb..90da81276ba3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6028,14 +6028,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mTextDir == null) {
mTextDir = getTextDirectionHeuristic();
}
- if (!precomputed.getParams().isSameTextMetricsInternal(
- getPaint(), mTextDir, mBreakStrategy, mHyphenationFrequency)) {
- throw new IllegalArgumentException(
+ final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
+ precomputed.getParams().checkResultUsable(getPaint(), mTextDir, mBreakStrategy,
+ mHyphenationFrequency);
+ switch (checkResult) {
+ case PrecomputedText.Params.UNUSABLE:
+ throw new IllegalArgumentException(
"PrecomputedText's Parameters don't match the parameters of this TextView."
+ "Consider using setTextMetricsParams(precomputedText.getParams()) "
+ "to override the settings of this TextView: "
+ "PrecomputedText: " + precomputed.getParams()
+ "TextView: " + getTextMetricsParams());
+ case PrecomputedText.Params.NEED_RECOMPUTE:
+ precomputed = PrecomputedText.create(precomputed, getTextMetricsParams());
+ break;
+ case PrecomputedText.Params.USABLE:
+ // pass through
}
} else if (type == BufferType.SPANNABLE || mMovement != null) {
text = mSpannableFactory.newSpannable(text);
diff --git a/core/jni/android/graphics/text/MeasuredText.cpp b/core/jni/android/graphics/text/MeasuredText.cpp
index 0bfadb407a93..d7d96fbf3956 100644
--- a/core/jni/android/graphics/text/MeasuredText.cpp
+++ b/core/jni/android/graphics/text/MeasuredText.cpp
@@ -84,14 +84,14 @@ static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong
// Regular JNI
static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr,
- jcharArray javaText, jboolean computeHyphenation,
- jboolean computeLayout) {
+ jlong hintPtr, jcharArray javaText, jboolean computeHyphenation,
+ jboolean computeLayout) {
ScopedCharArrayRO text(env, javaText);
const minikin::U16StringPiece textBuffer(text.get(), text.size());
// Pass the ownership to Java.
- return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation,
- computeLayout).release());
+ return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation, computeLayout,
+ toMeasuredParagraph(hintPtr)).release());
}
// Regular JNI
@@ -147,7 +147,7 @@ static const JNINativeMethod gMTBuilderMethods[] = {
{"nInitBuilder", "()J", (void*) nInitBuilder},
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
- {"nBuildMeasuredText", "(J[CZZ)J", (void*) nBuildMeasuredText},
+ {"nBuildMeasuredText", "(JJ[CZZ)J", (void*) nBuildMeasuredText},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
};
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index 3d15eb9577b5..a0dca2c0dc4f 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -133,7 +133,8 @@ public class MeasuredParagraphTest {
public void buildForStaticLayout() {
MeasuredParagraph mt = null;
- mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, false, false, null);
+ mt = MeasuredParagraph.buildForStaticLayout(
+ PAINT, "XXX", 0, 3, LTR, false, false, null /* no hint */, null);
assertNotNull(mt);
assertNotNull(mt.getChars());
assertEquals("XXX", charsToString(mt.getChars()));
@@ -147,8 +148,8 @@ public class MeasuredParagraphTest {
assertNotNull(mt.getMeasuredText());
// Recycle it
- MeasuredParagraph mt2 =
- MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, false, false, mt);
+ MeasuredParagraph mt2 = MeasuredParagraph.buildForStaticLayout(
+ PAINT, "_VVV_", 1, 4, RTL, false, false, null /* no hint */, mt);
assertEquals(mt2, mt);
assertNotNull(mt2.getChars());
assertEquals("VVV", charsToString(mt.getChars()));
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 6f786517ba12..2536619bb7c8 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -19,6 +19,7 @@ package android.graphics.text;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.Px;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -49,12 +50,17 @@ import libcore.util.NativeAllocationRegistry;
*/
public class MeasuredText {
private long mNativePtr;
+ private boolean mComputeHyphenation;
+ private boolean mComputeLayout;
private @NonNull char[] mChars;
// Use builder instead.
- private MeasuredText(long ptr, @NonNull char[] chars) {
+ private MeasuredText(long ptr, @NonNull char[] chars, boolean computeHyphenation,
+ boolean computeLayout) {
mNativePtr = ptr;
mChars = chars;
+ mComputeHyphenation = computeHyphenation;
+ mComputeLayout = computeLayout;
}
/**
@@ -172,6 +178,7 @@ public class MeasuredText {
private boolean mComputeHyphenation = false;
private boolean mComputeLayout = true;
private int mCurrentOffset = 0;
+ private @Nullable MeasuredText mHintMt = null;
/**
* Construct a builder.
@@ -188,6 +195,27 @@ public class MeasuredText {
}
/**
+ * Construct a builder with existing MeasuredText.
+ *
+ * The MeasuredText returned by build method will hold a reference of the text. Developer is
+ * not supposed to modify the text.
+ *
+ * @param text a text
+ */
+ public Builder(@NonNull MeasuredText text) {
+ Preconditions.checkNotNull(text);
+ mText = text.mChars;
+ mNativePtr = nInitBuilder();
+ if (!text.mComputeLayout) {
+ throw new IllegalArgumentException(
+ "The input MeasuredText must not be created with setComputeLayout(false).");
+ }
+ mComputeHyphenation = text.mComputeHyphenation;
+ mComputeLayout = text.mComputeLayout;
+ mHintMt = text;
+ }
+
+ /**
* Apply styles to the given length.
*
* Keeps an internal offset which increases at every append. The initial value for this
@@ -282,10 +310,16 @@ public class MeasuredText {
if (mCurrentOffset != mText.length) {
throw new IllegalStateException("Style info has not been provided for all text.");
}
+ if (mHintMt != null && mHintMt.mComputeHyphenation != mComputeHyphenation) {
+ throw new IllegalArgumentException(
+ "The hyphenation configuration is different from given hint MeasuredText");
+ }
try {
- long ptr = nBuildMeasuredText(mNativePtr, mText, mComputeHyphenation,
+ long hintPtr = (mHintMt == null) ? 0 : mHintMt.getNativePtr();
+ long ptr = nBuildMeasuredText(mNativePtr, hintPtr, mText, mComputeHyphenation,
+ mComputeLayout);
+ final MeasuredText res = new MeasuredText(ptr, mText, mComputeHyphenation,
mComputeLayout);
- MeasuredText res = new MeasuredText(ptr, mText);
sRegistry.registerNativeAllocation(res, ptr);
return res;
} finally {
@@ -339,6 +373,7 @@ public class MeasuredText {
private static native long nBuildMeasuredText(
/* Non Zero */ long nativeBuilderPtr,
+ long hintMtPtr,
@NonNull char[] text,
boolean computeHyphenation,
boolean computeLayout);