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

Commit 0d6e4b90 authored by Haoyu Zhang's avatar Haoyu Zhang Committed by Android (Google) Code Review
Browse files

Merge "Introduce TextLine#measureAllbounds"

parents 39dfa886 c8abba49
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -14929,6 +14929,8 @@ package android.graphics {
    method public android.graphics.PathEffect getPathEffect();
    method public float getRunAdvance(char[], int, int, int, int, boolean, int);
    method public float getRunAdvance(CharSequence, int, int, int, int, boolean, int);
    method public float getRunCharacterAdvance(@NonNull char[], int, int, int, int, boolean, int, @Nullable float[], int);
    method public float getRunCharacterAdvance(@NonNull CharSequence, int, int, int, int, boolean, int, @Nullable float[], int);
    method public android.graphics.Shader getShader();
    method @ColorInt public int getShadowLayerColor();
    method @ColorLong public long getShadowLayerColorLong();
+169 −31
Original line number Diff line number Diff line
@@ -408,14 +408,14 @@ public class TextLine {
                    final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;

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

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

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

                    if (j != runLimit) {  // charAt(j) == TAB_CHAR
@@ -436,6 +436,116 @@ public class TextLine {
        return h;
    }

    /**
     * Return the signed horizontal bounds of the characters in the line.
     *
     * The length of the returned array equals to 2 * mLen. The left bound of the i th character
     * is stored at index 2 * i. And the right bound of the i th character is stored at index
     * (2 * i + 1).
     *
     * Check the following examples. 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
     *   Input Text (visual)   :   L0 L1 L2 L3
     *   Output :  [0em, 1em, 1em, 2em, 2em, 3em, 3em, 4em]
     *
     * Example 2: All RTL chars within RTL context.
     *   Input Text (logical)  :   R0 R1 R2 R3
     *   Input Text (visual)   :   R3 R2 R1 R0
     *   Output :  [-1em, 0em, -2em, -1em, -3em, -2em, -4em, -3em]

     *
     * Example 3: BiDi chars within LTR context.
     *   Input Text (logical)  :   L0 L1 R2 R3 L4 L5
     *   Input Text (visual)   :   L0 L1 R3 R2 L4 L5
     *   Output :  [0em, 1em, 1em, 2em, 3em, 4em, 2em, 3em, 4em, 5em, 5em, 6em]

     *
     * Example 4: BiDi chars within RTL context.
     *   Input Text (logical)  :   L0 L1 R2 R3 L4 L5
     *   Input Text (visual)   :   L4 L5 R3 R2 L0 L1
     *   Output :  [-2em, -1em, -1em, 0em, -3em, -2em, -4em, -3em, -6em, -5em, -5em, -4em]
     *
     * @param bounds the array to receive the character bounds data. Its length should be at least
     *               2 times of the line length.
     * @param advances the array to receive the character advance data, nullable. If provided, its
     *                 length should be equal or larger than the line length.
     *
     * @throws IllegalArgumentException if the given {@code bounds} is null.
     * @throws IndexOutOfBoundsException if the given {@code bounds} or {@code advances} doesn't
     * have enough space to hold the result.
     */
    public void measureAllBounds(@NonNull float[] bounds, @Nullable float[] advances) {
        if (bounds == null) {
            throw new IllegalArgumentException("bounds can't be null");
        }
        if (bounds.length < 2 * mLen) {
            throw new IndexOutOfBoundsException("bounds doesn't have enough space to receive the "
                    + "result, needed: " + (2 * mLen) + " had: " + bounds.length);
        }
        if (advances == null) {
            advances = new float[mLen];
        }
        if (advances.length < mLen) {
            throw new IndexOutOfBoundsException("advance doesn't have enough space to receive the "
                    + "result, needed: " + mLen + " had: " + advances.length);
        }
        float h = 0;
        for (int runIndex = 0; runIndex < mDirections.getRunCount(); runIndex++) {
            final int runStart = mDirections.getRunStart(runIndex);
            if (runStart > mLen) break;
            final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
            final boolean runIsRtl = mDirections.isRunRtl(runIndex);

            int segStart = runStart;
            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
                if (j == runLimit || charAt(j) == TAB_CHAR) {
                    final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;

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

                    final float oldh = h;
                    h += sameDirection ? segmentWidth : -segmentWidth;
                    float currh = sameDirection ? oldh : h;
                    for (int offset = segStart; offset < j && offset < mLen; ++offset) {
                        if (runIsRtl) {
                            bounds[2 * offset + 1] = currh;
                            currh -= advances[offset];
                            bounds[2 * offset] = currh;
                        } else {
                            bounds[2 * offset] = currh;
                            currh += advances[offset];
                            bounds[2 * offset + 1] = currh;
                        }
                    }

                    if (j != runLimit) {  // charAt(j) == TAB_CHAR
                        final float leftX;
                        final float rightX;
                        if (runIsRtl) {
                            rightX = h;
                            h = mDir * nextTab(h * mDir);
                            leftX = h;
                        } else {
                            leftX = h;
                            h = mDir * nextTab(h * mDir);
                            rightX = h;
                        }
                        bounds[2 * j] = leftX;
                        bounds[2 * j + 1] = rightX;
                        advances[j] = rightX - leftX;
                    }

                    segStart = j + 1;
                }
            }
        }
    }

    /**
     * @see #measure(int, boolean, FontMetricsInt)
     * @return The measure results for all possible offsets
@@ -464,15 +574,15 @@ public class TextLine {
                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);
                    final float w = measureRun(segStart, j, j, runIsRtl, fmi, null, 0);
                    h += advance ? w : -w;

                    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) {
                            measurement[offset] =
                                    baseh + measureRun(segStart, offset, j, runIsRtl, crtfmi);
                            measurement[offset] = baseh
                                    + measureRun(segStart, offset, j, runIsRtl, crtfmi, null, 0);
                        }
                    }

@@ -518,14 +628,14 @@ public class TextLine {
            boolean needWidth) {

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

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

    /**
@@ -538,12 +648,15 @@ public class TextLine {
     * @param runIsRtl true if the run is right-to-left
     * @param fmi receives metrics 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.
     * @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,
            FontMetricsInt fmi) {
        return handleRun(start, offset, limit, runIsRtl, null, null, 0, 0, 0, 0, fmi, true);
            @Nullable FontMetricsInt fmi, @Nullable float[] advances, int advancesIndex) {
        return handleRun(start, offset, limit, runIsRtl, null, null, 0, 0, 0, 0, fmi, true,
                advances, advancesIndex);
    }

    /**
@@ -562,13 +675,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);
            handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, false);
            float w = -measureRun(start, limit, limit, runIsRtl, null, null, 0);
            handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null,
                    false, null, 0);
            return w;
        }

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


@@ -908,14 +1022,16 @@ public class TextLine {
    }

    private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd,
            boolean runIsRtl, int offset) {
            boolean runIsRtl, int offset, @Nullable float[] advances, int advancesIndex) {
        if (mCharsValid) {
            return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset);
            return wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd,
                    runIsRtl, offset, advances, advancesIndex);
        } else {
            final int delta = mStart;
            if (mComputed == null) {
                return wp.getRunAdvance(mText, delta + start, delta + end,
                        delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
            if (mComputed == null || advances != null) {
                return wp.getRunCharacterAdvance(mText, delta + start, delta + end,
                        delta + contextStart, delta + contextEnd, runIsRtl,
                        delta + offset, advances, advancesIndex);
            } else {
                return mComputed.getWidth(start + delta, end + delta);
            }
@@ -940,6 +1056,8 @@ public class TextLine {
     * @param needWidth true if the width of the run is needed
     * @param offset the offset for the purpose of measuring
     * @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.
     * @return the signed width of the run based on the run direction; only
     * valid if needWidth is true
     */
@@ -947,7 +1065,8 @@ public class TextLine {
            int contextStart, int contextEnd, boolean runIsRtl,
            Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom,
            FontMetricsInt fmi, boolean needWidth, int offset,
            @Nullable ArrayList<DecorationInfo> decorations) {
            @Nullable ArrayList<DecorationInfo> decorations,
            @Nullable float[] advances, int advancesIndex) {

        if (mIsJustifying) {
            wp.setWordSpacing(mAddedWidthForJustify);
@@ -967,7 +1086,8 @@ public class TextLine {
        final int numDecorations = decorations == null ? 0 : decorations.size();
        if (needWidth || ((c != null || consumer != null) && (wp.bgColor != 0
                || numDecorations != 0 || runIsRtl))) {
            totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset);
            totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset,
                    advances, advancesIndex);
        }

        final float leftX, rightX;
@@ -1009,10 +1129,10 @@ 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);
                    float decorationEndAdvance = getRunAdvance(
                            wp, start, end, contextStart, contextEnd, runIsRtl, decorationEnd);
                    float decorationStartAdvance = getRunAdvance(wp, start, end, contextStart,
                            contextEnd, runIsRtl, decorationStart, null, 0);
                    float decorationEndAdvance = getRunAdvance(wp, start, end, contextStart,
                            contextEnd, runIsRtl, decorationEnd, null, 0);
                    final float decorationXLeft, decorationXRight;
                    if (runIsRtl) {
                        decorationXLeft = rightX - decorationEndAdvance;
@@ -1179,19 +1299,27 @@ public class TextLine {
     * @param bottom the bottom of the line
     * @param fmi receives metrics information, can be null
     * @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.
     * @return the signed width of the run based on the run direction; only
     * valid if needWidth is true
     */
    private float handleRun(int start, int measureLimit,
            int limit, boolean runIsRtl, Canvas c,
            TextShaper.GlyphsConsumer consumer, float x, int top, int y,
            int bottom, FontMetricsInt fmi, boolean needWidth) {
            int bottom, FontMetricsInt fmi, boolean needWidth,
            @Nullable float[] advances, int advancesIndex) {

        if (measureLimit < start || measureLimit > limit) {
            throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of "
                    + "start (" + start + ") and limit (" + limit + ") bounds");
        }

        if (advances != null && advances.length - advancesIndex < measureLimit - start) {
            throw new IndexOutOfBoundsException("advances doesn't have enough space to receive the "
                    + "result");
        }

        // Case of an empty line, make sure we update fmi according to mPaint
        if (start == measureLimit) {
            final TextPaint wp = mWorkPaint;
@@ -1218,7 +1346,7 @@ public class TextLine {
            wp.setStartHyphenEdit(adjustStartHyphenEdit(start, wp.getStartHyphenEdit()));
            wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
            return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top,
                    y, bottom, fmi, needWidth, measureLimit, null);
                    y, bottom, fmi, needWidth, measureLimit, null, advances, advancesIndex);
        }

        // Shaping needs to take into account context up to metric boundaries,
@@ -1257,8 +1385,16 @@ public class TextLine {
            }

            if (replacement != null) {
                x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y,
                        bottom, fmi, needWidth || mlimit < measureLimit);
                final float width = handleReplacement(replacement, wp, i, mlimit, runIsRtl, c,
                        x, top, y, bottom, fmi, needWidth || mlimit < measureLimit);
                x += width;
                if (advances != null) {
                    // For replacement, the entire width is assigned to the first character.
                    advances[advancesIndex + i - start] = runIsRtl ? -width : width;
                    for (int j = i + 1; j < mlimit; ++j) {
                        advances[advancesIndex + j - start] = 0.0f;
                    }
                }
                continue;
            }

@@ -1300,7 +1436,8 @@ public class TextLine {
                            adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
                    x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c,
                            consumer, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
                            Math.min(activeEnd, mlimit), mDecorations);
                            Math.min(activeEnd, mlimit), mDecorations,
                            advances, advancesIndex + activeStart - start);

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

        return x - originalX;
+265 −3

File changed.

Preview size limit exceeded, changes collapsed.

+125 −0
Original line number Diff line number Diff line
@@ -3136,6 +3136,128 @@ public class Paint {
        return result;
    }


    /**
     * Measure the advance of each character within a run of text and also return the cursor
     * position within the run.
     *
     * @see #getRunAdvance(char[], int, int, int, int, boolean, int) for more details.
     *
     * @param text the text to measure. Cannot be null.
     * @param start the index of the start of the range to measure
     * @param end the index + 1 of the end of the range to measure
     * @param contextStart the index of the start of the shaping context
     * @param contextEnd the index + 1 of the end of the shaping context
     * @param isRtl whether the run is in RTL direction
     * @param offset index of caret position
     * @param advances the array that receives the computed character advances
     * @param advancesIndex the start index from which the advances array is filled
     * @return width measurement between start and offset
     * @throws IndexOutOfBoundsException if a) contextStart or contextEnd is out of array's range
     * or contextStart is larger than contextEnd,
     * b) start or end is not within the range [contextStart, contextEnd), or start is larger than
     * end,
     * c) offset is not within the range [start, end),
     * d) advances.length - advanceIndex is smaller than the length of the run, which equals to
     * end - start.
     *
     */
    public float getRunCharacterAdvance(@NonNull char[] text, int start, int end, int contextStart,
            int contextEnd, boolean isRtl, int offset,
            @Nullable float[] advances, int advancesIndex) {
        if (text == null) {
            throw new IllegalArgumentException("text cannot be null");
        }
        if (contextStart < 0 || contextEnd > text.length) {
            throw new IndexOutOfBoundsException("Invalid Context Range: " + contextStart + ", "
                    + contextEnd + " must be in 0, " + text.length);
        }

        if (start < contextStart || contextEnd < end) {
            throw new IndexOutOfBoundsException("Invalid start/end range: " + start + ", " + end
                    + " must be in " + contextStart + ", " + contextEnd);
        }

        if (offset < start || end < offset) {
            throw new IndexOutOfBoundsException("Invalid offset position: " + offset
                    + " must be in " + start + ", " + end);
        }

        if (advances != null && advances.length < advancesIndex - start + end) {
            throw new IndexOutOfBoundsException("Given array doesn't have enough space to receive "
                    + "the result, advances.length: " + advances.length + " advanceIndex: "
                    + advancesIndex + " needed space: " + (offset - start));
        }

        if (end == start) {
            return 0.0f;
        }

        return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd,
                isRtl, offset, advances, advancesIndex);
    }

    /**
     * @see #getRunCharacterAdvance(char[], int, int, int, int, boolean, int, float[], int)
     *
     * @param text the text to measure. Cannot be null.
     * @param start the index of the start of the range to measure
     * @param end the index + 1 of the end of the range to measure
     * @param contextStart the index of the start of the shaping context
     * @param contextEnd the index + 1 of the end of the shaping context
     * @param isRtl whether the run is in RTL direction
     * @param offset index of caret position
     * @param advances the array that receives the computed character advances
     * @param advancesIndex the start index from which the advances array is filled
     * @return width measurement between start and offset
     * @throws IndexOutOfBoundsException if a) contextStart or contextEnd is out of array's range
     * or contextStart is larger than contextEnd,
     * b) start or end is not within the range [contextStart, contextEnd), or end is larger than
     * start,
     * c) offset is not within the range [start, end),
     * d) advances.length - advanceIndex is smaller than the run length, which equals to
     * end - start.
     */
    public float getRunCharacterAdvance(@NonNull CharSequence text, int start, int end,
            int contextStart, int contextEnd, boolean isRtl, int offset,
            @Nullable float[] advances, int advancesIndex) {
        if (text == null) {
            throw new IllegalArgumentException("text cannot be null");
        }
        if (contextStart < 0 || contextEnd > text.length()) {
            throw new IndexOutOfBoundsException("Invalid Context Range: " + contextStart + ", "
                    + contextEnd + " must be in 0, " + text.length());
        }

        if (start < contextStart || contextEnd < end) {
            throw new IndexOutOfBoundsException("Invalid start/end range: " + start + ", " + end
                    + " must be in " + contextStart + ", " + contextEnd);
        }

        if (offset < start || end < offset) {
            throw new IndexOutOfBoundsException("Invalid offset position: " + offset
                    + " must be in " + start + ", " + end);
        }

        if (advances != null && advances.length < advancesIndex - start + end) {
            throw new IndexOutOfBoundsException("Given array doesn't have enough space to receive "
                    + "the result, advances.length: " + advances.length + " advanceIndex: "
                    + advancesIndex + " needed space: " + (offset - start));
        }

        if (end == start) {
            return 0.0f;
        }

        char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart);
        TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
        final float result = getRunCharacterAdvance(buf, start - contextStart, end - contextStart,
                0, contextEnd - contextStart, isRtl, offset - contextStart,
                advances, advancesIndex);
        TemporaryBuffer.recycle(buf);
        return result;
    }

    /**
     * Get the character offset within the string whose position is closest to the specified
     * horizontal position.
@@ -3247,6 +3369,9 @@ public class Paint {
    private static native boolean nHasGlyph(long paintPtr, int bidiFlags, String string);
    private static native float nGetRunAdvance(long paintPtr, char[] text, int start, int end,
            int contextStart, int contextEnd, boolean isRtl, int offset);
    private static native float nGetRunCharacterAdvance(long paintPtr, char[] text, int start,
            int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances,
            int advancesIndex);
    private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
            int contextStart, int contextEnd, boolean isRtl, float advance);
    private static native void nGetFontMetricsIntForText(long paintPtr, char[] text,
+137 −111

File changed.

Preview size limit exceeded, changes collapsed.