From 749e57e5c333289814dcdf8efb2c82b00585d838 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Tue, 29 Aug 2017 15:00:05 -0700 Subject: [PATCH] Implement LineBreaker callback and fix indent repeating. The last indent should be repeated, so we need to pass an int array instead of null even if the indent offset is out of array length. Bug: 65024629 Test: bit CtsTextTestCases:android.text.cts.StaticLayoutTest Test: bit CtsTextTestCases:android.text.cts.DynamicLayoutTest Test: bit CtsWidgetTestCases:android.widget.cts.TextViewTest Bug: 65024629 Change-Id: Ic1274d797db27ae1921135a27bab55e475369710 --- .../android/text/StaticLayoutPerfTest.java | 11 +++++ core/java/android/text/StaticLayout.java | 6 +-- core/jni/android_text_StaticLayout.cpp | 44 +++++++++++++++---- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java index 74d136654c62f..57a61ec8218f5 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java @@ -88,4 +88,15 @@ public class StaticLayoutPerfTest { .build(); } } + + @Test + public void testCreateRandom_breakBalanced() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + final CharSequence text = generateRandomParagraph(9); + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .build(); + } + } } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 888bb00b6b582..c124c7fd6a0cc 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -780,9 +780,7 @@ public class StaticLayout extends Layout { firstWidth, firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency, // TODO: Support more justification mode, e.g. letter spacing, stretching. - b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, - (indents != null && indents.length > mLineCount) ? indents : null, - mLineCount); + b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, indents, mLineCount); // measurement has to be done before performing line breaking // but we don't want to recompute fontmetrics or span ranges the @@ -1506,7 +1504,7 @@ public class StaticLayout extends Layout { @FloatRange(from = 0.0f) float restWidth, @Nullable int[] variableTabStops, int defaultTabStop, @BreakStrategy int breakStrategy, @HyphenationFrequency int hyphenationFrequency, boolean isJustified, - @Nullable int[] indents, @IntRange(from = 0) int intentsOffset); + @Nullable int[] indents, @IntRange(from = 0) int indentsOffset); private static native float nAddStyleRun(long nativePtr, long nativePaint, int start, int end, boolean isRtl); diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp index 15ab8f7d661ba..7442fa2d62dcc 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_StaticLayout.cpp @@ -52,17 +52,46 @@ struct JLineBreaksID { static jclass gLineBreaks_class; static JLineBreaksID gLineBreaks_fieldID; +class JNILineBreakerLineWidth : public minikin::LineBreaker::LineWidthDelegate { + public: + JNILineBreakerLineWidth(float firstWidth, int32_t firstLineCount, float restWidth, + std::vector&& indents, int32_t indentsOffset) + : mFirstWidth(firstWidth), mFirstLineCount(firstLineCount), mRestWidth(restWidth), + mIndents(std::move(indents)), mIndentsOffset(indentsOffset) {} + + float getLineWidth(size_t lineNo) override { + const float width = ((ssize_t)lineNo < (ssize_t)mFirstLineCount) + ? mFirstWidth : mRestWidth; + if (mIndents.empty()) { + return width; + } + + const size_t indentIndex = lineNo + mIndentsOffset; + if (indentIndex < mIndents.size()) { + return width - mIndents[indentIndex]; + } else { + return width - mIndents.back(); + } + } + + private: + const float mFirstWidth; + const int32_t mFirstLineCount; + const float mRestWidth; + const std::vector mIndents; + const int32_t mIndentsOffset; +}; + // set text and set a number of parameters for creating a layout (width, tabstops, strategy, // hyphenFrequency) static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length, jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth, jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency, - jboolean isJustified, jintArray indents, jint insetsOffset) { + jboolean isJustified, jintArray indents, jint indentsOffset) { minikin::LineBreaker* b = reinterpret_cast(nativePtr); b->resize(length); env->GetCharArrayRegion(text, 0, length, b->buffer()); b->setText(); - b->setLineWidths(firstWidth, firstWidthLineLimit, restWidth); if (variableTabStops == nullptr) { b->setTabStops(nullptr, 0, defaultTabStop); } else { @@ -73,17 +102,14 @@ static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray tex b->setHyphenationFrequency(static_cast(hyphenFrequency)); b->setJustified(isJustified); + std::vector indentVec; // TODO: copy indents only once when LineBreaker is started to be used. if (indents != nullptr) { - // If indents is not null, it is guaranteed that lineOffset is less than the size of array. ScopedIntArrayRO indentArr(env, indents); - std::vector indentVec( - indentArr.get() + insetsOffset, indentArr.get() + indentArr.size()); - b->setIndents(indentVec); - } else { - b->setIndents(std::vector()); + indentVec.assign(indentArr.get(), indentArr.get() + indentArr.size()); } - + b->setLineWidthDelegate(std::make_unique( + firstWidth, firstWidthLineLimit, restWidth, std::move(indentVec), indentsOffset)); } static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,