From 852de827fc6b7d7f36413ee2f36c1fb796755fc5 Mon Sep 17 00:00:00 2001 From: Haoyu Zhang Date: Wed, 27 Jul 2022 16:48:14 -0700 Subject: [PATCH] Optimize TextLine#measureAllOffset performance Bug: 238954768 Bug: 235353864 Test: atest StaticLayoutGetOffsetForHorizontalPerfTest Change-Id: Ic3a1a8fc5ef5d160401d390b920f3f2a409528f0 --- core/java/android/text/TextLine.java | 65 +++-- .../tests/coretests/assets/fonts/ligature.ttf | Bin 0 -> 880 bytes .../tests/coretests/assets/fonts/ligature.ttx | 224 ++++++++++++++++++ .../src/android/text/TextLineTest.java | 41 +++- libs/hwui/jni/Paint.cpp | 4 +- 5 files changed, 307 insertions(+), 27 deletions(-) create mode 100644 core/tests/coretests/assets/fonts/ligature.ttf create mode 100644 core/tests/coretests/assets/fonts/ligature.ttx diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 7394c22ee93fe..e38073a561946 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -553,16 +553,11 @@ public class TextLine { @VisibleForTesting public float[] measureAllOffsets(boolean[] trailing, FontMetricsInt fmi) { float[] measurement = new float[mLen + 1]; - - int[] target = new int[mLen + 1]; - for (int offset = 0; offset < target.length; ++offset) { - target[offset] = trailing[offset] ? offset - 1 : offset; - } - if (target[0] < 0) { + if (trailing[0]) { measurement[0] = 0; } - float h = 0; + float horizontal = 0; for (int runIndex = 0; runIndex < mDirections.getRunCount(); runIndex++) { final int runStart = mDirections.getRunStart(runIndex); if (runStart > mLen) break; @@ -572,27 +567,48 @@ public class TextLine { int segStart = runStart; for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; ++j) { if (j == runLimit || charAt(j) == TAB_CHAR) { - final float oldh = h; - final boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; - final float w = measureRun(segStart, j, j, runIsRtl, fmi, null, 0); - h += advance ? w : -w; + final float oldHorizontal = horizontal; + final boolean sameDirection = + (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; - final float baseh = advance ? oldh : h; - FontMetricsInt crtfmi = advance ? fmi : null; - for (int offset = segStart; offset <= j && offset <= mLen; ++offset) { - if (target[offset] >= segStart && target[offset] < j) { - measurement[offset] = baseh - + measureRun(segStart, offset, j, runIsRtl, crtfmi, null, 0); + // We are using measurement to receive character advance here. So that it + // doesn't need to allocate a new array. + // But be aware that when trailing[segStart] is true, measurement[segStart] + // will be computed in the previous run. And we need to store it first in case + // measureRun overwrites the result. + final float previousSegEndHorizontal = measurement[segStart]; + final float width = + measureRun(segStart, j, j, runIsRtl, fmi, measurement, segStart); + horizontal += sameDirection ? width : -width; + + float currHorizontal = sameDirection ? oldHorizontal : horizontal; + final int segLimit = Math.min(j, mLen); + + for (int offset = segStart; offset <= segLimit; ++offset) { + float advance = 0f; + // When offset == segLimit, advance is meaningless. + if (offset < segLimit) { + advance = runIsRtl ? -measurement[offset] : measurement[offset]; } + + if (offset == segStart && trailing[offset]) { + // If offset == segStart and trailing[segStart] is true, restore the + // value of measurement[segStart] from the previous run. + measurement[offset] = previousSegEndHorizontal; + } else if (offset != segLimit || trailing[offset]) { + measurement[offset] = currHorizontal; + } + + currHorizontal += advance; } if (j != runLimit) { // charAt(j) == TAB_CHAR - if (target[j] == j) { - measurement[j] = h; + if (!trailing[j]) { + measurement[j] = horizontal; } - h = mDir * nextTab(h * mDir); - if (target[j + 1] == j) { - measurement[j + 1] = h; + horizontal = mDir * nextTab(horizontal * mDir); + if (trailing[j + 1]) { + measurement[j + 1] = horizontal; } } @@ -600,10 +616,9 @@ public class TextLine { } } } - if (target[mLen] == mLen) { - measurement[mLen] = h; + if (!trailing[mLen]) { + measurement[mLen] = horizontal; } - return measurement; } diff --git a/core/tests/coretests/assets/fonts/ligature.ttf b/core/tests/coretests/assets/fonts/ligature.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d1e80597662aa6c2fd44c8158e13be7ec00a5d14 GIT binary patch literal 880 zcmZ8fziSjx5dPlo-Q|oSkr>5Bd1&mCC|3lFAR$FWazwdJG?n_iZ1xuS7neOqy3|%y z;y++#D_DqyDC7^Y5d_;H77B^&`pw%_@0?}ceBU?o_GV@e3czU`z{T}j-sZ~Z%U`Yl zuBLkTj(4rqyBNF#)D7}S-PmN9a+k}9qZ>H@h=OzfZh_;ru@8S?L0x|>B7e>k;tLU~&fpMEYkC4QZB7bI3T2B$*2;~o_<)8Rj*LS?Aich^U>QQ|rs z^$c}1ou|$gQ)^{Z571I`QaW^5`uuB~Yxmc&42{s$?BUQovR&93b?nIIG@Vo0X(Wo5 zohr^aPBy_utZmQo$276Hyh-L37ks;N!ScPtWfNiLcA3P()?}5`%NwrRQ?P?EvQn=N z=?zR{j-9F6DQ3^(W`!M0Vxz*fA-#cFJYG$cT<@fP?d2v9yKS?d4)S)G_$z(iJPDIv z!{5z2elUnk-!pL*`Ep~vQsBWLMurGJG**!!L5>btfB{v5{z%QAiuwO)s_Bc@cFQv5 ssL8bBxWxHXWby<}$bS_@VfUb-Ffi20O)KUktEedMq>Q3DvIRcQZ(yi!Z~y=R literal 0 HcmV?d00001 diff --git a/core/tests/coretests/assets/fonts/ligature.ttx b/core/tests/coretests/assets/fonts/ligature.ttx new file mode 100644 index 0000000000000..d7881f42c5d3f --- /dev/null +++ b/core/tests/coretests/assets/fonts/ligature.ttx @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Font for StaticLayoutLineBreakingTest + + + Regular + + + Font for StaticLayoutLineBreakingTest + + + SampleFont-Regular + + + Sample Font + + + Regular + + + Sample Font + + + SampleFont-Regular + + + + + + + + + + + + + + + + diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java index e3bcc8d2c2001..213e2a9f5e9a5 100644 --- a/core/tests/coretests/src/android/text/TextLineTest.java +++ b/core/tests/coretests/src/android/text/TextLineTest.java @@ -30,9 +30,9 @@ import android.text.style.AbsoluteSizeSpan; import android.text.style.ReplacementSpan; import android.text.style.TabStopSpan; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.filters.Suppress; +import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; @@ -98,6 +98,17 @@ public class TextLineTest { InstrumentationRegistry.getInstrumentation().getTargetContext().getAssets(), "fonts/StaticLayoutLineBreakingTestFont.ttf"); + // The test font has following coverage and width. + // U+0020: 1em + // U+0049 (I): 1em + // U+0066 (f): 0.5em + // U+0069 (i): 0.5em + // ligature fi: 2em + // U+10331 (\uD800\uDF31): 10em + private static final Typeface TYPEFACE_LIGATURE = Typeface.createFromAsset( + InstrumentationRegistry.getInstrumentation().getTargetContext().getAssets(), + "fonts/ligature.ttf"); + private TextLine getTextLine(CharSequence str, TextPaint paint, TabStops tabStops) { Layout layout = StaticLayout.Builder.obtain(str, 0, str.length(), paint, Integer.MAX_VALUE) @@ -265,6 +276,34 @@ public class TextLineTest { TextLine tl = getTextLine("I I", paint); assertMeasurements(tl, 3, false, new float[]{0.0f, 10.0f, 120.0f, 130.0f}); + assertMeasurements(tl, 3, true, + new float[]{0.0f, 10.0f, 120.0f, 130.0f}); + } + + @Test + public void testMeasure_surrogate() { + final TextPaint paint = new TextPaint(); + paint.setTypeface(TYPEFACE); + paint.setTextSize(10.0f); // make 1em = 10px + + TextLine tl = getTextLine("I\uD800\uDF31I", paint); + assertMeasurements(tl, 4, false, + new float[]{0.0f, 10.0f, 110.0f, 110.0f, 120.0f}); + assertMeasurements(tl, 4, true, + new float[]{0.0f, 10.0f, 110.0f, 110.0f, 120.0f}); + } + + @Test + public void testMeasure_ligature() { + final TextPaint paint = new TextPaint(); + paint.setTypeface(TYPEFACE_LIGATURE); + paint.setTextSize(10.0f); // make 1em = 10px + + TextLine tl = getTextLine("IfiI", paint); + assertMeasurements(tl, 4, false, + new float[]{0.0f, 10.0f, 20.0f, 30.0f, 40.0f}); + assertMeasurements(tl, 4, true, + new float[]{0.0f, 10.0f, 20.0f, 30.0f, 40.0f}); } @Test diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index ed453b158579c..f0a4bd0f00f0e 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -517,10 +517,12 @@ namespace PaintGlue { MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize, advancesArray.get()); + float result = minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset); if (advances) { + minikin::distributeAdvances(advancesArray.get(), buf, start, count); env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get()); } - return minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset); + return result; } static jfloat getRunAdvance___CIIIIZI_F(JNIEnv *env, jclass, jlong paintHandle, jcharArray text,