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

Commit 467f25a7 authored by Kenny Root's avatar Kenny Root Committed by Android (Google) Code Review
Browse files

Merge "Refactor Styled utility functions into reusable objects."

parents 26b1f5ef e8e45f2c
Loading
Loading
Loading
Loading
+129 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.text;

import android.text.Layout.Directions;

/**
 * Access the ICU bidi implementation.
 * @hide
@@ -44,5 +46,132 @@ package android.text;
        return result;
    }

    /**
     * Returns run direction information for a line within a paragraph.
     *
     * @param dir base line direction, either Layout.DIR_LEFT_TO_RIGHT or
     *     Layout.DIR_RIGHT_TO_LEFT
     * @param levels levels as returned from {@link #bidi}
     * @param lstart start of the line in the levels array
     * @param chars the character array (used to determine whitespace)
     * @param cstart the start of the line in the chars array
     * @param len the length of the line
     * @return the directions
     */
    public static Directions directions(int dir, byte[] levels, int lstart,
            char[] chars, int cstart, int len) {

        int baseLevel = dir == Layout.DIR_LEFT_TO_RIGHT ? 0 : 1;
        int curLevel = levels[lstart];
        int minLevel = curLevel;
        int runCount = 1;
        for (int i = lstart + 1, e = lstart + len; i < e; ++i) {
            int level = levels[i];
            if (level != curLevel) {
                curLevel = level;
                ++runCount;
            }
        }

        // add final run for trailing counter-directional whitespace
        int visLen = len;
        if ((curLevel & 1) != (baseLevel & 1)) {
            // look for visible end
            while (--visLen >= 0) {
                char ch = chars[cstart + visLen];

                if (ch == '\n') {
                    --visLen;
                    break;
                }

                if (ch != ' ' && ch != '\t') {
                    break;
                }
            }
            ++visLen;
            if (visLen != len) {
                ++runCount;
            }
        }

        if (runCount == 1 && minLevel == baseLevel) {
            // we're done, only one run on this line
            if ((minLevel & 1) != 0) {
                return Layout.DIRS_ALL_RIGHT_TO_LEFT;
            }
            return Layout.DIRS_ALL_LEFT_TO_RIGHT;
        }

        int[] ld = new int[runCount * 2];
        int maxLevel = minLevel;
        int levelBits = minLevel << Layout.RUN_LEVEL_SHIFT;
        {
            // Start of first pair is always 0, we write
            // length then start at each new run, and the
            // last run length after we're done.
            int n = 1;
            int prev = lstart;
            curLevel = minLevel;
            for (int i = lstart, e = lstart + visLen; i < e; ++i) {
                int level = levels[i];
                if (level != curLevel) {
                    curLevel = level;
                    if (level > maxLevel) {
                        maxLevel = level;
                    } else if (level < minLevel) {
                        minLevel = level;
                    }
                    // XXX ignore run length limit of 2^RUN_LEVEL_SHIFT
                    ld[n++] = (i - prev) | levelBits;
                    ld[n++] = i - lstart;
                    levelBits = curLevel << Layout.RUN_LEVEL_SHIFT;
                    prev = i;
                }
            }
            ld[n] = (lstart + visLen - prev) | levelBits;
            if (visLen < len) {
                ld[++n] = visLen;
                ld[++n] = (len - visLen) | (baseLevel << Layout.RUN_LEVEL_SHIFT);
            }
        }

        // See if we need to swap any runs.
        // If the min level run direction doesn't match the base
        // direction, we always need to swap (at this point
        // we have more than one run).
        // Otherwise, we don't need to swap the lowest level.
        // Since there are no logically adjacent runs at the same
        // level, if the max level is the same as the (new) min
        // level, we have a series of alternating levels that
        // is already in order, so there's no more to do.
        //
        boolean swap;
        if ((minLevel & 1) == baseLevel) {
            minLevel += 1;
            swap = maxLevel > minLevel;
        } else {
            swap = runCount > 1;
        }
        if (swap) {
            for (int level = maxLevel - 1; level >= minLevel; --level) {
                for (int i = 0; i < ld.length; i += 2) {
                    if (levels[ld[i]] >= level) {
                        int e = i + 2;
                        while (e < ld.length && levels[ld[e]] >= level) {
                            e += 2;
                        }
                        for (int low = i, hi = e - 2; low < hi; low += 2, hi -= 2) {
                            int x = ld[low]; ld[low] = ld[hi]; ld[hi] = x;
                            x = ld[low+1]; ld[low+1] = ld[hi+1]; ld[hi+1] = x;
                        }
                        i = e + 2;
                    }
                }
            }
        }
        return new Directions(ld);
    }

    private native static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo);
}
 No newline at end of file
+12 −13
Original line number Diff line number Diff line
@@ -208,11 +208,11 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
             * width because the width that was passed in was for the
             * full text, not the ellipsized form.
             */
            synchronized (sTemp) {
                mMax = (int) (FloatMath.ceil(Styled.measureText(paint, sTemp,
                                                source, 0, source.length(),
                                                null)));
            }
            TextLine line = TextLine.obtain();
            line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
                    Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
            mMax = (int) FloatMath.ceil(line.metrics(null));
            TextLine.recycle(line);
        }

        if (includepad) {
@@ -277,13 +277,12 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
                fm = new Metrics();
            }

            int wid;
            TextLine line = TextLine.obtain();
            line.set(paint, text, 0, text.length(), Layout.DIR_LEFT_TO_RIGHT,
                    Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
            fm.width = (int) FloatMath.ceil(line.metrics(fm));
            TextLine.recycle(line);

            synchronized (sTemp) {
                wid = (int) (FloatMath.ceil(Styled.measureText(paint, sTemp,
                                                text, 0, text.length(), fm)));
            }
            fm.width = wid;
            return fm;
        } else {
            return null;
+82 −549

File changed.

Preview size limit exceeded, changes collapsed.

+250 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 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 com.android.internal.util.ArrayUtils;

import android.graphics.Paint;
import android.icu.text.ArabicShaping;
import android.text.style.MetricAffectingSpan;
import android.text.style.ReplacementSpan;
import android.util.Log;

/**
 * @hide
 */
class MeasuredText {
    /* package */ CharSequence mText;
    /* package */ int mTextStart;
    /* package */ float[] mWidths;
    /* package */ char[] mChars;
    /* package */ byte[] mLevels;
    /* package */ int mDir;
    /* package */ boolean mEasy;
    /* package */ int mLen;
    private int mPos;
    private float[] mWorkWidths; // temp buffer for Paint.measureText, arrgh
    private TextPaint mWorkPaint;

    private MeasuredText() {
        mWorkPaint = new TextPaint();
    }

    private static MeasuredText[] cached = new MeasuredText[3];

    /* package */
    static MeasuredText obtain() {
        MeasuredText mt;
        synchronized (cached) {
            for (int i = cached.length; --i >= 0;) {
                if (cached[i] != null) {
                    mt = cached[i];
                    cached[i] = null;
                    return mt;
                }
            }
        }
        mt = new MeasuredText();
        Log.e("MEAS", "new: " + mt);
        return mt;
    }

    /* package */
    static MeasuredText recycle(MeasuredText mt) {
        mt.mText = null;
        if (mt.mLen < 1000) {
            synchronized(cached) {
                for (int i = 0; i < cached.length; ++i) {
                    if (cached[i] == null) {
                        cached[i] = mt;
                        break;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Analyzes text for
     * bidirectional runs.  Allocates working buffers.
     */
    /* package */
    void setPara(CharSequence text, int start, int end, int bidiRequest) {
        mText = text;
        mTextStart = start;

        int len = end - start;
        mLen = len;
        mPos = 0;

        if (mWidths == null || mWidths.length < len) {
            mWidths = new float[ArrayUtils.idealFloatArraySize(len)];
            mWorkWidths = new float[mWidths.length];
        }
        if (mChars == null || mChars.length < len) {
            mChars = new char[ArrayUtils.idealCharArraySize(len)];
        }
        TextUtils.getChars(text, start, end, mChars, 0);

        if (text instanceof Spanned) {
            Spanned spanned = (Spanned) text;
            ReplacementSpan[] spans = spanned.getSpans(start, end,
                    ReplacementSpan.class);

            for (int i = 0; i < spans.length; i++) {
                int startInPara = spanned.getSpanStart(spans[i]) - start;
                int endInPara = spanned.getSpanEnd(spans[i]) - start;
                for (int j = startInPara; j < endInPara; j++) {
                    mChars[j] = '\uFFFC';
                }
            }
        }

        if (TextUtils.doesNotNeedBidi(mChars, 0, len)) {
            mDir = 1;
            mEasy = true;
        } else {
            if (mLevels == null || mLevels.length < len) {
                mLevels = new byte[ArrayUtils.idealByteArraySize(len)];
            }
            mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels, len, false);
            mEasy = false;

            // shape
            if (mLen > 0) {
                byte[] levels = mLevels;
                char[] chars = mChars;
                byte level = levels[0];
                int pi = 0;
                for (int i = 1, e = mLen;; ++i) {
                    if (i == e || levels[i] != level) {
                        if ((level & 0x1) != 0) {
                            AndroidCharacter.mirror(chars, pi, i - pi);
                            ArabicShaping.SHAPER.shape(chars, pi, i - pi);
                        }
                        if (i == e) {
                            break;
                        }
                        pi = i;
                        level = levels[i];
                    }
                }
            }
        }
    }

    float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
        int p = mPos;
        float[] w = mWidths, ww = mWorkWidths;
        int count = paint.getTextWidths(mChars, p, len, ww);
        int width = 0;
        if (count < len) {
            // must have surrogate pairs in here, pad out the array with zero
            // for the trailing surrogates
            char[] chars = mChars;
            for (int i = 0, e = mLen; i < count; ++i) {
                width += (w[p++] = ww[i]);
                if (p < e && chars[p] >= '\udc00' && chars[p] < '\ue000' &&
                        chars[p-1] >= '\ud800' && chars[p-1] < '\udc00') {
                    w[p++] = 0;
                }
            }
        } else {
            for (int i = 0; i < len; ++i) {
                width += (w[p++] = ww[i]);
            }
        }
        mPos = p;
        if (fm != null) {
            paint.getFontMetricsInt(fm);
        }
        return width;
    }

    float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
            Paint.FontMetricsInt fm) {

        TextPaint workPaint = mWorkPaint;
        workPaint.set(paint);
        // XXX paint should not have a baseline shift, but...
        workPaint.baselineShift = 0;

        ReplacementSpan replacement = null;
        for (int i = 0; i < spans.length; i++) {
            MetricAffectingSpan span = spans[i];
            if (span instanceof ReplacementSpan) {
                replacement = (ReplacementSpan)span;
            } else {
                span.updateMeasureState(workPaint);
            }
        }

        float wid;
        if (replacement == null) {
            wid = addStyleRun(workPaint, len, fm);
        } else {
            // Use original text.  Shouldn't matter.
            wid = replacement.getSize(workPaint, mText, mTextStart + mPos,
                    mTextStart + mPos + len, fm);
            float[] w = mWidths;
            w[mPos] = wid;
            for (int i = mPos + 1, e = mPos + len; i < e; i++)
                w[i] = 0;
        }

        if (fm != null) {
            if (workPaint.baselineShift < 0) {
                fm.ascent += workPaint.baselineShift;
                fm.top += workPaint.baselineShift;
            } else {
                fm.descent += workPaint.baselineShift;
                fm.bottom += workPaint.baselineShift;
            }
        }

        return wid;
    }

    int breakText(int start, int limit, boolean forwards, float width) {
        float[] w = mWidths;
        if (forwards) {
            for (int i = start; i < limit; ++i) {
                if ((width -= w[i]) < 0) {
                    return i - start;
                }
            }
        } else {
            for (int i = limit; --i >= start;) {
                if ((width -= w[i]) < 0) {
                    return limit - i -1;
                }
            }
        }

        return limit - start;
    }

    float measure(int start, int limit) {
        float width = 0;
        float[] w = mWidths;
        for (int i = start; i < limit; ++i) {
            width += w[i];
        }
        return width;
    }
}
 No newline at end of file
+92 −340

File changed.

Preview size limit exceeded, changes collapsed.

Loading