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

Commit dd61718d authored by Seigo Nonaka's avatar Seigo Nonaka
Browse files

[2nd] Add cluster count API

This is a preparation of the inter character justification

This is a 2nd attempt. The previous attempt was reverted due to
SystemUIGoogleRoboRNGTests failure which assumes the JNI signature has
not been changed. The proper fix is update the Robolectric artifacts
used by SystemUIGoogleRoboRNGTest but it cannot be done in the Android
repo, so add a workaround of calling JNI of old signature on Robolectric
environment.

Bug: 283193133
Test: CtsTextTestCases
Test: minikin_tests
Test: SystemUIGoogleRoboRNGTests
Change-Id: I0eb14f432065d4fc87de4e6e8766ef561f879697
parent 57e3d19b
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -47292,6 +47292,7 @@ package android.text {
    method public int getLineForOffset(int);
    method public int getLineForOffset(int);
    method public int getLineForVertical(int);
    method public int getLineForVertical(int);
    method public float getLineLeft(int);
    method public float getLineLeft(int);
    method @FlaggedApi("com.android.text.flags.inter_character_justification") @IntRange(from=0) public int getLineLetterSpacingUnitCount(@IntRange(from=0) int, boolean);
    method public float getLineMax(int);
    method public float getLineMax(int);
    method public float getLineRight(int);
    method public float getLineRight(int);
    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingAmount();
    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingAmount();
+2 −2
Original line number Original line Diff line number Diff line
@@ -454,7 +454,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
            line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
            line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
                    Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
                    Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
                    mEllipsizedStart, mEllipsizedStart + mEllipsizedCount, useFallbackLineSpacing);
                    mEllipsizedStart, mEllipsizedStart + mEllipsizedCount, useFallbackLineSpacing);
            mMax = (int) Math.ceil(line.metrics(null, null, false));
            mMax = (int) Math.ceil(line.metrics(null, null, false, null));
            TextLine.recycle(line);
            TextLine.recycle(line);
        }
        }


@@ -603,7 +603,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
                0 /* ellipsisStart, 0 since text has not been ellipsized at this point */,
                0 /* ellipsisStart, 0 since text has not been ellipsized at this point */,
                0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */,
                0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */,
                useFallbackLineSpacing);
                useFallbackLineSpacing);
        fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false));
        fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false, null));
        TextLine.recycle(line);
        TextLine.recycle(line);


        return fm;
        return fm;
+80 −10
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package android.text;


import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
import static com.android.text.flags.Flags.FLAG_INTER_CHARACTER_JUSTIFICATION;


import android.annotation.FlaggedApi;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.FloatRange;
@@ -50,8 +51,10 @@ import com.android.internal.util.GrowingArrayUtils;


import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
import java.text.BreakIterator;
import java.util.Arrays;
import java.util.Arrays;
import java.util.List;
import java.util.List;
import java.util.Locale;


/**
/**
 * A base class that manages text layout in visual elements on
 * A base class that manages text layout in visual elements on
@@ -669,7 +672,8 @@ public abstract class Layout {
            int start = previousLineEnd;
            int start = previousLineEnd;
            previousLineEnd = getLineStart(lineNum + 1);
            previousLineEnd = getLineStart(lineNum + 1);
            final boolean justify = isJustificationRequired(lineNum);
            final boolean justify = isJustificationRequired(lineNum);
            int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
            int end = getLineVisibleEnd(lineNum, start, previousLineEnd,
                    true /* trailingSpaceAtLastLineIsVisible */);
            paint.setStartHyphenEdit(getStartHyphenEdit(lineNum));
            paint.setStartHyphenEdit(getStartHyphenEdit(lineNum));
            paint.setEndHyphenEdit(getEndHyphenEdit(lineNum));
            paint.setEndHyphenEdit(getEndHyphenEdit(lineNum));


@@ -1056,7 +1060,7 @@ public abstract class Layout {
            if (isJustificationRequired(line)) {
            if (isJustificationRequired(line)) {
                tl.justify(getJustifyWidth(line));
                tl.justify(getJustifyWidth(line));
            }
            }
            tl.metrics(null, rectF, false);
            tl.metrics(null, rectF, false, null);


            float lineLeft = rectF.left;
            float lineLeft = rectF.left;
            float lineRight = rectF.right;
            float lineRight = rectF.right;
@@ -1456,7 +1460,7 @@ public abstract class Layout {
        tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops,
        tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops,
                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
                isFallbackLineSpacingEnabled());
                isFallbackLineSpacingEnabled());
        float wid = tl.measure(offset - start, trailing, null, null);
        float wid = tl.measure(offset - start, trailing, null, null, null);
        TextLine.recycle(tl);
        TextLine.recycle(tl);


        if (clamped && wid > mWidth) {
        if (clamped && wid > mWidth) {
@@ -1792,11 +1796,68 @@ public abstract class Layout {
        if (isJustificationRequired(line)) {
        if (isJustificationRequired(line)) {
            tl.justify(getJustifyWidth(line));
            tl.justify(getJustifyWidth(line));
        }
        }
        final float width = tl.metrics(null, null, mUseBoundsForWidth);
        final float width = tl.metrics(null, null, mUseBoundsForWidth, null);
        TextLine.recycle(tl);
        TextLine.recycle(tl);
        return width;
        return width;
    }
    }


    /**
     * Returns the number of letter spacing unit in the line.
     *
     * <p>
     * This API returns a number of letters that is a target of letter spacing. The letter spacing
     * won't be added to the middle of the characters that are needed to be treated as a single,
     * e.g., ligatured or conjunct form. Note that this value is different from the number of]
     * grapheme clusters that is calculated by {@link BreakIterator#getCharacterInstance(Locale)}.
     * For example, if the "fi" is ligatured, the ligatured form is treated as single uni and letter
     * spacing is not added, but it has two separate grapheme cluster.
     *
     * <p>
     * This value is used for calculating the letter spacing amount for the justification because
     * the letter spacing is applied between clusters. For example, if extra {@code W} pixels needed
     * to be filled by letter spacing, the amount of letter spacing to be applied is
     * {@code W}/(letter spacing unit count - 1) px.
     *
     * @param line the index of the line
     * @param includeTrailingWhitespace whether to include trailing whitespace
     * @return the number of cluster count in the line.
     */
    @IntRange(from = 0)
    @FlaggedApi(FLAG_INTER_CHARACTER_JUSTIFICATION)
    public int getLineLetterSpacingUnitCount(@IntRange(from = 0) int line,
            boolean includeTrailingWhitespace) {
        final int start = getLineStart(line);
        final int end = includeTrailingWhitespace ? getLineEnd(line)
                : getLineVisibleEnd(line, getLineStart(line), getLineStart(line + 1),
                        false  // trailingSpaceAtLastLineIsVisible: Treating trailing whitespaces at
                               // the last line as a invisible chars for single line justification.
                );

        final Directions directions = getLineDirections(line);
        // Returned directions can actually be null
        if (directions == null) {
            return 0;
        }
        final int dir = getParagraphDirection(line);

        final TextLine tl = TextLine.obtain();
        final TextPaint paint = mWorkPaint;
        paint.set(mPaint);
        paint.setStartHyphenEdit(getStartHyphenEdit(line));
        paint.setEndHyphenEdit(getEndHyphenEdit(line));
        tl.set(paint, mText, start, end, dir, directions,
                false, null, // tab width is not used for cluster counting.
                getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
                isFallbackLineSpacingEnabled());
        if (mLineInfo == null) {
            mLineInfo = new TextLine.LineInfo();
        }
        mLineInfo.setClusterCount(0);
        tl.metrics(null, null, mUseBoundsForWidth, mLineInfo);
        TextLine.recycle(tl);
        return mLineInfo.getClusterCount();
    }

    /**
    /**
     * Returns the signed horizontal extent of the specified line, excluding
     * Returns the signed horizontal extent of the specified line, excluding
     * leading margin.  If full is false, excludes trailing whitespace.
     * leading margin.  If full is false, excludes trailing whitespace.
@@ -1823,7 +1884,7 @@ public abstract class Layout {
        if (isJustificationRequired(line)) {
        if (isJustificationRequired(line)) {
            tl.justify(getJustifyWidth(line));
            tl.justify(getJustifyWidth(line));
        }
        }
        final float width = tl.metrics(null, null, mUseBoundsForWidth);
        final float width = tl.metrics(null, null, mUseBoundsForWidth, null);
        TextLine.recycle(tl);
        TextLine.recycle(tl);
        return width;
        return width;
    }
    }
@@ -2432,15 +2493,22 @@ public abstract class Layout {
     * is not counted) on the specified line.
     * is not counted) on the specified line.
     */
     */
    public int getLineVisibleEnd(int line) {
    public int getLineVisibleEnd(int line) {
        return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1));
        return getLineVisibleEnd(line, getLineStart(line), getLineStart(line + 1),
                true /* trailingSpaceAtLastLineIsVisible */);
    }
    }


    private int getLineVisibleEnd(int line, int start, int end) {
    private int getLineVisibleEnd(int line, int start, int end,
            boolean trailingSpaceAtLastLineIsVisible) {
        CharSequence text = mText;
        CharSequence text = mText;
        char ch;
        char ch;

        // Historically, trailing spaces at the last line is counted as visible. However, this
        // doesn't work well for justification.
        if (trailingSpaceAtLastLineIsVisible) {
            if (line == getLineCount() - 1) {
            if (line == getLineCount() - 1) {
                return end;
                return end;
            }
            }
        }


        for (; end > start; end--) {
        for (; end > start; end--) {
            ch = text.charAt(end - 1);
            ch = text.charAt(end - 1);
@@ -2939,7 +3007,7 @@ public abstract class Layout {
            tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops,
            tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops,
                    0 /* ellipsisStart */, 0 /* ellipsisEnd */,
                    0 /* ellipsisStart */, 0 /* ellipsisEnd */,
                    false /* use fallback line spacing. unused */);
                    false /* use fallback line spacing. unused */);
            return margin + Math.abs(tl.metrics(null, null, useBoundsForWidth));
            return margin + Math.abs(tl.metrics(null, null, useBoundsForWidth, null));
        } finally {
        } finally {
            TextLine.recycle(tl);
            TextLine.recycle(tl);
            if (mt != null) {
            if (mt != null) {
@@ -3337,6 +3405,8 @@ public abstract class Layout {
    private boolean mUseBoundsForWidth;
    private boolean mUseBoundsForWidth;
    private @Nullable Paint.FontMetrics mMinimumFontMetrics;
    private @Nullable Paint.FontMetrics mMinimumFontMetrics;


    private TextLine.LineInfo mLineInfo = null;

    /** @hide */
    /** @hide */
    @IntDef(prefix = { "DIR_" }, value = {
    @IntDef(prefix = { "DIR_" }, value = {
            DIR_LEFT_TO_RIGHT,
            DIR_LEFT_TO_RIGHT,
+77 −35
Original line number Original line Diff line number Diff line
@@ -76,6 +76,21 @@ public class TextLine {
    private RectF mTmpRectForPaintAPI;
    private RectF mTmpRectForPaintAPI;
    private Rect mTmpRectForPrecompute;
    private Rect mTmpRectForPrecompute;


    // Recycling object for Paint APIs. Do not use outside getRunAdvances method.
    private Paint.RunInfo mRunInfo;

    public static final class LineInfo {
        private int mClusterCount;

        public int getClusterCount() {
            return mClusterCount;
        }

        public void setClusterCount(int clusterCount) {
            mClusterCount = clusterCount;
        }
    };

    private boolean mUseFallbackExtent = false;
    private boolean mUseFallbackExtent = false;


    // The start and end of a potentially existing ellipsis on this text line.
    // The start and end of a potentially existing ellipsis on this text line.
@@ -270,7 +285,7 @@ public class TextLine {
            // width.
            // width.
            return;
            return;
        }
        }
        final float width = Math.abs(measure(end, false, null, null));
        final float width = Math.abs(measure(end, false, null, null, null));
        mAddedWidthForJustify = (justifyWidth - width) / spaces;
        mAddedWidthForJustify = (justifyWidth - width) / spaces;
        mIsJustifying = true;
        mIsJustifying = true;
    }
    }
@@ -315,10 +330,12 @@ public class TextLine {
     * @param drawBounds output parameter for drawing bounding box. optional.
     * @param drawBounds output parameter for drawing bounding box. optional.
     * @param returnDrawWidth true for returning width of the bounding box, false for returning
     * @param returnDrawWidth true for returning width of the bounding box, false for returning
     *                       total advances.
     *                       total advances.
     * @param lineInfo an optional output parameter for filling line information.
     * @return the signed width of the line
     * @return the signed width of the line
     */
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth) {
    public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth,
            @Nullable LineInfo lineInfo) {
        if (returnDrawWidth) {
        if (returnDrawWidth) {
            if (drawBounds == null) {
            if (drawBounds == null) {
                if (mTmpRectForMeasure == null) {
                if (mTmpRectForMeasure == null) {
@@ -327,7 +344,7 @@ public class TextLine {
                drawBounds = mTmpRectForMeasure;
                drawBounds = mTmpRectForMeasure;
            }
            }
            drawBounds.setEmpty();
            drawBounds.setEmpty();
            float w = measure(mLen, false, fmi, drawBounds);
            float w = measure(mLen, false, fmi, drawBounds, lineInfo);
            float boundsWidth = drawBounds.width();
            float boundsWidth = drawBounds.width();
            if (Math.abs(w) > boundsWidth) {
            if (Math.abs(w) > boundsWidth) {
                return w;
                return w;
@@ -337,7 +354,7 @@ public class TextLine {
                return Math.signum(w) * boundsWidth;
                return Math.signum(w) * boundsWidth;
            }
            }
        } else {
        } else {
            return measure(mLen, false, fmi, drawBounds);
            return measure(mLen, false, fmi, drawBounds, lineInfo);
        }
        }
    }
    }


@@ -407,12 +424,13 @@ public class TextLine {
     *                 the edge of the preceding run's edge. See example above.
     *                 the edge of the preceding run's edge. See example above.
     * @param fmi receives metrics information about the requested character, can be null
     * @param fmi receives metrics information about the requested character, can be null
     * @param drawBounds output parameter for drawing bounding box. optional.
     * @param drawBounds output parameter for drawing bounding box. optional.
     * @param lineInfo an optional output parameter for filling line information.
     * @return the signed graphical offset from the leading margin to the requested character edge.
     * @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
     *         The positive value means the offset is right from the leading edge. The negative
     *         value means the offset is left from the leading edge.
     *         value means the offset is left from the leading edge.
     */
     */
    public float measure(@IntRange(from = 0) int offset, boolean trailing,
    public float measure(@IntRange(from = 0) int offset, boolean trailing,
            @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds) {
            @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable LineInfo lineInfo) {
        if (offset > mLen) {
        if (offset > mLen) {
            throw new IndexOutOfBoundsException(
            throw new IndexOutOfBoundsException(
                    "offset(" + offset + ") should be less than line limit(" + mLen + ")");
                    "offset(" + offset + ") should be less than line limit(" + mLen + ")");
@@ -437,16 +455,16 @@ public class TextLine {


                    if (targetIsInThisSegment && sameDirection) {
                    if (targetIsInThisSegment && sameDirection) {
                        return h + measureRun(segStart, offset, j, runIsRtl, fmi, drawBounds, null,
                        return h + measureRun(segStart, offset, j, runIsRtl, fmi, drawBounds, null,
                                0, h);
                                0, h, lineInfo);
                    }
                    }


                    final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi, drawBounds,
                    final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi, drawBounds,
                            null, 0, h);
                            null, 0, h, lineInfo);
                    h += sameDirection ? segmentWidth : -segmentWidth;
                    h += sameDirection ? segmentWidth : -segmentWidth;


                    if (targetIsInThisSegment) {
                    if (targetIsInThisSegment) {
                        return h + measureRun(segStart, offset, j, runIsRtl, null, null,  null, 0,
                        return h + measureRun(segStart, offset, j, runIsRtl, null, null,  null, 0,
                                h);
                                h, lineInfo);
                    }
                    }


                    if (j != runLimit) {  // charAt(j) == TAB_CHAR
                    if (j != runLimit) {  // charAt(j) == TAB_CHAR
@@ -537,7 +555,8 @@ public class TextLine {
                    final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
                    final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;


                    final float segmentWidth =
                    final float segmentWidth =
                            measureRun(segStart, j, j, runIsRtl, null, null, advances, segStart, 0);
                            measureRun(segStart, j, j, runIsRtl, null, null, advances, segStart, 0,
                                    null);


                    final float oldh = h;
                    final float oldh = h;
                    h += sameDirection ? segmentWidth : -segmentWidth;
                    h += sameDirection ? segmentWidth : -segmentWidth;
@@ -578,7 +597,7 @@ public class TextLine {
    }
    }


    /**
    /**
     * @see #measure(int, boolean, FontMetricsInt, RectF)
     * @see #measure(int, boolean, FontMetricsInt, RectF, LineInfo)
     * @return The measure results for all possible offsets
     * @return The measure results for all possible offsets
     */
     */
    @VisibleForTesting
    @VisibleForTesting
@@ -610,7 +629,7 @@ public class TextLine {
                    final float previousSegEndHorizontal = measurement[segStart];
                    final float previousSegEndHorizontal = measurement[segStart];
                    final float width =
                    final float width =
                            measureRun(segStart, j, j, runIsRtl, fmi, null, measurement, segStart,
                            measureRun(segStart, j, j, runIsRtl, fmi, null, measurement, segStart,
                                    0);
                                    0, null);
                    horizontal += sameDirection ? width : -width;
                    horizontal += sameDirection ? width : -width;


                    float currHorizontal = sameDirection ? oldHorizontal : horizontal;
                    float currHorizontal = sameDirection ? oldHorizontal : horizontal;
@@ -675,14 +694,14 @@ public class TextLine {
            boolean needWidth) {
            boolean needWidth) {


        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
            float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0);
            float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0, null);
            handleRun(start, limit, limit, runIsRtl, c, null, x + w, top,
            handleRun(start, limit, limit, runIsRtl, c, null, x + w, top,
                    y, bottom, null, null, false, null, 0);
                    y, bottom, null, null, false, null, 0, null);
            return w;
            return w;
        }
        }


        return handleRun(start, limit, limit, runIsRtl, c, null, x, top,
        return handleRun(start, limit, limit, runIsRtl, c, null, x, top,
                y, bottom, null, null, needWidth, null, 0);
                y, bottom, null, null, needWidth, null, 0, null);
    }
    }


    /**
    /**
@@ -698,19 +717,20 @@ public class TextLine {
     * @param advances receives the advance information about the requested run, can be null.
     * @param advances receives the advance information about the requested run, can be null.
     * @param advancesIndex the start index to fill in the advance information.
     * @param advancesIndex the start index to fill in the advance information.
     * @param x horizontal offset of the run.
     * @param x horizontal offset of the run.
     * @param lineInfo an optional output parameter for filling line information.
     * @return the signed width from the start of the run to the leading edge
     * @return the signed width from the start of the run to the leading edge
     * of the character at offset, based on the run (not paragraph) direction
     * of the character at offset, based on the run (not paragraph) direction
     */
     */
    private float measureRun(int start, int offset, int limit, boolean runIsRtl,
    private float measureRun(int start, int offset, int limit, boolean runIsRtl,
            @Nullable FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable float[] advances,
            @Nullable FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable float[] advances,
            int advancesIndex, float x) {
            int advancesIndex, float x, @Nullable LineInfo lineInfo) {
        if (drawBounds != null && (mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
        if (drawBounds != null && (mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
            float w = -measureRun(start, offset, limit, runIsRtl, null, null, null, 0, 0);
            float w = -measureRun(start, offset, limit, runIsRtl, null, null, null, 0, 0, null);
            return handleRun(start, offset, limit, runIsRtl, null, null, x + w, 0, 0, 0, fmi,
            return handleRun(start, offset, limit, runIsRtl, null, null, x + w, 0, 0, 0, fmi,
                    drawBounds, true, advances, advancesIndex);
                    drawBounds, true, advances, advancesIndex, lineInfo);
        }
        }
        return handleRun(start, offset, limit, runIsRtl, null, null, x, 0, 0, 0, fmi, drawBounds,
        return handleRun(start, offset, limit, runIsRtl, null, null, x, 0, 0, 0, fmi, drawBounds,
                true, advances, advancesIndex);
                true, advances, advancesIndex, lineInfo);
    }
    }


    /**
    /**
@@ -729,14 +749,14 @@ public class TextLine {
            int limit, boolean runIsRtl, float x, boolean needWidth) {
            int limit, boolean runIsRtl, float x, boolean needWidth) {


        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
            float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0);
            float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0, null);
            handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, null,
            handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, null,
                    false, null, 0);
                    false, null, 0, null);
            return w;
            return w;
        }
        }


        return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null, null,
        return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null, null,
                needWidth, null, 0);
                needWidth, null, 0, null);
    }
    }




@@ -1077,16 +1097,35 @@ public class TextLine {


    private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd,
    private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd,
            boolean runIsRtl, int offset, @Nullable float[] advances, int advancesIndex,
            boolean runIsRtl, int offset, @Nullable float[] advances, int advancesIndex,
            RectF drawingBounds) {
            RectF drawingBounds, @Nullable LineInfo lineInfo) {
        if (lineInfo != null) {
            if (mRunInfo == null) {
                mRunInfo = new Paint.RunInfo();
            }
            mRunInfo.setClusterCount(0);
        } else {
            mRunInfo = null;
        }
        if (mCharsValid) {
        if (mCharsValid) {
            return wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd,
            float r = wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd,
                    runIsRtl, offset, advances, advancesIndex, drawingBounds);
                    runIsRtl, offset, advances, advancesIndex, drawingBounds, mRunInfo);
            if (lineInfo != null) {
                lineInfo.setClusterCount(lineInfo.getClusterCount() + mRunInfo.getClusterCount());
            }
            return r;
        } else {
        } else {
            final int delta = mStart;
            final int delta = mStart;
            if (mComputed == null || advances != null) {
            // TODO: Add cluster information to the PrecomputedText for better performance of
                return wp.getRunCharacterAdvance(mText, delta + start, delta + end,
            // justification.
            if (mComputed == null || advances != null || lineInfo != null) {
                float r = wp.getRunCharacterAdvance(mText, delta + start, delta + end,
                        delta + contextStart, delta + contextEnd, runIsRtl,
                        delta + contextStart, delta + contextEnd, runIsRtl,
                        delta + offset, advances, advancesIndex, drawingBounds);
                        delta + offset, advances, advancesIndex, drawingBounds, mRunInfo);
                if (lineInfo != null) {
                    lineInfo.setClusterCount(
                            lineInfo.getClusterCount() + mRunInfo.getClusterCount());
                }
                return r;
            } else {
            } else {
                if (drawingBounds != null) {
                if (drawingBounds != null) {
                    if (mTmpRectForPrecompute == null) {
                    if (mTmpRectForPrecompute == null) {
@@ -1120,6 +1159,7 @@ public class TextLine {
     * @param decorations the list of locations and paremeters for drawing decorations
     * @param decorations the list of locations and paremeters for drawing decorations
     * @param advances receives the advance information about the requested run, can be null.
     * @param advances receives the advance information about the requested run, can be null.
     * @param advancesIndex the start index to fill in the advance information.
     * @param advancesIndex the start index to fill in the advance information.
     * @param lineInfo an optional output parameter for filling line information.
     * @return the signed width of the run based on the run direction; only
     * @return the signed width of the run based on the run direction; only
     * valid if needWidth is true
     * valid if needWidth is true
     */
     */
@@ -1128,7 +1168,7 @@ public class TextLine {
            Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom,
            Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom,
            FontMetricsInt fmi, RectF drawBounds, boolean needWidth, int offset,
            FontMetricsInt fmi, RectF drawBounds, boolean needWidth, int offset,
            @Nullable ArrayList<DecorationInfo> decorations,
            @Nullable ArrayList<DecorationInfo> decorations,
            @Nullable float[] advances, int advancesIndex) {
            @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) {


        if (mIsJustifying) {
        if (mIsJustifying) {
            wp.setWordSpacing(mAddedWidthForJustify);
            wp.setWordSpacing(mAddedWidthForJustify);
@@ -1155,7 +1195,8 @@ public class TextLine {
                mTmpRectForPaintAPI = new RectF();
                mTmpRectForPaintAPI = new RectF();
            }
            }
            totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset,
            totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset,
                    advances, advancesIndex, drawBounds == null ? null : mTmpRectForPaintAPI);
                    advances, advancesIndex, drawBounds == null ? null : mTmpRectForPaintAPI,
                    lineInfo);
            if (drawBounds != null) {
            if (drawBounds != null) {
                if (runIsRtl) {
                if (runIsRtl) {
                    mTmpRectForPaintAPI.offset(x - totalWidth, 0);
                    mTmpRectForPaintAPI.offset(x - totalWidth, 0);
@@ -1206,9 +1247,9 @@ public class TextLine {
                    final int decorationStart = Math.max(info.start, start);
                    final int decorationStart = Math.max(info.start, start);
                    final int decorationEnd = Math.min(info.end, offset);
                    final int decorationEnd = Math.min(info.end, offset);
                    float decorationStartAdvance = getRunAdvance(wp, start, end, contextStart,
                    float decorationStartAdvance = getRunAdvance(wp, start, end, contextStart,
                            contextEnd, runIsRtl, decorationStart, null, 0, null);
                            contextEnd, runIsRtl, decorationStart, null, 0, null, null);
                    float decorationEndAdvance = getRunAdvance(wp, start, end, contextStart,
                    float decorationEndAdvance = getRunAdvance(wp, start, end, contextStart,
                            contextEnd, runIsRtl, decorationEnd, null, 0, null);
                            contextEnd, runIsRtl, decorationEnd, null, 0, null, null);
                    final float decorationXLeft, decorationXRight;
                    final float decorationXLeft, decorationXRight;
                    if (runIsRtl) {
                    if (runIsRtl) {
                        decorationXLeft = rightX - decorationEndAdvance;
                        decorationXLeft = rightX - decorationEndAdvance;
@@ -1377,6 +1418,7 @@ public class TextLine {
     * @param needWidth true if the width is required
     * @param needWidth true if the width is required
     * @param advances receives the advance information about the requested run, can be null.
     * @param advances receives the advance information about the requested run, can be null.
     * @param advancesIndex the start index to fill in the advance information.
     * @param advancesIndex the start index to fill in the advance information.
     * @param lineInfo an optional output parameter for filling line information.
     * @return the signed width of the run based on the run direction; only
     * @return the signed width of the run based on the run direction; only
     * valid if needWidth is true
     * valid if needWidth is true
     */
     */
@@ -1384,7 +1426,7 @@ public class TextLine {
            int limit, boolean runIsRtl, Canvas c,
            int limit, boolean runIsRtl, Canvas c,
            TextShaper.GlyphsConsumer consumer, float x, int top, int y,
            TextShaper.GlyphsConsumer consumer, float x, int top, int y,
            int bottom, FontMetricsInt fmi, RectF drawBounds, boolean needWidth,
            int bottom, FontMetricsInt fmi, RectF drawBounds, boolean needWidth,
            @Nullable float[] advances, int advancesIndex) {
            @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) {


        if (measureLimit < start || measureLimit > limit) {
        if (measureLimit < start || measureLimit > limit) {
            throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of "
            throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of "
@@ -1431,7 +1473,7 @@ public class TextLine {
            wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
            wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
            return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top,
            return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top,
                    y, bottom, fmi, drawBounds, needWidth, measureLimit, null, advances,
                    y, bottom, fmi, drawBounds, needWidth, measureLimit, null, advances,
                    advancesIndex);
                    advancesIndex, lineInfo);
        }
        }


        // Shaping needs to take into account context up to metric boundaries,
        // Shaping needs to take into account context up to metric boundaries,
@@ -1523,7 +1565,7 @@ public class TextLine {
                            consumer, x, top, y, bottom, fmi, drawBounds,
                            consumer, x, top, y, bottom, fmi, drawBounds,
                            needWidth || activeEnd < measureLimit,
                            needWidth || activeEnd < measureLimit,
                            Math.min(activeEnd, mlimit), mDecorations,
                            Math.min(activeEnd, mlimit), mDecorations,
                            advances, advancesIndex + activeStart - start);
                            advances, advancesIndex + activeStart - start, lineInfo);


                    activeStart = j;
                    activeStart = j;
                    activePaint.set(wp);
                    activePaint.set(wp);
@@ -1551,7 +1593,7 @@ public class TextLine {
            x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x,
            x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x,
                    top, y, bottom, fmi, drawBounds, needWidth || activeEnd < measureLimit,
                    top, y, bottom, fmi, drawBounds, needWidth || activeEnd < measureLimit,
                    Math.min(activeEnd, mlimit), mDecorations,
                    Math.min(activeEnd, mlimit), mDecorations,
                    advances, advancesIndex + activeStart - start);
                    advances, advancesIndex + activeStart - start, lineInfo);
        }
        }


        return x - originalX;
        return x - originalX;
+41 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading