Improve BoringLayout#isBoring for PrecomputedText

Bug: 216736786
Test: atest PrecomputedTextTest
Test: perf score becomes 758,071 -> 110,755
Change-Id: I57d3eb9d1e896ed1c847733c4e96ac52dd97d2c4
This commit is contained in:
Seigo Nonaka
2022-02-03 15:20:20 +09:00
parent daa918b287
commit 6b9b5167e1
6 changed files with 117 additions and 14 deletions

View File

@@ -16412,6 +16412,7 @@ package android.graphics.text {
public class MeasuredText {
method public void getBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.Rect);
method @FloatRange(from=0.0f) @Px public float getCharWidthAt(@IntRange(from=0) int);
method public void getFontMetricsInt(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.Paint.FontMetricsInt);
method @FloatRange(from=0.0) @Px public float getWidth(@IntRange(from=0) int, @IntRange(from=0) int);
}
@@ -44939,6 +44940,7 @@ package android.text {
method public char charAt(int);
method public static android.text.PrecomputedText create(@NonNull CharSequence, @NonNull android.text.PrecomputedText.Params);
method public void getBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.Rect);
method public void getFontMetricsInt(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.Paint.FontMetricsInt);
method @IntRange(from=0) public int getParagraphCount();
method @IntRange(from=0) public int getParagraphEnd(@IntRange(from=0) int);
method @IntRange(from=0) public int getParagraphStart(@IntRange(from=0) int);

View File

@@ -286,6 +286,16 @@ public class MeasuredParagraph {
mMeasuredText.getBounds(start, end, bounds);
}
/**
* Retrieves the font metrics for the given range.
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
*/
public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Paint.FontMetricsInt fmi) {
mMeasuredText.getFontMetricsInt(start, end, fmi);
}
/**
* Returns a width of the character at the offset.
*

View File

@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.text.LineBreakConfig;
import android.graphics.text.MeasuredText;
@@ -696,6 +697,38 @@ public class PrecomputedText implements Spannable {
getMeasuredParagraph(paraIndex).getBounds(start - paraStart, end - paraStart, bounds);
}
/**
* Retrieves the text font metrics for the given range.
* Both {@code start} and {@code end} offset need to be in the same paragraph, otherwise
* IllegalArgumentException will be thrown.
*
* @param start the inclusive start offset in the text
* @param end the exclusive end offset in the text
* @param outMetrics the output font metrics
* @throws IllegalArgumentException if start and end offset are in the different paragraph.
*/
public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Paint.FontMetricsInt outMetrics) {
Preconditions.checkArgument(0 <= start && start <= mText.length(), "invalid start offset");
Preconditions.checkArgument(0 <= end && end <= mText.length(), "invalid end offset");
Preconditions.checkArgument(start <= end, "start offset can not be larger than end offset");
Objects.requireNonNull(outMetrics);
if (start == end) {
mParams.getTextPaint().getFontMetricsInt(outMetrics);
return;
}
final int paraIndex = findParaIndex(start);
final int paraStart = getParagraphStart(paraIndex);
final int paraEnd = getParagraphEnd(paraIndex);
if (start < paraStart || paraEnd < end) {
throw new IllegalArgumentException("Cannot measured across the paragraph:"
+ "para: (" + paraStart + ", " + paraEnd + "), "
+ "request: (" + start + ", " + end + ")");
}
getMeasuredParagraph(paraIndex).getFontMetricsInt(start - paraStart,
end - paraStart, outMetrics);
}
/**
* Returns a width of a character at offset
*

View File

@@ -866,8 +866,12 @@ public class TextLine {
wp.getFontMetricsInt(mChars, start, count, contextStart, contextCount, runIsRtl,
fmi);
} else {
wp.getFontMetricsInt(mText, mStart + start, count, mStart + contextStart, contextCount,
runIsRtl, fmi);
if (mComputed == null) {
wp.getFontMetricsInt(mText, mStart + start, count, mStart + contextStart,
contextCount, runIsRtl, fmi);
} else {
mComputed.getFontMetricsInt(mStart + start, mStart + end, fmi);
}
}
updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,

View File

@@ -34,6 +34,7 @@ import libcore.util.NativeAllocationRegistry;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* Result of text shaping of the single paragraph string.
@@ -56,18 +57,22 @@ import java.lang.annotation.RetentionPolicy;
public class MeasuredText {
private static final String TAG = "MeasuredText";
private long mNativePtr;
private boolean mComputeHyphenation;
private boolean mComputeLayout;
private @NonNull char[] mChars;
private final long mNativePtr;
private final boolean mComputeHyphenation;
private final boolean mComputeLayout;
@NonNull private final char[] mChars;
private final int mTop;
private final int mBottom;
// Use builder instead.
private MeasuredText(long ptr, @NonNull char[] chars, boolean computeHyphenation,
boolean computeLayout) {
boolean computeLayout, int top, int bottom) {
mNativePtr = ptr;
mChars = chars;
mComputeHyphenation = computeHyphenation;
mComputeLayout = computeLayout;
mTop = top;
mBottom = bottom;
}
/**
@@ -123,6 +128,30 @@ public class MeasuredText {
nGetBounds(mNativePtr, mChars, start, end, rect);
}
/**
* Retrieves the font metrics of the given range
*
* @param start an inclusive start index of the range
* @param end an exclusive end index of the range
* @param outMetrics an output metrics object
*/
public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Paint.FontMetricsInt outMetrics) {
Preconditions.checkArgument(0 <= start && start <= mChars.length,
"start(%d) must be 0 <= start <= %d", start, mChars.length);
Preconditions.checkArgument(0 <= end && end <= mChars.length,
"end(%d) must be 0 <= end <= %d", end, mChars.length);
Preconditions.checkArgument(start <= end,
"start(%d) is larger than end(%d)", start, end);
Objects.requireNonNull(outMetrics);
long packed = nGetExtent(mNativePtr, mChars, start, end);
outMetrics.ascent = (int) (packed >> 32);
outMetrics.descent = (int) (packed & 0xFFFFFFFF);
outMetrics.top = Math.min(outMetrics.ascent, mTop);
outMetrics.bottom = Math.max(outMetrics.descent, mBottom);
}
/**
* Returns the width of the character at the given offset.
*
@@ -160,6 +189,8 @@ public class MeasuredText {
@CriticalNative
private static native float nGetCharWidthAt(long nativePtr, int offset);
private static native long nGetExtent(long nativePtr, char[] buf, int start, int end);
/**
* Helper class for creating a {@link MeasuredText}.
* <p>
@@ -189,6 +220,9 @@ public class MeasuredText {
private boolean mFastHyphenation = false;
private int mCurrentOffset = 0;
private @Nullable MeasuredText mHintMt = null;
private int mTop = 0;
private int mBottom = 0;
private Paint.FontMetricsInt mCachedMetrics = new Paint.FontMetricsInt();
/**
* Construct a builder.
@@ -269,6 +303,10 @@ public class MeasuredText {
nAddStyleRun(mNativePtr, paint.getNativeInstance(), lbStyle, lbWordStyle,
mCurrentOffset, end, isRtl);
mCurrentOffset = end;
paint.getFontMetricsInt(mCachedMetrics);
mTop = Math.min(mTop, mCachedMetrics.top);
mBottom = Math.max(mBottom, mCachedMetrics.bottom);
return this;
}
@@ -419,7 +457,7 @@ public class MeasuredText {
long ptr = nBuildMeasuredText(mNativePtr, hintPtr, mText, mComputeHyphenation,
mComputeLayout, mFastHyphenation);
final MeasuredText res = new MeasuredText(ptr, mText, mComputeHyphenation,
mComputeLayout);
mComputeLayout, mTop, mBottom);
sRegistry.registerNativeAllocation(res, ptr);
return res;
} finally {

View File

@@ -134,6 +134,21 @@ static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jin
GraphicsJNI::irect_to_jrect(ir, env, bounds);
}
// Regular JNI
static jlong nGetExtent(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jint start,
jint end) {
ScopedCharArrayRO text(env, javaText);
const minikin::U16StringPiece textBuffer(text.get(), text.size());
const minikin::Range range(start, end);
minikin::MinikinExtent extent = toMeasuredParagraph(ptr)->getExtent(textBuffer, range);
int32_t ascent = SkScalarRoundToInt(extent.ascent);
int32_t descent = SkScalarRoundToInt(extent.descent);
return (((jlong)(ascent)) << 32) | ((jlong)descent);
}
// CriticalNative
static jlong nGetReleaseFunc(CRITICAL_JNI_PARAMS) {
return toJLong(&releaseMeasuredParagraph);
@@ -153,12 +168,13 @@ static const JNINativeMethod gMTBuilderMethods[] = {
};
static const JNINativeMethod gMTMethods[] = {
// MeasuredParagraph native functions.
{"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives
{"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*) nGetBounds}, // Regular JNI
{"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives
{"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage}, // Critical Native
{"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt}, // Critical Native
// MeasuredParagraph native functions.
{"nGetWidth", "(JII)F", (void*)nGetWidth}, // Critical Natives
{"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*)nGetBounds}, // Regular JNI
{"nGetExtent", "(J[CII)J", (void*)nGetExtent}, // Regular JNI
{"nGetReleaseFunc", "()J", (void*)nGetReleaseFunc}, // Critical Natives
{"nGetMemoryUsage", "(J)I", (void*)nGetMemoryUsage}, // Critical Native
{"nGetCharWidthAt", "(JI)F", (void*)nGetCharWidthAt}, // Critical Native
};
int register_android_graphics_text_MeasuredText(JNIEnv* env) {