Optimize GraphemeClusterSegmentFinder performance
This CL utilized minikin:isGraphemeBreak to compute all grapheme break indices when creating GraphemeClusterSegmentFinder. This makes all methods (such as #nextStartBoundary) of GraphemeClusterSegmentFinder O(1) instead of O(n).
Bug: 271004887
Test: atest TextViewHandwritingGestureTest
Change-Id: I61c1f2c45123a584456b4860b72cd4fdc84c5f1c
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index bcbe706..baf2d8d 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -374,6 +374,7 @@
"jni/text/LineBreaker.cpp",
"jni/text/MeasuredText.cpp",
"jni/text/TextShaper.cpp",
+ "jni/text/GraphemeBreak.cpp",
],
header_libs: [
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index b7a1563..770822a 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -66,6 +66,7 @@
extern int register_android_graphics_text_LineBreaker(JNIEnv* env);
extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
extern int register_android_graphics_text_TextShaper(JNIEnv* env);
+extern int register_android_graphics_text_GraphemeBreak(JNIEnv* env);
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
@@ -125,6 +126,8 @@
{"android.graphics.text.MeasuredText",
REG_JNI(register_android_graphics_text_MeasuredText)},
{"android.graphics.text.TextRunShaper", REG_JNI(register_android_graphics_text_TextShaper)},
+ {"android.graphics.text.GraphemeBreak",
+ REG_JNI(register_android_graphics_text_GraphemeBreak)},
{"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
};
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index c509ed4..09ae7e7 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -77,6 +77,7 @@
extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
extern int register_android_graphics_text_TextShaper(JNIEnv *env);
+extern int register_android_graphics_text_GraphemeBreak(JNIEnv* env);
extern int register_android_graphics_MeshSpecification(JNIEnv* env);
extern int register_android_graphics_Mesh(JNIEnv* env);
@@ -148,6 +149,7 @@
REG_JNI(register_android_graphics_text_MeasuredText),
REG_JNI(register_android_graphics_text_LineBreaker),
REG_JNI(register_android_graphics_text_TextShaper),
+ REG_JNI(register_android_graphics_text_GraphemeBreak),
REG_JNI(register_android_graphics_MeshSpecification),
REG_JNI(register_android_graphics_Mesh),
diff --git a/libs/hwui/jni/text/GraphemeBreak.cpp b/libs/hwui/jni/text/GraphemeBreak.cpp
new file mode 100644
index 0000000..55f03bd
--- /dev/null
+++ b/libs/hwui/jni/text/GraphemeBreak.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "GraphemeBreaker"
+
+#include <minikin/GraphemeBreak.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+
+#include "GraphicsJNI.h"
+
+namespace android {
+
+static void nIsGraphemeBreak(JNIEnv* env, jclass, jfloatArray advances, jcharArray text, jint start,
+ jint end, jbooleanArray isGraphemeBreak) {
+ if (start > end || env->GetArrayLength(advances) < end ||
+ env->GetArrayLength(isGraphemeBreak) < end - start) {
+ doThrowAIOOBE(env);
+ }
+
+ if (start == end) {
+ return;
+ }
+
+ ScopedFloatArrayRO advancesArray(env, advances);
+ ScopedCharArrayRO textArray(env, text);
+ ScopedBooleanArrayRW isGraphemeBreakArray(env, isGraphemeBreak);
+
+ size_t count = end - start;
+ for (size_t offset = 0; offset < count; ++offset) {
+ bool isBreak = minikin::GraphemeBreak::isGraphemeBreak(advancesArray.get(), textArray.get(),
+ start, end, start + offset);
+ isGraphemeBreakArray[offset] = isBreak ? JNI_TRUE : JNI_FALSE;
+ }
+}
+
+static const JNINativeMethod gMethods[] = {
+ {"nIsGraphemeBreak",
+ "("
+ "[F" // advances
+ "[C" // text
+ "I" // start
+ "I" // end
+ "[Z" // isGraphemeBreak
+ ")V",
+ (void*)nIsGraphemeBreak},
+};
+
+int register_android_graphics_text_GraphemeBreak(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/text/GraphemeBreak", gMethods,
+ NELEM(gMethods));
+}
+
+} // namespace android