Loading core/java/android/text/StaticLayout.java +36 −4 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; import java.util.Arrays; import java.util.Locale; /** * StaticLayout is a Layout for text that will not be edited after it Loading @@ -51,6 +52,10 @@ public class StaticLayout extends Layout { * @hide */ public final static class Builder { private Builder() { mNativePtr = nNewBuilder(); } static Builder obtain() { Builder b = null; synchronized (sLock) { Loading Loading @@ -96,6 +101,7 @@ public class StaticLayout extends Layout { // release any expensive state /* package */ void finish() { nFinishBuilder(mNativePtr); mMeasuredText.finish(); } Loading Loading @@ -160,6 +166,14 @@ public class StaticLayout extends Layout { return this; } /* @hide */ public void setLocale(Locale locale) { if (!locale.equals(mLocale)) { nBuilderSetLocale(mNativePtr, locale.toLanguageTag()); mLocale = locale; } } public StaticLayout build() { // TODO: can optimize based on whether ellipsis is needed StaticLayout result = new StaticLayout(mText); Loading @@ -168,6 +182,17 @@ public class StaticLayout extends Layout { return result; } @Override protected void finalize() throws Throwable { try { nFreeBuilder(mNativePtr); } finally { super.finalize(); } } /* package */ long mNativePtr; CharSequence mText; int mStart; int mEnd; Loading @@ -186,6 +211,8 @@ public class StaticLayout extends Layout { // This will go away and be subsumed by native builder code MeasuredText mMeasuredText; Locale mLocale; private static final Object sLock = new Object(); private static final Builder[] sCached = new Builder[3]; } Loading Loading @@ -322,13 +349,13 @@ public class StaticLayout extends Layout { float spacingadd = b.mSpacingAdd; float ellipsizedWidth = b.mEllipsizedWidth; TextUtils.TruncateAt ellipsize = b.mEllipsize; LineBreaks lineBreaks = new LineBreaks(); LineBreaks lineBreaks = new LineBreaks(); // TODO: move to builder to avoid allocation costs // store span end locations int[] spanEndCache = new int[4]; // store fontMetrics per span range // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range) int[] fmCache = new int[4 * 4]; final String localeLanguageTag = paint.getTextLocale().toLanguageTag(); b.setLocale(paint.getTextLocale()); // TODO: also respect LocaleSpan within the text mLineCount = 0; Loading Loading @@ -466,7 +493,7 @@ public class StaticLayout extends Layout { } } int breakCount = nComputeLineBreaks(localeLanguageTag, chs, widths, paraEnd - paraStart, firstWidth, int breakCount = nComputeLineBreaks(b.mNativePtr, chs, widths, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, false, lineBreaks, lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length); Loading Loading @@ -911,11 +938,16 @@ public class StaticLayout extends Layout { // the arrays inside the LineBreaks objects are passed in as well // to reduce the number of JNI calls in the common case where the // arrays do not have to be resized private static native int nComputeLineBreaks(String locale, char[] text, float[] widths, private static native int nComputeLineBreaks(long nativePtr, char[] text, float[] widths, int length, float firstWidth, int firstWidthLineCount, float restWidth, int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle, int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength); private static native long nNewBuilder(); private static native void nFreeBuilder(long nativePtr); private static native void nFinishBuilder(long nativePtr); private static native void nBuilderSetLocale(long nativePtr, String locale); private int mLineCount; private int mTopPadding, mBottomPadding; private int mColumns; Loading core/jni/android_text_StaticLayout.cpp +91 −51 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ #include <list> #include <algorithm> namespace android { struct JLineBreaksID { Loading @@ -40,6 +41,53 @@ struct JLineBreaksID { static jclass gLineBreaks_class; static JLineBreaksID gLineBreaks_fieldID; class Builder { public: ~Builder() { utext_close(&mUText); delete mBreakIterator; } void setLocale(const Locale& locale) { delete mBreakIterator; UErrorCode status = U_ZERO_ERROR; mBreakIterator = BreakIterator::createLineInstance(locale, status); // TODO: check status } void resize(size_t size) { mTextBuf.resize(size); } uint16_t* buffer() { return mTextBuf.data(); } // set text to current contents of buffer void setText() { UErrorCode status = U_ZERO_ERROR; utext_openUChars(&mUText, mTextBuf.data(), mTextBuf.size(), &status); mBreakIterator->setText(&mUText, status); } void finish() { if (mTextBuf.size() > MAX_TEXT_BUF_RETAIN) { mTextBuf.clear(); mTextBuf.shrink_to_fit(); } } BreakIterator* breakIterator() const { return mBreakIterator; } private: const size_t MAX_TEXT_BUF_RETAIN = 32678; BreakIterator* mBreakIterator = nullptr; UText mUText = UTEXT_INITIALIZER; std::vector<uint16_t>mTextBuf; }; static const int CHAR_SPACE = 0x20; static const int CHAR_TAB = 0x09; static const int CHAR_NEWLINE = 0x0a; Loading @@ -50,7 +98,7 @@ class TabStops { // specified stops must be a sorted array (allowed to be null) TabStops(JNIEnv* env, jintArray stops, jint defaultTabWidth) : mStops(env), mTabWidth(defaultTabWidth) { if (stops != NULL) { if (stops != nullptr) { mStops.reset(stops); mNumStops = mStops.size(); } else { Loading Loading @@ -430,37 +478,6 @@ class GreedyLineBreaker : public LineBreaker { } }; class ScopedBreakIterator { public: ScopedBreakIterator(JNIEnv* env, BreakIterator* breakIterator, const jchar* inputText, jint length) : mBreakIterator(breakIterator), mChars(inputText) { UErrorCode status = U_ZERO_ERROR; mUText = utext_openUChars(NULL, mChars, length, &status); if (mUText == NULL) { return; } mBreakIterator->setText(mUText, status); } inline BreakIterator* operator->() { return mBreakIterator; } ~ScopedBreakIterator() { utext_close(mUText); delete mBreakIterator; } private: BreakIterator* mBreakIterator; const jchar* mChars; UText* mUText; // disable copying and assignment ScopedBreakIterator(const ScopedBreakIterator&); void operator=(const ScopedBreakIterator&); }; static jint recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, jfloatArray recycleWidths, jbooleanArray recycleFlags, jint recycleLength, const std::vector<jint>& breaks, Loading Loading @@ -526,7 +543,7 @@ void computePrimitives(const jchar* textArr, const jfloat* widthsArr, jint lengt primitives->push_back(p); } static jint nComputeLineBreaks(JNIEnv* env, jclass, jstring javaLocaleName, static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, jcharArray inputText, jfloatArray widths, jint length, jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth, jintArray variableTabStops, jint defaultTabStop, jboolean optimize, Loading @@ -535,29 +552,24 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jstring javaLocaleName, jint recycleLength) { std::vector<int> breaks; ScopedCharArrayRO textScopedArr(env, inputText); Builder* b = reinterpret_cast<Builder*>(nativePtr); b->resize(length); env->GetCharArrayRegion(inputText, 0, length, b->buffer()); b->setText(); // TODO: this array access is pretty inefficient, but we'll replace it anyway ScopedFloatArrayRO widthsScopedArr(env, widths); ScopedIcuLocale icuLocale(env, javaLocaleName); if (icuLocale.valid()) { UErrorCode status = U_ZERO_ERROR; BreakIterator* it = BreakIterator::createLineInstance(icuLocale.locale(), status); if (!U_SUCCESS(status) || it == NULL) { if (it) { delete it; } } else { ScopedBreakIterator breakIterator(env, it, textScopedArr.get(), length); BreakIterator* breakIterator = b->breakIterator(); int loc = breakIterator->first(); while ((loc = breakIterator->next()) != BreakIterator::DONE) { breaks.push_back(loc); } } } // TODO: all these allocations can be moved into the builder std::vector<Primitive> primitives; TabStops tabStops(env, variableTabStops, defaultTabStop); computePrimitives(textScopedArr.get(), widthsScopedArr.get(), length, breaks, tabStops, &primitives); computePrimitives(b->buffer(), widthsScopedArr.get(), length, breaks, tabStops, &primitives); LineWidth lineWidth(firstWidth, firstWidthLineLimit, restWidth); std::vector<int> computedBreaks; Loading @@ -571,13 +583,41 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jstring javaLocaleName, GreedyLineBreaker breaker(primitives, lineWidth); breaker.computeBreaks(&computedBreaks, &computedWidths, &computedFlags); } b->finish(); return recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleFlags, recycleLength, computedBreaks, computedWidths, computedFlags); } static jlong nNewBuilder(JNIEnv*, jclass) { return reinterpret_cast<jlong>(new Builder); } static void nFreeBuilder(JNIEnv*, jclass, jlong nativePtr) { delete reinterpret_cast<Builder*>(nativePtr); } static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) { Builder* b = reinterpret_cast<Builder*>(nativePtr); b->finish(); } static void nBuilderSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName) { ScopedIcuLocale icuLocale(env, javaLocaleName); Builder* b = reinterpret_cast<Builder*>(nativePtr); if (icuLocale.valid()) { b->setLocale(icuLocale.locale()); } } static JNINativeMethod gMethods[] = { {"nComputeLineBreaks", "(Ljava/lang/String;[C[FIFIF[IIZLandroid/text/StaticLayout$LineBreaks;[I[F[ZI)I", (void*) nComputeLineBreaks} {"nNewBuilder", "()J", (void*) nNewBuilder}, {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, {"nFinishBuilder", "(J)V", (void*) nFinishBuilder}, {"nBuilderSetLocale", "(JLjava/lang/String;)V", (void*) nBuilderSetLocale}, {"nComputeLineBreaks", "(J[C[FIFIF[IIZLandroid/text/StaticLayout$LineBreaks;[I[F[ZI)I", (void*) nComputeLineBreaks} }; int register_android_text_StaticLayout(JNIEnv* env) Loading Loading
core/java/android/text/StaticLayout.java +36 −4 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; import java.util.Arrays; import java.util.Locale; /** * StaticLayout is a Layout for text that will not be edited after it Loading @@ -51,6 +52,10 @@ public class StaticLayout extends Layout { * @hide */ public final static class Builder { private Builder() { mNativePtr = nNewBuilder(); } static Builder obtain() { Builder b = null; synchronized (sLock) { Loading Loading @@ -96,6 +101,7 @@ public class StaticLayout extends Layout { // release any expensive state /* package */ void finish() { nFinishBuilder(mNativePtr); mMeasuredText.finish(); } Loading Loading @@ -160,6 +166,14 @@ public class StaticLayout extends Layout { return this; } /* @hide */ public void setLocale(Locale locale) { if (!locale.equals(mLocale)) { nBuilderSetLocale(mNativePtr, locale.toLanguageTag()); mLocale = locale; } } public StaticLayout build() { // TODO: can optimize based on whether ellipsis is needed StaticLayout result = new StaticLayout(mText); Loading @@ -168,6 +182,17 @@ public class StaticLayout extends Layout { return result; } @Override protected void finalize() throws Throwable { try { nFreeBuilder(mNativePtr); } finally { super.finalize(); } } /* package */ long mNativePtr; CharSequence mText; int mStart; int mEnd; Loading @@ -186,6 +211,8 @@ public class StaticLayout extends Layout { // This will go away and be subsumed by native builder code MeasuredText mMeasuredText; Locale mLocale; private static final Object sLock = new Object(); private static final Builder[] sCached = new Builder[3]; } Loading Loading @@ -322,13 +349,13 @@ public class StaticLayout extends Layout { float spacingadd = b.mSpacingAdd; float ellipsizedWidth = b.mEllipsizedWidth; TextUtils.TruncateAt ellipsize = b.mEllipsize; LineBreaks lineBreaks = new LineBreaks(); LineBreaks lineBreaks = new LineBreaks(); // TODO: move to builder to avoid allocation costs // store span end locations int[] spanEndCache = new int[4]; // store fontMetrics per span range // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range) int[] fmCache = new int[4 * 4]; final String localeLanguageTag = paint.getTextLocale().toLanguageTag(); b.setLocale(paint.getTextLocale()); // TODO: also respect LocaleSpan within the text mLineCount = 0; Loading Loading @@ -466,7 +493,7 @@ public class StaticLayout extends Layout { } } int breakCount = nComputeLineBreaks(localeLanguageTag, chs, widths, paraEnd - paraStart, firstWidth, int breakCount = nComputeLineBreaks(b.mNativePtr, chs, widths, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, false, lineBreaks, lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length); Loading Loading @@ -911,11 +938,16 @@ public class StaticLayout extends Layout { // the arrays inside the LineBreaks objects are passed in as well // to reduce the number of JNI calls in the common case where the // arrays do not have to be resized private static native int nComputeLineBreaks(String locale, char[] text, float[] widths, private static native int nComputeLineBreaks(long nativePtr, char[] text, float[] widths, int length, float firstWidth, int firstWidthLineCount, float restWidth, int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle, int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength); private static native long nNewBuilder(); private static native void nFreeBuilder(long nativePtr); private static native void nFinishBuilder(long nativePtr); private static native void nBuilderSetLocale(long nativePtr, String locale); private int mLineCount; private int mTopPadding, mBottomPadding; private int mColumns; Loading
core/jni/android_text_StaticLayout.cpp +91 −51 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ #include <list> #include <algorithm> namespace android { struct JLineBreaksID { Loading @@ -40,6 +41,53 @@ struct JLineBreaksID { static jclass gLineBreaks_class; static JLineBreaksID gLineBreaks_fieldID; class Builder { public: ~Builder() { utext_close(&mUText); delete mBreakIterator; } void setLocale(const Locale& locale) { delete mBreakIterator; UErrorCode status = U_ZERO_ERROR; mBreakIterator = BreakIterator::createLineInstance(locale, status); // TODO: check status } void resize(size_t size) { mTextBuf.resize(size); } uint16_t* buffer() { return mTextBuf.data(); } // set text to current contents of buffer void setText() { UErrorCode status = U_ZERO_ERROR; utext_openUChars(&mUText, mTextBuf.data(), mTextBuf.size(), &status); mBreakIterator->setText(&mUText, status); } void finish() { if (mTextBuf.size() > MAX_TEXT_BUF_RETAIN) { mTextBuf.clear(); mTextBuf.shrink_to_fit(); } } BreakIterator* breakIterator() const { return mBreakIterator; } private: const size_t MAX_TEXT_BUF_RETAIN = 32678; BreakIterator* mBreakIterator = nullptr; UText mUText = UTEXT_INITIALIZER; std::vector<uint16_t>mTextBuf; }; static const int CHAR_SPACE = 0x20; static const int CHAR_TAB = 0x09; static const int CHAR_NEWLINE = 0x0a; Loading @@ -50,7 +98,7 @@ class TabStops { // specified stops must be a sorted array (allowed to be null) TabStops(JNIEnv* env, jintArray stops, jint defaultTabWidth) : mStops(env), mTabWidth(defaultTabWidth) { if (stops != NULL) { if (stops != nullptr) { mStops.reset(stops); mNumStops = mStops.size(); } else { Loading Loading @@ -430,37 +478,6 @@ class GreedyLineBreaker : public LineBreaker { } }; class ScopedBreakIterator { public: ScopedBreakIterator(JNIEnv* env, BreakIterator* breakIterator, const jchar* inputText, jint length) : mBreakIterator(breakIterator), mChars(inputText) { UErrorCode status = U_ZERO_ERROR; mUText = utext_openUChars(NULL, mChars, length, &status); if (mUText == NULL) { return; } mBreakIterator->setText(mUText, status); } inline BreakIterator* operator->() { return mBreakIterator; } ~ScopedBreakIterator() { utext_close(mUText); delete mBreakIterator; } private: BreakIterator* mBreakIterator; const jchar* mChars; UText* mUText; // disable copying and assignment ScopedBreakIterator(const ScopedBreakIterator&); void operator=(const ScopedBreakIterator&); }; static jint recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, jfloatArray recycleWidths, jbooleanArray recycleFlags, jint recycleLength, const std::vector<jint>& breaks, Loading Loading @@ -526,7 +543,7 @@ void computePrimitives(const jchar* textArr, const jfloat* widthsArr, jint lengt primitives->push_back(p); } static jint nComputeLineBreaks(JNIEnv* env, jclass, jstring javaLocaleName, static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, jcharArray inputText, jfloatArray widths, jint length, jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth, jintArray variableTabStops, jint defaultTabStop, jboolean optimize, Loading @@ -535,29 +552,24 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jstring javaLocaleName, jint recycleLength) { std::vector<int> breaks; ScopedCharArrayRO textScopedArr(env, inputText); Builder* b = reinterpret_cast<Builder*>(nativePtr); b->resize(length); env->GetCharArrayRegion(inputText, 0, length, b->buffer()); b->setText(); // TODO: this array access is pretty inefficient, but we'll replace it anyway ScopedFloatArrayRO widthsScopedArr(env, widths); ScopedIcuLocale icuLocale(env, javaLocaleName); if (icuLocale.valid()) { UErrorCode status = U_ZERO_ERROR; BreakIterator* it = BreakIterator::createLineInstance(icuLocale.locale(), status); if (!U_SUCCESS(status) || it == NULL) { if (it) { delete it; } } else { ScopedBreakIterator breakIterator(env, it, textScopedArr.get(), length); BreakIterator* breakIterator = b->breakIterator(); int loc = breakIterator->first(); while ((loc = breakIterator->next()) != BreakIterator::DONE) { breaks.push_back(loc); } } } // TODO: all these allocations can be moved into the builder std::vector<Primitive> primitives; TabStops tabStops(env, variableTabStops, defaultTabStop); computePrimitives(textScopedArr.get(), widthsScopedArr.get(), length, breaks, tabStops, &primitives); computePrimitives(b->buffer(), widthsScopedArr.get(), length, breaks, tabStops, &primitives); LineWidth lineWidth(firstWidth, firstWidthLineLimit, restWidth); std::vector<int> computedBreaks; Loading @@ -571,13 +583,41 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jstring javaLocaleName, GreedyLineBreaker breaker(primitives, lineWidth); breaker.computeBreaks(&computedBreaks, &computedWidths, &computedFlags); } b->finish(); return recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleFlags, recycleLength, computedBreaks, computedWidths, computedFlags); } static jlong nNewBuilder(JNIEnv*, jclass) { return reinterpret_cast<jlong>(new Builder); } static void nFreeBuilder(JNIEnv*, jclass, jlong nativePtr) { delete reinterpret_cast<Builder*>(nativePtr); } static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) { Builder* b = reinterpret_cast<Builder*>(nativePtr); b->finish(); } static void nBuilderSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName) { ScopedIcuLocale icuLocale(env, javaLocaleName); Builder* b = reinterpret_cast<Builder*>(nativePtr); if (icuLocale.valid()) { b->setLocale(icuLocale.locale()); } } static JNINativeMethod gMethods[] = { {"nComputeLineBreaks", "(Ljava/lang/String;[C[FIFIF[IIZLandroid/text/StaticLayout$LineBreaks;[I[F[ZI)I", (void*) nComputeLineBreaks} {"nNewBuilder", "()J", (void*) nNewBuilder}, {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, {"nFinishBuilder", "(J)V", (void*) nFinishBuilder}, {"nBuilderSetLocale", "(JLjava/lang/String;)V", (void*) nBuilderSetLocale}, {"nComputeLineBreaks", "(J[C[FIFIF[IIZLandroid/text/StaticLayout$LineBreaks;[I[F[ZI)I", (void*) nComputeLineBreaks} }; int register_android_text_StaticLayout(JNIEnv* env) Loading