Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 888dbca2 authored by Seigo Nonaka's avatar Seigo Nonaka
Browse files

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
parent e9f5e860
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -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;
        }
    }

    /**
+94 −142
Original line number Diff line number Diff line
@@ -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;
        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 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;

            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 || charAt(j) == TAB_CHAR) {
                    h += drawRun(c, segStart, j, runIsRtl, x + h, top, y, bottom,
                            runIndex != (runCount - 1) || j != mLen);

                if (j == runLimit || codept == '\t') {
                    h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom,
                            i != lastRunIndex || 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;
                }
            }
        }