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

Commit 4674e642 authored by Seigo Nonaka's avatar Seigo Nonaka
Browse files

Revert "Refactor MeasuredText"

This reverts commit 75492afb.

Reason for revert: 70146381

Change-Id: Ibb6433b5e02608326ef51cc16d8d5d8efa86beec
parent 75492afb
Loading
Loading
Loading
Loading
+0 −374
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.text;

import android.annotation.IntRange;
import android.annotation.NonNull;

import com.android.internal.util.ArrayUtils;

import libcore.util.EmptyArray;

/**
 * Implements a growing array of int primitives.
 *
 * These arrays are NOT thread safe.
 *
 * @hide
 */
public final class AutoGrowArray {
    private static final int MIN_CAPACITY_INCREMENT = 12;
    private static final int MAX_CAPACITY_TO_BE_KEPT = 10000;

    /**
     * Returns next capacity size.
     *
     * The returned capacity is larger than requested capacity.
     */
    private static int computeNewCapacity(int currentSize, int requested) {
        final int targetCapacity = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2)
                ?  MIN_CAPACITY_INCREMENT : currentSize >> 1);
        return targetCapacity > requested ? targetCapacity : requested;
    }

    /**
     * An auto growing byte array.
     */
    public static class ByteArray {

        private @NonNull byte[] mValues;
        private @IntRange(from = 0) int mSize;

        /**
         * Creates an empty ByteArray with the default initial capacity.
         */
        public ByteArray() {
            this(10);
        }

        /**
         * Creates an empty ByteArray with the specified initial capacity.
         */
        public ByteArray(@IntRange(from = 0) int initialCapacity) {
            if (initialCapacity == 0) {
                mValues = EmptyArray.BYTE;
            } else {
                mValues = ArrayUtils.newUnpaddedByteArray(initialCapacity);
            }
            mSize = 0;
        }

        /**
         * Changes the size of this ByteArray. If this ByteArray is shrinked, the backing array
         * capacity is unchanged.
         */
        public void resize(@IntRange(from = 0) int newSize) {
            if (newSize > mValues.length) {
                ensureCapacity(newSize - mSize);
            }
            mSize = newSize;
        }

        /**
         * Appends the specified value to the end of this array.
         */
        public void append(byte value) {
            ensureCapacity(1);
            mValues[mSize++] = value;
        }

        /**
         * Ensures capacity to append at least <code>count</code> values.
         */
        private void ensureCapacity(@IntRange int count) {
            final int requestedSize = mSize + count;
            if (requestedSize >= mValues.length) {
                final int newCapacity = computeNewCapacity(mSize, requestedSize);
                final byte[] newValues = ArrayUtils.newUnpaddedByteArray(newCapacity);
                System.arraycopy(mValues, 0, newValues, 0, mSize);
                mValues = newValues;
            }
        }

        /**
         * Removes all values from this array.
         */
        public void clear() {
            mSize = 0;
        }

        /**
         * Removes all values from this array and release the internal array object if it is too
         * large.
         */
        public void clearWithReleasingLargeArray() {
            clear();
            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
                mValues = EmptyArray.BYTE;
            }
        }

        /**
         * Returns the value at the specified position in this array.
         */
        public byte get(@IntRange(from = 0) int index) {
            return mValues[index];
        }

        /**
         * Sets the value at the specified position in this array.
         */
        public void set(@IntRange(from = 0) int index, byte value) {
            mValues[index] = value;
        }

        /**
         * Returns the number of values in this array.
         */
        public @IntRange(from = 0) int size() {
            return mSize;
        }

        /**
         * Returns internal raw array.
         *
         * Note that this array may have larger size than you requested.
         * Use size() instead for getting the actual array size.
         */
        public @NonNull byte[] getRawArray() {
            return mValues;
        }
    }

    /**
     * An auto growing int array.
     */
    public static class IntArray {

        private @NonNull int[] mValues;
        private @IntRange(from = 0) int mSize;

        /**
         * Creates an empty IntArray with the default initial capacity.
         */
        public IntArray() {
            this(10);
        }

        /**
         * Creates an empty IntArray with the specified initial capacity.
         */
        public IntArray(@IntRange(from = 0) int initialCapacity) {
            if (initialCapacity == 0) {
                mValues = EmptyArray.INT;
            } else {
                mValues = ArrayUtils.newUnpaddedIntArray(initialCapacity);
            }
            mSize = 0;
        }

        /**
         * Changes the size of this IntArray. If this IntArray is shrinked, the backing array
         * capacity is unchanged.
         */
        public void resize(@IntRange(from = 0) int newSize) {
            if (newSize > mValues.length) {
                ensureCapacity(newSize - mSize);
            }
            mSize = newSize;
        }

        /**
         * Appends the specified value to the end of this array.
         */
        public void append(int value) {
            ensureCapacity(1);
            mValues[mSize++] = value;
        }

        /**
         * Ensures capacity to append at least <code>count</code> values.
         */
        private void ensureCapacity(@IntRange(from = 0) int count) {
            final int requestedSize = mSize + count;
            if (requestedSize >= mValues.length) {
                final int newCapacity = computeNewCapacity(mSize, requestedSize);
                final int[] newValues = ArrayUtils.newUnpaddedIntArray(newCapacity);
                System.arraycopy(mValues, 0, newValues, 0, mSize);
                mValues = newValues;
            }
        }

        /**
         * Removes all values from this array.
         */
        public void clear() {
            mSize = 0;
        }

        /**
         * Removes all values from this array and release the internal array object if it is too
         * large.
         */
        public void clearWithReleasingLargeArray() {
            clear();
            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
                mValues = EmptyArray.INT;
            }
        }

        /**
         * Returns the value at the specified position in this array.
         */
        public int get(@IntRange(from = 0) int index) {
            return mValues[index];
        }

        /**
         * Sets the value at the specified position in this array.
         */
        public void set(@IntRange(from = 0) int index, int value) {
            mValues[index] = value;
        }

        /**
         * Returns the number of values in this array.
         */
        public @IntRange(from = 0) int size() {
            return mSize;
        }

        /**
         * Returns internal raw array.
         *
         * Note that this array may have larger size than you requested.
         * Use size() instead for getting the actual array size.
         */
        public @NonNull int[] getRawArray() {
            return mValues;
        }
    }

    /**
     * An auto growing float array.
     */
    public static class FloatArray {

        private @NonNull float[] mValues;
        private @IntRange(from = 0) int mSize;

        /**
         * Creates an empty FloatArray with the default initial capacity.
         */
        public FloatArray() {
            this(10);
        }

        /**
         * Creates an empty FloatArray with the specified initial capacity.
         */
        public FloatArray(@IntRange(from = 0) int initialCapacity) {
            if (initialCapacity == 0) {
                mValues = EmptyArray.FLOAT;
            } else {
                mValues = ArrayUtils.newUnpaddedFloatArray(initialCapacity);
            }
            mSize = 0;
        }

        /**
         * Changes the size of this FloatArray. If this FloatArray is shrinked, the backing array
         * capacity is unchanged.
         */
        public void resize(@IntRange(from = 0) int newSize) {
            if (newSize > mValues.length) {
                ensureCapacity(newSize - mSize);
            }
            mSize = newSize;
        }

        /**
         * Appends the specified value to the end of this array.
         */
        public void append(float value) {
            ensureCapacity(1);
            mValues[mSize++] = value;
        }

        /**
         * Ensures capacity to append at least <code>count</code> values.
         */
        private void ensureCapacity(int count) {
            final int requestedSize = mSize + count;
            if (requestedSize >= mValues.length) {
                final int newCapacity = computeNewCapacity(mSize, requestedSize);
                final float[] newValues = ArrayUtils.newUnpaddedFloatArray(newCapacity);
                System.arraycopy(mValues, 0, newValues, 0, mSize);
                mValues = newValues;
            }
        }

        /**
         * Removes all values from this array.
         */
        public void clear() {
            mSize = 0;
        }

        /**
         * Removes all values from this array and release the internal array object if it is too
         * large.
         */
        public void clearWithReleasingLargeArray() {
            clear();
            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
                mValues = EmptyArray.FLOAT;
            }
        }

        /**
         * Returns the value at the specified position in this array.
         */
        public float get(@IntRange(from = 0) int index) {
            return mValues[index];
        }

        /**
         * Sets the value at the specified position in this array.
         */
        public void set(@IntRange(from = 0) int index, float value) {
            mValues[index] = value;
        }

        /**
         * Returns the number of values in this array.
         */
        public @IntRange(from = 0) int size() {
            return mSize;
        }

        /**
         * Returns internal raw array.
         *
         * Note that this array may have larger size than you requested.
         * Use size() instead for getting the actual array size.
         */
        public @NonNull float[] getRawArray() {
            return mValues;
        }
    }
}
+15 −14
Original line number Diff line number Diff line
@@ -1907,14 +1907,22 @@ public abstract class Layout {

    private static float measurePara(TextPaint paint, CharSequence text, int start, int end,
            TextDirectionHeuristic textDir) {
        MeasuredText mt = null;
        MeasuredText mt = MeasuredText.obtain();
        TextLine tl = TextLine.obtain();
        try {
            mt = MeasuredText.buildForBidi(text, start, end, textDir, mt);
            final char[] chars = mt.getChars();
            final int len = chars.length;
            final Directions directions = mt.getDirections(0, len);
            final int dir = mt.getParagraphDir();
            mt.setPara(text, start, end, textDir);
            Directions directions;
            int dir;
            if (mt.mEasy) {
                directions = DIRS_ALL_LEFT_TO_RIGHT;
                dir = Layout.DIR_LEFT_TO_RIGHT;
            } else {
                directions = AndroidBidi.directions(mt.mDir, mt.mLevels,
                    0, mt.mChars, 0, mt.mLen);
                dir = mt.mDir;
            }
            char[] chars = mt.mChars;
            int len = mt.mLen;
            boolean hasTabs = false;
            TabStops tabStops = null;
            // leading margins should be taken into account when measuring a paragraph
@@ -1947,9 +1955,7 @@ public abstract class Layout {
            return margin + Math.abs(tl.metrics(null));
        } finally {
            TextLine.recycle(tl);
            if (mt != null) {
                mt.recycle();
            }
            MeasuredText.recycle(mt);
        }
    }

@@ -2266,11 +2272,6 @@ public abstract class Layout {
    private SpanSet<LineBackgroundSpan> mLineBackgroundSpans;
    private int mJustificationMode;

    /** @hide */
    @IntDef({DIR_LEFT_TO_RIGHT, DIR_RIGHT_TO_LEFT})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Direction {}

    public static final int DIR_LEFT_TO_RIGHT = 1;
    public static final int DIR_RIGHT_TO_LEFT = -1;

+170 −459

File changed.

Preview size limit exceeded, changes collapsed.

+84 −26
Original line number Diff line number Diff line
@@ -21,10 +21,10 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Paint;
import android.text.AutoGrowArray.FloatArray;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
import android.text.style.LineHeightSpan;
import android.text.style.MetricAffectingSpan;
import android.text.style.TabStopSpan;
import android.util.Log;
import android.util.Pools.SynchronizedPool;
@@ -99,6 +99,8 @@ public class StaticLayout extends Layout {
            b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
            b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
            b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;

            b.mMeasuredText = MeasuredText.obtain();
            return b;
        }

@@ -109,6 +111,8 @@ public class StaticLayout extends Layout {
        private static void recycle(@NonNull Builder b) {
            b.mPaint = null;
            b.mText = null;
            MeasuredText.recycle(b.mMeasuredText);
            b.mMeasuredText = null;
            b.mLeftIndents = null;
            b.mRightIndents = null;
            b.mLeftPaddings = null;
@@ -124,6 +128,7 @@ public class StaticLayout extends Layout {
            mRightIndents = null;
            mLeftPaddings = null;
            mRightPaddings = null;
            mMeasuredText.finish();
        }

        public Builder setText(CharSequence source) {
@@ -439,6 +444,9 @@ public class StaticLayout extends Layout {

        private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();

        // This will go away and be subsumed by native builder code
        private MeasuredText mMeasuredText;

        private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
    }

@@ -610,7 +618,11 @@ public class StaticLayout extends Layout {
        TextUtils.TruncateAt ellipsize = b.mEllipsize;
        final boolean addLastLineSpacing = b.mAddLastLineLineSpacing;
        LineBreaks lineBreaks = new LineBreaks();  // TODO: move to builder to avoid allocation costs
        FloatArray widths = new FloatArray();
        // store span end locations
        int[] spanEndCache = new int[4];
        // store fontMetrics per span range
        // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
        int[] fmCache = new int[4 * 4];

        mLineCount = 0;
        mEllipsized = false;
@@ -622,6 +634,8 @@ public class StaticLayout extends Layout {
        Paint.FontMetricsInt fm = b.mFontMetricsInt;
        int[] chooseHtv = null;

        MeasuredText measured = b.mMeasuredText;

        Spanned spanned = null;
        if (source instanceof Spanned)
            spanned = (Spanned) source;
@@ -648,7 +662,6 @@ public class StaticLayout extends Layout {
                b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
                indents, mLeftPaddings, mRightPaddings);

        MeasuredText measured = null;
        try {
            int paraEnd;
            for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
@@ -708,6 +721,13 @@ public class StaticLayout extends Layout {
                    }
                }

                measured.setPara(source, paraStart, paraEnd, textDir);
                char[] chs = measured.mChars;
                float[] widths = measured.mWidths;
                byte[] chdirs = measured.mLevels;
                int dir = measured.mDir;
                boolean easy = measured.mEasy;

                // tab stop locations
                int[] variableTabStops = null;
                if (spanned != null) {
@@ -723,16 +743,50 @@ public class StaticLayout extends Layout {
                    }
                }

                measured = MeasuredText.buildForStaticLayout(
                        paint, source, paraStart, paraEnd, textDir, nativePtr, measured);
                final char[] chs = measured.getChars();
                final int[] spanEndCache = measured.getSpanEndCache().getRawArray();
                final int[] fmCache = measured.getFontMetrics().getRawArray();
                widths.resize(chs.length);

                // measurement has to be done before performing line breaking
                // but we don't want to recompute fontmetrics or span ranges the
                // second time, so we cache those and then use those stored values
                int fmCacheCount = 0;
                int spanEndCacheCount = 0;
                for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
                    if (fmCacheCount * 4 >= fmCache.length) {
                        int[] grow = new int[fmCacheCount * 4 * 2];
                        System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
                        fmCache = grow;
                    }

                    if (spanEndCacheCount >= spanEndCache.length) {
                        int[] grow = new int[spanEndCacheCount * 2];
                        System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
                        spanEndCache = grow;
                    }

                    if (spanned == null) {
                        spanEnd = paraEnd;
                        int spanLen = spanEnd - spanStart;
                        measured.addStyleRun(paint, spanLen, fm, nativePtr);
                    } else {
                        spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
                                MetricAffectingSpan.class);
                        int spanLen = spanEnd - spanStart;
                        MetricAffectingSpan[] spans =
                                spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
                        spans = TextUtils.removeEmptySpans(spans, spanned,
                                MetricAffectingSpan.class);
                        measured.addStyleRun(paint, spans, spanLen, fm, nativePtr);
                    }

                    // the order of storage here (top, bottom, ascent, descent) has to match the
                    // code below where these values are retrieved
                    fmCache[fmCacheCount * 4 + 0] = fm.top;
                    fmCache[fmCacheCount * 4 + 1] = fm.bottom;
                    fmCache[fmCacheCount * 4 + 2] = fm.ascent;
                    fmCache[fmCacheCount * 4 + 3] = fm.descent;
                    fmCacheCount++;

                    spanEndCache[spanEndCacheCount] = spanEnd;
                    spanEndCacheCount++;
                }

                int breakCount = nComputeLineBreaks(
                        nativePtr,
@@ -755,7 +809,7 @@ public class StaticLayout extends Layout {
                        lineBreaks.ascents,
                        lineBreaks.descents,
                        lineBreaks.flags,
                        widths.getRawArray());
                        widths);

                final int[] breaks = lineBreaks.breaks;
                final float[] lineWidths = lineBreaks.widths;
@@ -778,7 +832,7 @@ public class StaticLayout extends Layout {
                            width += lineWidths[i];
                        } else {
                            for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
                                width += widths.get(j);
                                width += widths[j];
                            }
                        }
                        flag |= flags[i] & TAB_MASK;
@@ -842,10 +896,10 @@ public class StaticLayout extends Layout {
                        v = out(source, here, endPos,
                                ascent, descent, fmTop, fmBottom,
                                v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
                                flags[breakIndex], needMultiply, measured, bufEnd,
                                includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(),
                                paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
                                paint, moreChars);
                                flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd,
                                includepad, trackpad, addLastLineSpacing, chs, widths, paraStart,
                                ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint,
                                moreChars);

                        if (endPos < spanEnd) {
                            // preserve metrics for current span
@@ -873,8 +927,7 @@ public class StaticLayout extends Layout {

            if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
                    && mLineCount < mMaximumVisibleLineCount) {
                measured = MeasuredText.buildForStaticLayout(
                        paint, source, bufEnd, bufEnd, textDir, nativePtr, measured);
                measured.setPara(source, bufEnd, bufEnd, textDir);

                paint.getFontMetricsInt(fm);

@@ -884,15 +937,12 @@ public class StaticLayout extends Layout {
                        v,
                        spacingmult, spacingadd, null,
                        null, fm, 0,
                        needMultiply, measured, bufEnd,
                        needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
                        includepad, trackpad, addLastLineSpacing, null,
                        null, bufStart, ellipsize,
                        ellipsizedWidth, 0, paint, false);
            }
        } finally {
            if (measured != null) {
                measured.recycle();
            }
            nFinish(nativePtr);
        }
    }
@@ -902,8 +952,8 @@ public class StaticLayout extends Layout {
    private int out(final CharSequence text, final int start, final int end, int above, int below,
            int top, int bottom, int v, final float spacingmult, final float spacingadd,
            final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
            final int flags, final boolean needMultiply, final MeasuredText measured,
            final int bufEnd, final boolean includePad, final boolean trackPad,
            final int flags, final boolean needMultiply, final byte[] chdirs, final int dir,
            final boolean easy, final int bufEnd, final boolean includePad, final boolean trackPad,
            final boolean addLastLineLineSpacing, final char[] chs, final float[] widths,
            final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
            final float textWidth, final TextPaint paint, final boolean moreChars) {
@@ -911,7 +961,6 @@ public class StaticLayout extends Layout {
        final int off = j * mColumns;
        final int want = off + mColumns + TOP;
        int[] lines = mLines;
        final int dir = measured.getParagraphDir();

        if (want >= lines.length) {
            final int[] grow = ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(want));
@@ -937,8 +986,17 @@ public class StaticLayout extends Layout {
        // one bit for start field
        lines[off + TAB] |= flags & TAB_MASK;
        lines[off + HYPHEN] = flags;

        lines[off + DIR] |= dir << DIR_SHIFT;
        mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);
        // easy means all chars < the first RTL, so no emoji, no nothing
        // XXX a run with no text or all spaces is easy but might be an empty
        // RTL paragraph.  Make sure easy is false if this is the case.
        if (easy) {
            mLineDirections[j] = DIRS_ALL_LEFT_TO_RIGHT;
        } else {
            mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
                    start - widthStart, end - start);
        }

        final boolean firstLine = (j == 0);
        final boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
+42 −25

File changed.

Preview size limit exceeded, changes collapsed.