Improve BoringLayout#isBoring for PrecomputedText
Bug: 216736786
Test: atest PrecomputedTextTest
Test: perf score becomes 758,071 -> 110,755
Change-Id: I57d3eb9d1e896ed1c847733c4e96ac52dd97d2c4
diff --git a/core/api/current.txt b/core/api/current.txt
index 11e0a6b..3b3462c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -16412,6 +16412,7 @@
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 @@
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);
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 748d551..bedd409 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -287,6 +287,16 @@
}
/**
+ * 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.
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index be66db2..9307e56 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -21,6 +21,7 @@
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;
@@ -697,6 +698,38 @@
}
/**
+ * 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
*
* @param offset an offset of the text.
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 49e2111..e39231c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -866,8 +866,12 @@
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,
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 6d691c1..3f7f088 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -34,6 +34,7 @@
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 @@
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;
}
/**
@@ -124,6 +129,30 @@
}
/**
+ * 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.
*
* @param offset an offset of the character.
@@ -160,6 +189,8 @@
@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 @@
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 @@
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 @@
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 {
diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp
index 76ea2d5..c13c800 100644
--- a/libs/hwui/jni/text/MeasuredText.cpp
+++ b/libs/hwui/jni/text/MeasuredText.cpp
@@ -134,6 +134,21 @@
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 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) {