diff options
author | Doug Felt <dougfelt@google.com> | 2010-03-29 14:58:40 -0700 |
---|---|---|
committer | Doug Felt <dougfelt@google.com> | 2010-04-07 14:54:51 -0700 |
commit | e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7a (patch) | |
tree | 6af998e3ce59000a4edcf0a40e49e1eb9636acab /core/java/android/text/AndroidBidi.java | |
parent | 5d470d03df643801aa4eef11a26deee70da7b952 (diff) | |
download | frameworks_base-e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7a.zip frameworks_base-e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7a.tar.gz frameworks_base-e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7a.tar.bz2 |
Refactor Styled utility functions into reusable objects.
This takes utility functions from Styled and a few other classes and
incorporates them into two new utility classes, TextLine and
MeasuredText. The main point of this is to support shaping by skia,
to experiment with how this will look, this also introduces
character-based Arabic shaping.
MeasuredText is used by code that determines line breaks by generating
and examining character widths in logical order. Factoring the code
in this way makes it usable by the ellipsize functions in TextUtils as
well as by StaticLayout. This class takes over the caching of widths
and chars arrays that was previously performed by StyledText. A small
number of MeasuredText objects are themselves cached by the class and
accesed using static obtain and recycle methods. Generally only these
few cached instances are ever created.
TextLine is used by code that draws or measures text on a line. This
unifies the line measuring and rendering code, and pushes assumptions
about how rtl text is treated closer to the points where skia code is
invoked. TextLine implements the functions that were previously
provided by Styled, working on member arrays rather than
explicitly-passed arguments. It implements the same kind of static
cache as MeasuredText.
TextLine and MeasureText simulate arabic glyph generation and shaping
by using ArabicShaping, ported with very minor changes from ICU4J's
ArabicShaping. This class generates shaped Arabic glyphs and Lam-Alef
ligatures using Unicode presentation forms. ArabicShaping is not
intended to be permanent, but to be replaced by real shaping from the
skia layer. It is introduced in order to emulate the behavior of real
shaping so that higher level code dealing with rendering shaped text
and cursor movement over ligatures can be developed and tested; it
also provides basic-level support for Arabic.
Since cursor movement depends on conjuncts whose formation is
font-dependent, cursor movement code that was formerly in Layout and
StaticLayout was moved into TextLine so that it can work on the shaped
text.
Other than these changes, the other major change is a rework of the
ellipsize utility functions to combine multiple branches into fewer
branches with additional state.
Updated copyright notices on new files.
Change-Id: I492cb58b51f5aaf6f14cb1419bdbed49eac5ba29
Diffstat (limited to 'core/java/android/text/AndroidBidi.java')
-rw-r--r-- | core/java/android/text/AndroidBidi.java | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java index e4f934e..eacd40d 100644 --- a/core/java/android/text/AndroidBidi.java +++ b/core/java/android/text/AndroidBidi.java @@ -16,6 +16,8 @@ package android.text; +import android.text.Layout.Directions; + /** * Access the ICU bidi implementation. * @hide @@ -44,5 +46,132 @@ package android.text; return result; } + /** + * Returns run direction information for a line within a paragraph. + * + * @param dir base line direction, either Layout.DIR_LEFT_TO_RIGHT or + * Layout.DIR_RIGHT_TO_LEFT + * @param levels levels as returned from {@link #bidi} + * @param lstart start of the line in the levels array + * @param chars the character array (used to determine whitespace) + * @param cstart the start of the line in the chars array + * @param len the length of the line + * @return the directions + */ + public static Directions directions(int dir, byte[] levels, int lstart, + char[] chars, int cstart, int len) { + + int baseLevel = dir == Layout.DIR_LEFT_TO_RIGHT ? 0 : 1; + int curLevel = levels[lstart]; + int minLevel = curLevel; + int runCount = 1; + for (int i = lstart + 1, e = lstart + len; i < e; ++i) { + int level = levels[i]; + if (level != curLevel) { + curLevel = level; + ++runCount; + } + } + + // add final run for trailing counter-directional whitespace + int visLen = len; + if ((curLevel & 1) != (baseLevel & 1)) { + // look for visible end + while (--visLen >= 0) { + char ch = chars[cstart + visLen]; + + if (ch == '\n') { + --visLen; + break; + } + + if (ch != ' ' && ch != '\t') { + break; + } + } + ++visLen; + if (visLen != len) { + ++runCount; + } + } + + if (runCount == 1 && minLevel == baseLevel) { + // we're done, only one run on this line + if ((minLevel & 1) != 0) { + return Layout.DIRS_ALL_RIGHT_TO_LEFT; + } + return Layout.DIRS_ALL_LEFT_TO_RIGHT; + } + + int[] ld = new int[runCount * 2]; + int maxLevel = minLevel; + int levelBits = minLevel << Layout.RUN_LEVEL_SHIFT; + { + // Start of first pair is always 0, we write + // length then start at each new run, and the + // last run length after we're done. + int n = 1; + int prev = lstart; + curLevel = minLevel; + for (int i = lstart, e = lstart + visLen; i < e; ++i) { + int level = levels[i]; + if (level != curLevel) { + curLevel = level; + if (level > maxLevel) { + maxLevel = level; + } else if (level < minLevel) { + minLevel = level; + } + // XXX ignore run length limit of 2^RUN_LEVEL_SHIFT + ld[n++] = (i - prev) | levelBits; + ld[n++] = i - lstart; + levelBits = curLevel << Layout.RUN_LEVEL_SHIFT; + prev = i; + } + } + ld[n] = (lstart + visLen - prev) | levelBits; + if (visLen < len) { + ld[++n] = visLen; + ld[++n] = (len - visLen) | (baseLevel << Layout.RUN_LEVEL_SHIFT); + } + } + + // See if we need to swap any runs. + // If the min level run direction doesn't match the base + // direction, we always need to swap (at this point + // we have more than one run). + // Otherwise, we don't need to swap the lowest level. + // Since there are no logically adjacent runs at the same + // level, if the max level is the same as the (new) min + // level, we have a series of alternating levels that + // is already in order, so there's no more to do. + // + boolean swap; + if ((minLevel & 1) == baseLevel) { + minLevel += 1; + swap = maxLevel > minLevel; + } else { + swap = runCount > 1; + } + if (swap) { + for (int level = maxLevel - 1; level >= minLevel; --level) { + for (int i = 0; i < ld.length; i += 2) { + if (levels[ld[i]] >= level) { + int e = i + 2; + while (e < ld.length && levels[ld[e]] >= level) { + e += 2; + } + for (int low = i, hi = e - 2; low < hi; low += 2, hi -= 2) { + int x = ld[low]; ld[low] = ld[hi]; ld[hi] = x; + x = ld[low+1]; ld[low+1] = ld[hi+1]; ld[hi+1] = x; + } + i = e + 2; + } + } + } + } + return new Directions(ld); + } + private native static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo); }
\ No newline at end of file |