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

Commit 9255c75e authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[2nd] Add cluster count API" into main

parents 6b06705b dd61718d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -47294,6 +47294,7 @@ package android.text {
    method public int getLineForOffset(int);
    method public int getLineForVertical(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 getLineRight(int);
    method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingAmount();
+2 −2
Original line number 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,
                    Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
                    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);
        }

@@ -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 /* ellipsisEnd, 0 since text has not been ellipsized at this point */,
                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);

        return fm;
+80 −10
Original line number 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_USE_BOUNDS_FOR_WIDTH;
import static com.android.text.flags.Flags.FLAG_INTER_CHARACTER_JUSTIFICATION;

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

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

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

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

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

        if (clamped && wid > mWidth) {
@@ -1792,11 +1796,68 @@ public abstract class Layout {
        if (isJustificationRequired(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);
        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
     * leading margin.  If full is false, excludes trailing whitespace.
@@ -1823,7 +1884,7 @@ public abstract class Layout {
        if (isJustificationRequired(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);
        return width;
    }
@@ -2432,15 +2493,22 @@ public abstract class Layout {
     * is not counted) on the specified 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;
        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) {
                return end;
            }
        }

        for (; end > start; end--) {
            ch = text.charAt(end - 1);
@@ -2939,7 +3007,7 @@ public abstract class Layout {
            tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops,
                    0 /* ellipsisStart */, 0 /* ellipsisEnd */,
                    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 {
            TextLine.recycle(tl);
            if (mt != null) {
@@ -3337,6 +3405,8 @@ public abstract class Layout {
    private boolean mUseBoundsForWidth;
    private @Nullable Paint.FontMetrics mMinimumFontMetrics;

    private TextLine.LineInfo mLineInfo = null;

    /** @hide */
    @IntDef(prefix = { "DIR_" }, value = {
            DIR_LEFT_TO_RIGHT,
+77 −35
Original line number Diff line number Diff line
@@ -76,6 +76,21 @@ public class TextLine {
    private RectF mTmpRectForPaintAPI;
    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;

    // The start and end of a potentially existing ellipsis on this text line.
@@ -270,7 +285,7 @@ public class TextLine {
            // width.
            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;
        mIsJustifying = true;
    }
@@ -315,10 +330,12 @@ public class TextLine {
     * @param drawBounds output parameter for drawing bounding box. optional.
     * @param returnDrawWidth true for returning width of the bounding box, false for returning
     *                       total advances.
     * @param lineInfo an optional output parameter for filling line information.
     * @return the signed width of the line
     */
    @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 (drawBounds == null) {
                if (mTmpRectForMeasure == null) {
@@ -327,7 +344,7 @@ public class TextLine {
                drawBounds = mTmpRectForMeasure;
            }
            drawBounds.setEmpty();
            float w = measure(mLen, false, fmi, drawBounds);
            float w = measure(mLen, false, fmi, drawBounds, lineInfo);
            float boundsWidth = drawBounds.width();
            if (Math.abs(w) > boundsWidth) {
                return w;
@@ -337,7 +354,7 @@ public class TextLine {
                return Math.signum(w) * boundsWidth;
            }
        } 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.
     * @param fmi receives metrics information about the requested character, can be null
     * @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.
     *         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(@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) {
            throw new IndexOutOfBoundsException(
                    "offset(" + offset + ") should be less than line limit(" + mLen + ")");
@@ -437,16 +455,16 @@ public class TextLine {

                    if (targetIsInThisSegment && sameDirection) {
                        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,
                            null, 0, h);
                            null, 0, h, lineInfo);
                    h += sameDirection ? segmentWidth : -segmentWidth;

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

                    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 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;
                    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
     */
    @VisibleForTesting
@@ -610,7 +629,7 @@ public class TextLine {
                    final float previousSegEndHorizontal = measurement[segStart];
                    final float width =
                            measureRun(segStart, j, j, runIsRtl, fmi, null, measurement, segStart,
                                    0);
                                    0, null);
                    horizontal += sameDirection ? width : -width;

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

        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,
                    y, bottom, null, null, false, null, 0);
                    y, bottom, null, null, false, null, 0, null);
            return w;
        }

        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 advancesIndex the start index to fill in the advance information.
     * @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
     * of the character at offset, based on the run (not paragraph) direction
     */
    private float measureRun(int start, int offset, int limit, boolean runIsRtl,
            @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) {
            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,
                    drawBounds, true, advances, advancesIndex);
                    drawBounds, true, advances, advancesIndex, lineInfo);
        }
        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) {

        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,
                    false, null, 0);
                    false, null, 0, null);
            return w;
        }

        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,
            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) {
            return wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd,
                    runIsRtl, offset, advances, advancesIndex, drawingBounds);
            float r = wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd,
                    runIsRtl, offset, advances, advancesIndex, drawingBounds, mRunInfo);
            if (lineInfo != null) {
                lineInfo.setClusterCount(lineInfo.getClusterCount() + mRunInfo.getClusterCount());
            }
            return r;
        } else {
            final int delta = mStart;
            if (mComputed == null || advances != null) {
                return wp.getRunCharacterAdvance(mText, delta + start, delta + end,
            // TODO: Add cluster information to the PrecomputedText for better performance of
            // justification.
            if (mComputed == null || advances != null || lineInfo != null) {
                float r = wp.getRunCharacterAdvance(mText, delta + start, delta + end,
                        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 {
                if (drawingBounds != null) {
                    if (mTmpRectForPrecompute == null) {
@@ -1120,6 +1159,7 @@ public class TextLine {
     * @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 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
     * 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,
            FontMetricsInt fmi, RectF drawBounds, boolean needWidth, int offset,
            @Nullable ArrayList<DecorationInfo> decorations,
            @Nullable float[] advances, int advancesIndex) {
            @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) {

        if (mIsJustifying) {
            wp.setWordSpacing(mAddedWidthForJustify);
@@ -1155,7 +1195,8 @@ public class TextLine {
                mTmpRectForPaintAPI = new RectF();
            }
            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 (runIsRtl) {
                    mTmpRectForPaintAPI.offset(x - totalWidth, 0);
@@ -1206,9 +1247,9 @@ public class TextLine {
                    final int decorationStart = Math.max(info.start, start);
                    final int decorationEnd = Math.min(info.end, offset);
                    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,
                            contextEnd, runIsRtl, decorationEnd, null, 0, null);
                            contextEnd, runIsRtl, decorationEnd, null, 0, null, null);
                    final float decorationXLeft, decorationXRight;
                    if (runIsRtl) {
                        decorationXLeft = rightX - decorationEndAdvance;
@@ -1377,6 +1418,7 @@ public class TextLine {
     * @param needWidth true if the width is required
     * @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 lineInfo an optional output parameter for filling line information.
     * @return the signed width of the run based on the run direction; only
     * valid if needWidth is true
     */
@@ -1384,7 +1426,7 @@ public class TextLine {
            int limit, boolean runIsRtl, Canvas c,
            TextShaper.GlyphsConsumer consumer, float x, int top, int y,
            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) {
            throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of "
@@ -1431,7 +1473,7 @@ public class TextLine {
            wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
            return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top,
                    y, bottom, fmi, drawBounds, needWidth, measureLimit, null, advances,
                    advancesIndex);
                    advancesIndex, lineInfo);
        }

        // 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,
                            needWidth || activeEnd < measureLimit,
                            Math.min(activeEnd, mlimit), mDecorations,
                            advances, advancesIndex + activeStart - start);
                            advances, advancesIndex + activeStart - start, lineInfo);

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

        return x - originalX;
+41 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading