Optimize TextLine#measureAllOffset performance
Bug: 238954768 Bug: 235353864 Test: atest StaticLayoutGetOffsetForHorizontalPerfTest Change-Id: Ic3a1a8fc5ef5d160401d390b920f3f2a409528f0
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
BIN
core/tests/coretests/assets/fonts/ligature.ttf
Normal file
BIN
core/tests/coretests/assets/fonts/ligature.ttf
Normal file
Binary file not shown.
224
core/tests/coretests/assets/fonts/ligature.ttx
Normal file
224
core/tests/coretests/assets/fonts/ligature.ttx
Normal file
@@ -0,0 +1,224 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
|
||||
|
||||
<GlyphOrder>
|
||||
<GlyphID id="0" name=".notdef"/>
|
||||
<GlyphID id="1" name="1em"/>
|
||||
<GlyphID id="2" name="f"/>
|
||||
<GlyphID id="3" name="i"/>
|
||||
<GlyphID id="4" name="fi"/>
|
||||
</GlyphOrder>
|
||||
|
||||
<head>
|
||||
<tableVersion value="1.0"/>
|
||||
<fontRevision value="1.0"/>
|
||||
<checkSumAdjustment value="0x640cdb2f"/>
|
||||
<magicNumber value="0x5f0f3cf5"/>
|
||||
<flags value="00000000 00000011"/>
|
||||
<unitsPerEm value="100"/>
|
||||
<created value="Fri Mar 17 07:26:00 2017"/>
|
||||
<macStyle value="00000000 00000000"/>
|
||||
<lowestRecPPEM value="7"/>
|
||||
<fontDirectionHint value="2"/>
|
||||
<glyphDataFormat value="0"/>
|
||||
</head>
|
||||
|
||||
<hhea>
|
||||
<tableVersion value="0x00010000"/>
|
||||
<ascent value="1000"/>
|
||||
<descent value="-200"/>
|
||||
<lineGap value="0"/>
|
||||
<caretSlopeRise value="1"/>
|
||||
<caretSlopeRun value="0"/>
|
||||
<caretOffset value="0"/>
|
||||
<reserved0 value="0"/>
|
||||
<reserved1 value="0"/>
|
||||
<reserved2 value="0"/>
|
||||
<reserved3 value="0"/>
|
||||
<metricDataFormat value="0"/>
|
||||
</hhea>
|
||||
|
||||
<maxp>
|
||||
<tableVersion value="0x10000"/>
|
||||
<maxZones value="0"/>
|
||||
<maxTwilightPoints value="0"/>
|
||||
<maxStorage value="0"/>
|
||||
<maxFunctionDefs value="0"/>
|
||||
<maxInstructionDefs value="0"/>
|
||||
<maxStackElements value="0"/>
|
||||
<maxSizeOfInstructions value="0"/>
|
||||
<maxComponentElements value="0"/>
|
||||
</maxp>
|
||||
|
||||
<OS_2>
|
||||
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
|
||||
will be recalculated by the compiler -->
|
||||
<version value="3"/>
|
||||
<xAvgCharWidth value="594"/>
|
||||
<usWeightClass value="400"/>
|
||||
<usWidthClass value="5"/>
|
||||
<fsType value="00000000 00001000"/>
|
||||
<ySubscriptXSize value="650"/>
|
||||
<ySubscriptYSize value="600"/>
|
||||
<ySubscriptXOffset value="0"/>
|
||||
<ySubscriptYOffset value="75"/>
|
||||
<ySuperscriptXSize value="650"/>
|
||||
<ySuperscriptYSize value="600"/>
|
||||
<ySuperscriptXOffset value="0"/>
|
||||
<ySuperscriptYOffset value="350"/>
|
||||
<yStrikeoutSize value="50"/>
|
||||
<yStrikeoutPosition value="300"/>
|
||||
<sFamilyClass value="0"/>
|
||||
<panose>
|
||||
<bFamilyType value="0"/>
|
||||
<bSerifStyle value="0"/>
|
||||
<bWeight value="5"/>
|
||||
<bProportion value="0"/>
|
||||
<bContrast value="0"/>
|
||||
<bStrokeVariation value="0"/>
|
||||
<bArmStyle value="0"/>
|
||||
<bLetterForm value="0"/>
|
||||
<bMidline value="0"/>
|
||||
<bXHeight value="0"/>
|
||||
</panose>
|
||||
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
|
||||
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
|
||||
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
|
||||
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
|
||||
<achVendID value="UKWN"/>
|
||||
<fsSelection value="00000000 01000000"/>
|
||||
<usFirstCharIndex value="32"/>
|
||||
<usLastCharIndex value="122"/>
|
||||
<sTypoAscender value="800"/>
|
||||
<sTypoDescender value="-200"/>
|
||||
<sTypoLineGap value="200"/>
|
||||
<usWinAscent value="1000"/>
|
||||
<usWinDescent value="200"/>
|
||||
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
|
||||
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
|
||||
<sxHeight value="500"/>
|
||||
<sCapHeight value="700"/>
|
||||
<usDefaultChar value="0"/>
|
||||
<usBreakChar value="32"/>
|
||||
<usMaxContext value="0"/>
|
||||
</OS_2>
|
||||
|
||||
<GSUB>
|
||||
<Version value="0x00010000"/>
|
||||
<ScriptList>
|
||||
<ScriptRecord index="0">
|
||||
<ScriptTag value="latn"/>
|
||||
<Script>
|
||||
<DefaultLangSys>
|
||||
<ReqFeatureIndex value="65535"/>
|
||||
<FeatureIndex index="0" value="0"/>
|
||||
</DefaultLangSys>
|
||||
</Script>
|
||||
</ScriptRecord>
|
||||
</ScriptList>
|
||||
<FeatureList>
|
||||
<FeatureRecord index="0">
|
||||
<FeatureTag value="liga"/>
|
||||
<Feature>
|
||||
<LookupListIndex index="0" value="0"/>
|
||||
</Feature>
|
||||
</FeatureRecord>
|
||||
</FeatureList>
|
||||
<LookupList>
|
||||
<Lookup index="0">
|
||||
<LookupType value="4"/>
|
||||
<LookupFlag value="0"/>
|
||||
<LigatureSubst index="0">
|
||||
<LigatureSet glyph="f">
|
||||
<Ligature components="i" glyph="fi"/>
|
||||
</LigatureSet>
|
||||
</LigatureSubst>
|
||||
</Lookup>
|
||||
</LookupList>
|
||||
</GSUB>
|
||||
|
||||
<hmtx>
|
||||
<mtx name=".notdef" width="50" lsb="0"/>
|
||||
<mtx name="1em" width="100" lsb="0"/>
|
||||
<mtx name="f" width="50" lsb="0"/>
|
||||
<mtx name="i" width="50" lsb="0"/>
|
||||
<mtx name="fi" width="200" lsb="0"/>
|
||||
</hmtx>
|
||||
|
||||
<cmap>
|
||||
<tableVersion version="0"/>
|
||||
<cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0">
|
||||
<map code="0x0020" name="1em" />
|
||||
<map code="0x002e" name="1em" /> <!-- . -->
|
||||
<map code="0x0049" name="1em" /> <!-- I -->
|
||||
<map code="0x0066" name="f" /> <!-- f -->
|
||||
<map code="0x0069" name="i" /> <!-- i -->
|
||||
</cmap_format_12>
|
||||
</cmap>
|
||||
|
||||
<loca>
|
||||
<!-- The 'loca' table will be calculated by the compiler -->
|
||||
</loca>
|
||||
|
||||
<glyf>
|
||||
<TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
|
||||
<TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
|
||||
<TTGlyph name="f" xMin="0" yMin="0" xMax="0" yMax="0" />
|
||||
<TTGlyph name="i" xMin="0" yMin="0" xMax="0" yMax="0" />
|
||||
<TTGlyph name="fi" xMin="0" yMin="0" xMax="0" yMax="0" />
|
||||
</glyf>
|
||||
|
||||
<name>
|
||||
<namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||
Font for StaticLayoutLineBreakingTest
|
||||
</namerecord>
|
||||
<namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||
Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||
Font for StaticLayoutLineBreakingTest
|
||||
</namerecord>
|
||||
<namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||
SampleFont-Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
|
||||
Sample Font
|
||||
</namerecord>
|
||||
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
|
||||
Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
|
||||
Sample Font
|
||||
</namerecord>
|
||||
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
|
||||
SampleFont-Regular
|
||||
</namerecord>
|
||||
</name>
|
||||
|
||||
<post>
|
||||
<formatType value="3.0"/>
|
||||
<italicAngle value="0.0"/>
|
||||
<underlinePosition value="-75"/>
|
||||
<underlineThickness value="50"/>
|
||||
<isFixedPitch value="0"/>
|
||||
<minMemType42 value="0"/>
|
||||
<maxMemType42 value="0"/>
|
||||
<minMemType1 value="0"/>
|
||||
<maxMemType1 value="0"/>
|
||||
</post>
|
||||
|
||||
</ttFont>
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user