diff options
| author | 2018-12-12 11:34:13 +0000 | |
|---|---|---|
| committer | 2018-12-12 11:34:13 +0000 | |
| commit | ab11e004490229e41cbd3cc31361bb67bb8a3e07 (patch) | |
| tree | b993f2fad594681002a0ee30ead458f7d52c51bc | |
| parent | 745bdd02cda76324cccf14fb8aa9c24af87f22cb (diff) | |
| parent | 291ef0536d2ed154f5c559dbcdbfc92b10a21e66 (diff) | |
Merge "Recompute PcT with existing PcT for different direction"
| -rw-r--r-- | api/current.txt | 2 | ||||
| -rw-r--r-- | core/java/android/text/MeasuredParagraph.java | 15 | ||||
| -rw-r--r-- | core/java/android/text/PrecomputedText.java | 121 | ||||
| -rw-r--r-- | core/java/android/text/StaticLayout.java | 24 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 14 | ||||
| -rw-r--r-- | core/jni/android/graphics/text/MeasuredText.cpp | 10 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/text/MeasuredParagraphTest.java | 7 | ||||
| -rw-r--r-- | graphics/java/android/graphics/text/MeasuredText.java | 41 |
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); |