diff options
author | Alan Lau <alanlau@google.com> | 2014-07-30 17:32:03 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-07-30 16:17:51 +0000 |
commit | c006227265d7ac705ebeef2f7d87104fdb63f32a (patch) | |
tree | 52368a410963a471e4ba1033130308151d1fce1d | |
parent | b6bfa9e45a953b83289ee57da661746deae7a63f (diff) | |
parent | 273906bb6af6e43f8a2503c7409363bdb76b706c (diff) | |
download | frameworks_base-c006227265d7ac705ebeef2f7d87104fdb63f32a.zip frameworks_base-c006227265d7ac705ebeef2f7d87104fdb63f32a.tar.gz frameworks_base-c006227265d7ac705ebeef2f7d87104fdb63f32a.tar.bz2 |
Merge "Revert "Revert "DO NOT MERGE Implement line breaking using ICU break iterator""" into klp-modular-dev
-rw-r--r-- | core/java/android/text/StaticLayout.java | 123 | ||||
-rw-r--r-- | core/jni/Android.mk | 1 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
-rw-r--r-- | core/jni/android_text_StaticLayout.cpp | 110 |
4 files changed, 130 insertions, 106 deletions
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 814326c..3aad4aa 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -161,6 +161,9 @@ public class StaticLayout extends Layout { float spacingadd, boolean includepad, boolean trackpad, float ellipsizedWidth, TextUtils.TruncateAt ellipsize) { + int[] breakOpp = null; + final String localeLanguageTag = paint.getTextLocale().toLanguageTag(); + mLineCount = 0; int v = 0; @@ -175,8 +178,6 @@ public class StaticLayout extends Layout { if (source instanceof Spanned) spanned = (Spanned) source; - int DEFAULT_DIR = DIR_LEFT_TO_RIGHT; // XXX - int paraEnd; for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) { paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd); @@ -243,6 +244,9 @@ public class StaticLayout extends Layout { int dir = measured.mDir; boolean easy = measured.mEasy; + breakOpp = nLineBreakOpportunities(localeLanguageTag, chs, paraEnd - paraStart, breakOpp); + int breakOppIndex = 0; + int width = firstWidth; float w = 0; @@ -355,15 +359,12 @@ public class StaticLayout extends Layout { if (fmBottom > fitBottom) fitBottom = fmBottom; - // From the Unicode Line Breaking Algorithm (at least approximately) - boolean isLineBreak = isSpaceOrTab || - // / is class SY and - is class HY, except when followed by a digit - ((c == CHAR_SLASH || c == CHAR_HYPHEN) && - (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) || - // Ideographs are class ID: breakpoints when adjacent, except for NS - // (non-starters), which can be broken after but not before - (c >= CHAR_FIRST_CJK && isIdeographic(c, true) && - j + 1 < spanEnd && isIdeographic(chs[j + 1 - paraStart], false)); + while (breakOpp[breakOppIndex] != -1 + && breakOpp[breakOppIndex] < j - paraStart + 1) { + breakOppIndex++; + } + boolean isLineBreak = breakOppIndex < breakOpp.length && + breakOpp[breakOppIndex] == j - paraStart + 1; if (isLineBreak) { okWidth = w; @@ -491,97 +492,6 @@ public class StaticLayout extends Layout { } } - /** - * Returns true if the specified character is one of those specified - * as being Ideographic (class ID) by the Unicode Line Breaking Algorithm - * (http://www.unicode.org/unicode/reports/tr14/), and is therefore OK - * to break between a pair of. - * - * @param includeNonStarters also return true for category NS - * (non-starters), which can be broken - * after but not before. - */ - private static final boolean isIdeographic(char c, boolean includeNonStarters) { - if (c >= '\u2E80' && c <= '\u2FFF') { - return true; // CJK, KANGXI RADICALS, DESCRIPTION SYMBOLS - } - if (c == '\u3000') { - return true; // IDEOGRAPHIC SPACE - } - if (c >= '\u3040' && c <= '\u309F') { - if (!includeNonStarters) { - switch (c) { - case '\u3041': // # HIRAGANA LETTER SMALL A - case '\u3043': // # HIRAGANA LETTER SMALL I - case '\u3045': // # HIRAGANA LETTER SMALL U - case '\u3047': // # HIRAGANA LETTER SMALL E - case '\u3049': // # HIRAGANA LETTER SMALL O - case '\u3063': // # HIRAGANA LETTER SMALL TU - case '\u3083': // # HIRAGANA LETTER SMALL YA - case '\u3085': // # HIRAGANA LETTER SMALL YU - case '\u3087': // # HIRAGANA LETTER SMALL YO - case '\u308E': // # HIRAGANA LETTER SMALL WA - case '\u3095': // # HIRAGANA LETTER SMALL KA - case '\u3096': // # HIRAGANA LETTER SMALL KE - case '\u309B': // # KATAKANA-HIRAGANA VOICED SOUND MARK - case '\u309C': // # KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK - case '\u309D': // # HIRAGANA ITERATION MARK - case '\u309E': // # HIRAGANA VOICED ITERATION MARK - return false; - } - } - return true; // Hiragana (except small characters) - } - if (c >= '\u30A0' && c <= '\u30FF') { - if (!includeNonStarters) { - switch (c) { - case '\u30A0': // # KATAKANA-HIRAGANA DOUBLE HYPHEN - case '\u30A1': // # KATAKANA LETTER SMALL A - case '\u30A3': // # KATAKANA LETTER SMALL I - case '\u30A5': // # KATAKANA LETTER SMALL U - case '\u30A7': // # KATAKANA LETTER SMALL E - case '\u30A9': // # KATAKANA LETTER SMALL O - case '\u30C3': // # KATAKANA LETTER SMALL TU - case '\u30E3': // # KATAKANA LETTER SMALL YA - case '\u30E5': // # KATAKANA LETTER SMALL YU - case '\u30E7': // # KATAKANA LETTER SMALL YO - case '\u30EE': // # KATAKANA LETTER SMALL WA - case '\u30F5': // # KATAKANA LETTER SMALL KA - case '\u30F6': // # KATAKANA LETTER SMALL KE - case '\u30FB': // # KATAKANA MIDDLE DOT - case '\u30FC': // # KATAKANA-HIRAGANA PROLONGED SOUND MARK - case '\u30FD': // # KATAKANA ITERATION MARK - case '\u30FE': // # KATAKANA VOICED ITERATION MARK - return false; - } - } - return true; // Katakana (except small characters) - } - if (c >= '\u3400' && c <= '\u4DB5') { - return true; // CJK UNIFIED IDEOGRAPHS EXTENSION A - } - if (c >= '\u4E00' && c <= '\u9FBB') { - return true; // CJK UNIFIED IDEOGRAPHS - } - if (c >= '\uF900' && c <= '\uFAD9') { - return true; // CJK COMPATIBILITY IDEOGRAPHS - } - if (c >= '\uA000' && c <= '\uA48F') { - return true; // YI SYLLABLES - } - if (c >= '\uA490' && c <= '\uA4CF') { - return true; // YI RADICALS - } - if (c >= '\uFE62' && c <= '\uFE66') { - return true; // SMALL PLUS SIGN to SMALL EQUALS SIGN - } - if (c >= '\uFF10' && c <= '\uFF19') { - return true; // WIDE DIGITS - } - - return false; - } - private int out(CharSequence text, int start, int end, int above, int below, int top, int bottom, int v, float spacingmult, float spacingadd, @@ -930,6 +840,11 @@ public class StaticLayout extends Layout { mMeasured = MeasuredText.recycle(mMeasured); } + // returns an array with terminal sentinel value -1 to indicate end + // this is so that arrays can be recycled instead of allocating new arrays + // every time + private static native int[] nLineBreakOpportunities(String locale, char[] text, int length, int[] recycle); + private int mLineCount; private int mTopPadding, mBottomPadding; private int mColumns; @@ -955,13 +870,9 @@ public class StaticLayout extends Layout { private static final int TAB_INCREMENT = 20; // same as Layout, but that's private - private static final char CHAR_FIRST_CJK = '\u2E80'; - private static final char CHAR_NEW_LINE = '\n'; private static final char CHAR_TAB = '\t'; private static final char CHAR_SPACE = ' '; - private static final char CHAR_SLASH = '/'; - private static final char CHAR_HYPHEN = '-'; private static final char CHAR_ZWSP = '\u200B'; private static final double EXTRA_ROUNDING = 0.5; diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 2e0acb1..01517b2 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -62,6 +62,7 @@ LOCAL_SRC_FILES:= \ android_view_VelocityTracker.cpp \ android_text_AndroidCharacter.cpp \ android_text_AndroidBidi.cpp \ + android_text_StaticLayout.cpp \ android_os_Debug.cpp \ android_os_MemoryFile.cpp \ android_os_MessageQueue.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index b4599b6..ead3cbe 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -149,6 +149,7 @@ extern int register_android_net_NetworkUtils(JNIEnv* env); extern int register_android_net_TrafficStats(JNIEnv* env); extern int register_android_net_wifi_WifiNative(JNIEnv* env); extern int register_android_text_AndroidCharacter(JNIEnv *env); +extern int register_android_text_StaticLayout(JNIEnv *env); extern int register_android_text_AndroidBidi(JNIEnv *env); extern int register_android_opengl_classes(JNIEnv *env); extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env); @@ -1105,6 +1106,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_XmlBlock), REG_JNI(register_android_emoji_EmojiFactory), REG_JNI(register_android_text_AndroidCharacter), + REG_JNI(register_android_text_StaticLayout), REG_JNI(register_android_text_AndroidBidi), REG_JNI(register_android_view_InputDevice), REG_JNI(register_android_view_KeyCharacterMap), diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp new file mode 100644 index 0000000..696926c --- /dev/null +++ b/core/jni/android_text_StaticLayout.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "StaticLayout" + +#include "ScopedIcuLocale.h" +#include "unicode/locid.h" +#include "unicode/brkiter.h" +#include "utils/misc.h" +#include "utils/Log.h" +#include "ScopedPrimitiveArray.h" +#include "JNIHelp.h" +#include <android_runtime/AndroidRuntime.h> +#include <vector> + +namespace android { + +class ScopedBreakIterator { + public: + ScopedBreakIterator(JNIEnv* env, BreakIterator* breakIterator, jcharArray inputText, + jint length) : mBreakIterator(breakIterator), mChars(env, inputText) { + UErrorCode status = U_ZERO_ERROR; + mUText = utext_openUChars(NULL, mChars.get(), length, &status); + if (mUText == NULL) { + return; + } + + mBreakIterator->setText(mUText, status); + } + + inline BreakIterator* operator->() { + return mBreakIterator; + } + + ~ScopedBreakIterator() { + utext_close(mUText); + delete mBreakIterator; + } + private: + BreakIterator* mBreakIterator; + ScopedCharArrayRO mChars; + UText* mUText; + + // disable copying and assignment + ScopedBreakIterator(const ScopedBreakIterator&); + void operator=(const ScopedBreakIterator&); +}; + +static jintArray nLineBreakOpportunities(JNIEnv* env, jclass, jstring javaLocaleName, + jcharArray inputText, jint length, + jintArray recycle) { + jintArray ret; + std::vector<jint> breaks(16); + + 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, inputText, length); + for (int loc = breakIterator->first(); loc != BreakIterator::DONE; + loc = breakIterator->next()) { + breaks.push_back(loc); + } + } + } + + breaks.push_back(-1); // sentinel terminal value + + if (recycle != NULL && env->GetArrayLength(recycle) >= breaks.size()) { + ret = recycle; + } else { + ret = env->NewIntArray(breaks.size()); + } + + if (ret != NULL) { + env->SetIntArrayRegion(ret, 0, breaks.size(), &breaks.front()); + } + + return ret; +} + +static JNINativeMethod gMethods[] = { + {"nLineBreakOpportunities", "(Ljava/lang/String;[CI[I)[I", (void*) nLineBreakOpportunities} +}; + +int register_android_text_StaticLayout(JNIEnv* env) +{ + return AndroidRuntime::registerNativeMethods(env, "android/text/StaticLayout", + gMethods, NELEM(gMethods)); +} + +} |