diff options
| author | Seigo Nonaka <nona@google.com> | 2018-11-09 00:48:58 -0800 |
|---|---|---|
| committer | Seigo Nonaka <nona@google.com> | 2018-11-14 13:18:38 -0800 |
| commit | 888dbca2f720ae2ae52dce1d88063e46e3b53c28 (patch) | |
| tree | ed2a3cae9df1d72f67a10e77ae2f475cc4b8fff7 /core/java/android | |
| parent | e9f5e860547e7314c4c0e03fa4838c5b06164f5d (diff) | |
Refactor TextLine class
Did following refactoring:
- The surrogate composition is not necessary since this method is only
interested in TAB character.
- Move BiDi information interpretation logic to Layout.Directions.
- The short circuit for DIRS_ALL_LEFT_TO_RIGHT/DIRS_ALL_RIGHT_TO_LEFT
is not necessary.
- Renamed the variables.
Bug: 119312268
Test: atest TextLineTest
Test: TreeHugger
Test: Verified locally this doesn't cause performance regression
Change-Id: If987387673ec5a22f703952dfbe45b084664ee59
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/text/Layout.java | 46 | ||||
| -rw-r--r-- | core/java/android/text/TextLine.java | 236 |
2 files changed, 140 insertions, 142 deletions
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index e5fd2921227d..c8e0dd291041 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -2360,6 +2360,52 @@ public abstract class Layout { public Directions(int[] dirs) { mDirections = dirs; } + + /** + * Returns number of BiDi runs. + * + * @hide + */ + public @IntRange(from = 0) int getRunCount() { + return mDirections.length / 2; + } + + /** + * Returns the start offset of the BiDi run. + * + * @param runIndex the index of the BiDi run + * @return the start offset of the BiDi run. + * @hide + */ + public @IntRange(from = 0) int getRunStart(@IntRange(from = 0) int runIndex) { + return mDirections[runIndex * 2]; + } + + /** + * Returns the length of the BiDi run. + * + * Note that this method may return too large number due to reducing the number of object + * allocations. The too large number means the remaining part is assigned to this run. The + * caller must clamp the returned value. + * + * @param runIndex the index of the BiDi run + * @return the length of the BiDi run. + * @hide + */ + public @IntRange(from = 0) int getRunLength(@IntRange(from = 0) int runIndex) { + return mDirections[runIndex * 2 + 1] & RUN_LENGTH_MASK; + } + + /** + * Returns true if the BiDi run is RTL. + * + * @param runIndex the index of the BiDi run + * @return true if the BiDi run is RTL. + * @hide + */ + public boolean isRunRtl(int runIndex) { + return (mDirections[runIndex * 2 + 1] & RUN_RTL_FLAG) != 0; + } } /** diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 44dfd115f7f2..7b638b4c4a63 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -16,6 +16,7 @@ package android.text; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; @@ -51,6 +52,8 @@ import java.util.ArrayList; public class TextLine { private static final boolean DEBUG = false; + private static final char TAB_CHAR = '\t'; + private TextPaint mPaint; @UnsupportedAppUsage private CharSequence mText; @@ -232,6 +235,10 @@ public class TextLine { mEllipsisEnd = ellipsisStart != ellipsisEnd ? ellipsisEnd : 0; } + private char charAt(int i) { + return mCharsValid ? mChars[i] : mText.charAt(i + mStart); + } + /** * Justify the line to the given width. */ @@ -261,51 +268,23 @@ public class TextLine { * @param bottom the bottom of the line */ void draw(Canvas c, float x, int top, int y, int bottom) { - if (!mHasTabs) { - if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { - drawRun(c, 0, mLen, false, x, top, y, bottom, false); - return; - } - if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { - drawRun(c, 0, mLen, true, x, top, y, bottom, false); - return; - } - } - float h = 0; - int[] runs = mDirections.mDirections; - - int lastRunIndex = runs.length - 2; - for (int i = 0; i < runs.length; i += 2) { - int runStart = runs[i]; - int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK); - if (runLimit > mLen) { - runLimit = mLen; - } - boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0; + final int runCount = mDirections.getRunCount(); + for (int runIndex = 0; runIndex < runCount; runIndex++) { + final int runStart = mDirections.getRunStart(runIndex); + final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen); + final boolean runIsRtl = mDirections.isRunRtl(runIndex); - int segstart = runStart; + int segStart = runStart; for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { - int codept = 0; - if (mHasTabs && j < runLimit) { - codept = mChars[j]; - if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) { - codept = Character.codePointAt(mChars, j); - if (codept > 0xFFFF) { - ++j; - continue; - } - } - } - - if (j == runLimit || codept == '\t') { - h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom, - i != lastRunIndex || j != mLen); + if (j == runLimit || charAt(j) == TAB_CHAR) { + h += drawRun(c, segStart, j, runIsRtl, x + h, top, y, bottom, + runIndex != (runCount - 1) || j != mLen); - if (codept == '\t') { + if (j != runLimit) { // charAt(j) == TAB_CHAR h = mDir * nextTab(h * mDir); } - segstart = j + 1; + segStart = j + 1; } } } @@ -323,75 +302,81 @@ public class TextLine { } /** - * Returns information about a position on the line. + * Returns the signed graphical offset from the leading margin. * - * @param offset the line-relative character offset, between 0 and the - * line length, inclusive - * @param trailing true to measure the trailing edge of the character - * before offset, false to measure the leading edge of the character - * at offset. - * @param fmi receives metrics information about the requested - * character, can be null. - * @return the signed offset from the leading margin to the requested - * character edge. + * Following examples are all for measuring offset=3. LX(e.g. L0, L1, ...) denotes a + * character which has LTR BiDi property. On the other hand, RX(e.g. R0, R1, ...) denotes a + * character which has RTL BiDi property. Assuming all character has 1em width. + * + * Example 1: All LTR chars within LTR context + * Input Text (logical) : L0 L1 L2 L3 L4 L5 L6 L7 L8 + * Input Text (visual) : L0 L1 L2 L3 L4 L5 L6 L7 L8 + * Output(trailing=true) : |--------| (Returns 3em) + * Output(trailing=false): |--------| (Returns 3em) + * + * Example 2: All RTL chars within RTL context. + * Input Text (logical) : R0 R1 R2 R3 R4 R5 R6 R7 R8 + * Input Text (visual) : R8 R7 R6 R5 R4 R3 R2 R1 R0 + * Output(trailing=true) : |--------| (Returns -3em) + * Output(trailing=false): |--------| (Returns -3em) + * + * Example 3: BiDi chars within LTR context. + * Input Text (logical) : L0 L1 L2 R3 R4 R5 L6 L7 L8 + * Input Text (visual) : L0 L1 L2 R5 R4 R3 L6 L7 L8 + * Output(trailing=true) : |-----------------| (Returns 6em) + * Output(trailing=false): |--------| (Returns 3em) + * + * Example 4: BiDi chars within RTL context. + * Input Text (logical) : L0 L1 L2 R3 R4 R5 L6 L7 L8 + * Input Text (visual) : L6 L7 L8 R5 R4 R3 L0 L1 L2 + * Output(trailing=true) : |-----------------| (Returns -6em) + * Output(trailing=false): |--------| (Returns -3em) + * + * @param offset the line-relative character offset, between 0 and the line length, inclusive + * @param trailing no effect if the offset is not on the BiDi transition offset. If the offset + * is on the BiDi transition offset and true is passed, the offset is regarded + * as the edge of the trailing run's edge. If false, the offset is regarded as + * the edge of the preceding run's edge. See example above. + * @param fmi receives metrics information about the requested character, can be null + * @return the signed graphical offset from the leading margin to the requested character edge. + * The positive value means the offset is right from the leading edge. The negative + * value means the offset is left from the leading edge. */ - public float measure(int offset, boolean trailing, FontMetricsInt fmi) { - int target = trailing ? offset - 1 : offset; + public float measure(@IntRange(from = 0) int offset, boolean trailing, + @NonNull FontMetricsInt fmi) { + if (offset > mLen) { + throw new IndexOutOfBoundsException( + "offset(" + offset + ") should be less than line limit(" + mLen + ")"); + } + final int target = trailing ? offset - 1 : offset; if (target < 0) { return 0; } float h = 0; + for (int runIndex = 0; runIndex < mDirections.getRunCount(); runIndex++) { + final int runStart = mDirections.getRunStart(runIndex); + final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen); + final boolean runIsRtl = mDirections.isRunRtl(runIndex); - if (!mHasTabs) { - if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { - return measureRun(0, offset, mLen, false, fmi); - } - if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { - return measureRun(0, offset, mLen, true, fmi); - } - } - - char[] chars = mChars; - int[] runs = mDirections.mDirections; - for (int i = 0; i < runs.length; i += 2) { - int runStart = runs[i]; - int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK); - if (runLimit > mLen) { - runLimit = mLen; - } - boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0; - - int segstart = runStart; + int segStart = runStart; for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { - int codept = 0; - if (mHasTabs && j < runLimit) { - codept = chars[j]; - if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) { - codept = Character.codePointAt(chars, j); - if (codept > 0xFFFF) { - ++j; - continue; - } - } - } - - if (j == runLimit || codept == '\t') { - boolean inSegment = target >= segstart && target < j; + if (j == runLimit || charAt(j) == TAB_CHAR) { + final boolean targetIsInThisSegment = target >= segStart && target < j; + final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; - boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; - if (inSegment && advance) { - return h + measureRun(segstart, offset, j, runIsRtl, fmi); + if (targetIsInThisSegment && sameDirection) { + return h + measureRun(segStart, offset, j, runIsRtl, fmi); } - float w = measureRun(segstart, j, j, runIsRtl, fmi); - h += advance ? w : -w; + final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi); + h += sameDirection ? segmentWidth : -segmentWidth; - if (inSegment) { - return h + measureRun(segstart, offset, j, runIsRtl, null); + if (targetIsInThisSegment) { + return h + measureRun(segStart, offset, j, runIsRtl, null); } - if (codept == '\t') { + if (j != runLimit) { // charAt(j) == TAB_CHAR if (offset == j) { return h; } @@ -401,7 +386,7 @@ public class TextLine { } } - segstart = j + 1; + segStart = j + 1; } } } @@ -426,62 +411,29 @@ public class TextLine { } float h = 0; + for (int runIndex = 0; runIndex < mDirections.getRunCount(); runIndex++) { + final int runStart = mDirections.getRunStart(runIndex); + final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen); + final boolean runIsRtl = mDirections.isRunRtl(runIndex); - if (!mHasTabs) { - if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { - for (int offset = 0; offset <= mLen; ++offset) { - measurement[offset] = measureRun(0, offset, mLen, false, fmi); - } - return measurement; - } - if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { - for (int offset = 0; offset <= mLen; ++offset) { - measurement[offset] = measureRun(0, offset, mLen, true, fmi); - } - return measurement; - } - } - - char[] chars = mChars; - int[] runs = mDirections.mDirections; - for (int i = 0; i < runs.length; i += 2) { - int runStart = runs[i]; - int runLimit = runStart + (runs[i + 1] & Layout.RUN_LENGTH_MASK); - if (runLimit > mLen) { - runLimit = mLen; - } - boolean runIsRtl = (runs[i + 1] & Layout.RUN_RTL_FLAG) != 0; - - int segstart = runStart; + int segStart = runStart; for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; ++j) { - int codept = 0; - if (mHasTabs && j < runLimit) { - codept = chars[j]; - if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) { - codept = Character.codePointAt(chars, j); - if (codept > 0xFFFF) { - ++j; - continue; - } - } - } - - if (j == runLimit || codept == '\t') { - float oldh = h; - boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; - float w = measureRun(segstart, j, j, runIsRtl, fmi); + if (j == runLimit || charAt(j) == TAB_CHAR) { + final float oldh = h; + final boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; + final float w = measureRun(segStart, j, j, runIsRtl, fmi); h += advance ? w : -w; - float baseh = advance ? oldh : h; + final float baseh = advance ? oldh : h; FontMetricsInt crtfmi = advance ? fmi : null; - for (int offset = segstart; offset <= j && offset <= mLen; ++offset) { - if (target[offset] >= segstart && target[offset] < j) { + for (int offset = segStart; offset <= j && offset <= mLen; ++offset) { + if (target[offset] >= segStart && target[offset] < j) { measurement[offset] = - baseh + measureRun(segstart, offset, j, runIsRtl, crtfmi); + baseh + measureRun(segStart, offset, j, runIsRtl, crtfmi); } } - if (codept == '\t') { + if (j != runLimit) { // charAt(j) == TAB_CHAR if (target[j] == j) { measurement[j] = h; } @@ -491,7 +443,7 @@ public class TextLine { } } - segstart = j + 1; + segStart = j + 1; } } } |
