TextLine.java revision c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1
1e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/*
2e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Copyright (C) 2010 The Android Open Source Project
3e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
4e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Licensed under the Apache License, Version 2.0 (the "License");
5e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * you may not use this file except in compliance with the License.
6e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * You may obtain a copy of the License at
7e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
8e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *      http://www.apache.org/licenses/LICENSE-2.0
9e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
10e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Unless required by applicable law or agreed to in writing, software
11e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * distributed under the License is distributed on an "AS IS" BASIS,
12e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * See the License for the specific language governing permissions and
14e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * limitations under the License.
15e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */
16e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
17e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltpackage android.text;
18e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
19e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Bitmap;
20e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Canvas;
21e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint;
22e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint.FontMetricsInt;
23cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunneimport android.graphics.RectF;
24e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.Layout.Directions;
25c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.Layout.TabStops;
26e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.CharacterStyle;
27e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.MetricAffectingSpan;
28e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.ReplacementSpan;
29e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.util.Log;
30e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
31dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunneimport com.android.internal.util.ArrayUtils;
32dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
33945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunneimport java.lang.reflect.Array;
34945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
35e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/**
36e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Represents a line of styled text, for measuring in visual order and
37e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * for rendering.
38e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
39e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Get a new instance using obtain(), and when finished with it, return it
40e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * to the pool using recycle().
41e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
42e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Call set to prepare the instance for use, then either draw, measure,
43e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * metrics, or caretToLeftRightOf.
44e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
45e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @hide
46e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */
47e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltclass TextLine {
48bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private static final boolean DEBUG = false;
49bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy
50e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private TextPaint mPaint;
51e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private CharSequence mText;
52e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mStart;
53e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mLen;
54e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mDir;
55e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Directions mDirections;
56e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mHasTabs;
57c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private TabStops mTabs;
58e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private char[] mChars;
59e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mCharsValid;
60e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Spanned mSpanned;
61345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne    private final TextPaint mWorkPaint = new TextPaint();
62c1f44830809f0a8526855f13822702ea756214faGilles Debunne    private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet =
63c1f44830809f0a8526855f13822702ea756214faGilles Debunne            new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class);
64c1f44830809f0a8526855f13822702ea756214faGilles Debunne    private final SpanSet<CharacterStyle> mCharacterStyleSpanSet =
65c1f44830809f0a8526855f13822702ea756214faGilles Debunne            new SpanSet<CharacterStyle>(CharacterStyle.class);
66c1f44830809f0a8526855f13822702ea756214faGilles Debunne    private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet =
67c1f44830809f0a8526855f13822702ea756214faGilles Debunne            new SpanSet<ReplacementSpan>(ReplacementSpan.class);
68e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
69bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private static final TextLine[] sCached = new TextLine[3];
70e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
71e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
72e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns a new TextLine from the shared pool.
73e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
74e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return an uninitialized TextLine
75e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
76e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine obtain() {
77e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl;
78bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        synchronized (sCached) {
79bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            for (int i = sCached.length; --i >= 0;) {
80bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                if (sCached[i] != null) {
81bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    tl = sCached[i];
82bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    sCached[i] = null;
83e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return tl;
84e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
85e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
86e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
87e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl = new TextLine();
88bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        if (DEBUG) {
89bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            Log.v("TLINE", "new: " + tl);
90bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        }
91e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return tl;
92e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
93e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
94e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
95e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Puts a TextLine back into the shared pool. Do not use this TextLine once
96e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * it has been returned.
97e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param tl the textLine
98e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return null, as a convenience from clearing references to the provided
99e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * TextLine
100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
101e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine recycle(TextLine tl) {
102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mText = null;
103e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mPaint = null;
104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mDirections = null;
105c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne
106c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne        tl.mMetricAffectingSpanSpanSet.recycle();
107c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne        tl.mCharacterStyleSpanSet.recycle();
108c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne        tl.mReplacementSpanSpanSet.recycle();
109c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne
110bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        synchronized(sCached) {
111bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            for (int i = 0; i < sCached.length; ++i) {
112bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                if (sCached[i] == null) {
113bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    sCached[i] = tl;
114f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne                    break;
115e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
116e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
117e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
118e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return null;
119e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
121e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Initializes a TextLine and prepares it for use.
123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param paint the base paint for the line
125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param text the text, can be Styled
126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the line relative to the text
127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the line relative to the text
128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param dir the paragraph direction of this line
129e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param directions the directions information of this line
130e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param hasTabs true if the line might contain tabs or emoji
131c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param tabStops the tabStops. Can be null.
132e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
134c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            Directions directions, boolean hasTabs, TabStops tabStops) {
135e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mPaint = paint;
136e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mText = text;
137e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mStart = start;
138e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mLen = limit - start;
139e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDir = dir;
140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDirections = directions;
1418059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if (mDirections == null) {
1428059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            throw new IllegalArgumentException("Directions cannot be null");
1438059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        }
144e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mHasTabs = hasTabs;
145e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mSpanned = null;
146e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
147e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean hasReplacement = false;
148e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (text instanceof Spanned) {
149e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            mSpanned = (Spanned) text;
150c1f44830809f0a8526855f13822702ea756214faGilles Debunne            mReplacementSpanSpanSet.init(mSpanned, start, limit);
151c1f44830809f0a8526855f13822702ea756214faGilles Debunne            hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
152e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
153e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1541e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
155e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
156e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
157e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mChars == null || mChars.length < mLen) {
158e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                mChars = new char[ArrayUtils.idealCharArraySize(mLen)];
159e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
160e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextUtils.getChars(text, start, limit, mChars, 0);
1610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (hasReplacement) {
1620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Handle these all at once so we don't have to do it as we go.
1630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Replace the first character of each replacement run with the
1640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // object-replacement character and the remainder with zero width
1650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // non-break space aka BOM.  Cursor movement code skips these
1660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // zero-width characters.
1670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                char[] chars = mChars;
1680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int i = start, inext; i < limit; i = inext) {
169c1f44830809f0a8526855f13822702ea756214faGilles Debunne                    inext = mReplacementSpanSpanSet.getNextTransition(i, limit);
170c1f44830809f0a8526855f13822702ea756214faGilles Debunne                    if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) {
1711e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                        // transition into a span
1720c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        chars[i - start] = '\ufffc';
1730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        for (int j = i - start + 1, e = inext - start; j < e; ++j) {
1740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip
1750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        }
1760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
1770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
1780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
179e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
180c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        mTabs = tabStops;
181e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
182e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
183e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Renders the TextLine.
185e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to render on
187e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the leading margin position
188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
190e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void draw(Canvas c, float x, int top, int y, int bottom) {
193e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
195bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                drawRun(c, 0, mLen, false, x, top, y, bottom, false);
196e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
197e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
198e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
199bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                drawRun(c, 0, mLen, true, x, top, y, bottom, false);
200e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        RectF emojiRect = null;
207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lastRunIndex = runs.length - 2;
209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
211e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
212e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
213e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = mChars[j];
224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
225e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(mChars, j);
226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
236bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom,
237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            i != lastRunIndex || j != mLen);
238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    } else if (bm != null) {
242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bitmapHeight = bm.getHeight();
244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float scale = -bmAscent / bitmapHeight;
245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float width = bm.getWidth() * scale;
246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (emojiRect == null) {
248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            emojiRect = new RectF();
249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        emojiRect.set(x + h, y + bmAscent,
251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                                x + h + width, y);
252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        c.drawBitmap(bm, null, emojiRect, mPaint);
253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += width;
254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns metrics information for the entire line.
264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives font metrics information, can be null
266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the line
267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float metrics(FontMetricsInt fmi) {
269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return measure(mLen, false, fmi);
270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns information about a position on the line.
274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the line-relative character offset, between 0 and the
276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * line length, inclusive
277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param trailing true to measure the trailing edge of the character
278e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * before offset, false to measure the leading edge of the character
279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * at offset.
280e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character, can be null.
282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed offset from the leading margin to the requested
283e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character edge.
284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
285e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float measure(int offset, boolean trailing, FontMetricsInt fmi) {
286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int target = trailing ? offset - 1 : offset;
287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (target < 0) {
288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return 0;
289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
290e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
295bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                return measureRun(0, offset, mLen, false, fmi);
296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
298bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                return measureRun(0, offset, mLen, true, fmi);
299e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        char[] chars = mChars;
303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = chars[j];
319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(chars, j);
321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
328e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
329e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
330e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean inSegment = target >= segstart && target < j;
332e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
333e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
334e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment && advance) {
335bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                        return h += measureRun(segstart, offset, j, runIsRtl, fmi);
336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
337e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
338bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    float w = measureRun(segstart, j, j, runIsRtl, fmi);
339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    h += advance ? w : -w;
340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment) {
342bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                        return h += measureRun(segstart, offset, j, runIsRtl, null);
343e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (offset == j) {
347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (target == j) {
351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (bm != null) {
356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
357e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float wid = bm.getWidth() * -bmAscent / bm.getHeight();
358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += mDir * wid;
359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
360e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
365e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return h;
368e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
369e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Draws a unidirectional (but possibly multi-styled) run of text.
372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
373bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
374e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to draw on
375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start
376e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit
377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
378e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the position of the run that is closest to the leading margin
379e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
380e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
381e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width value is required.
383e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run, based on the paragraph direction.
384e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Only valid if needWidth is true.
385e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
386bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float drawRun(Canvas c, int start,
387e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, float x, int top, int y, int bottom,
388e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean needWidth) {
389e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
390e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
391bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            float w = -measureRun(start, limit, limit, runIsRtl, null);
392bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            handleRun(start, limit, limit, runIsRtl, c, x + w, top,
3930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    y, bottom, null, false);
394e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return w;
395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
396e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
397bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        return handleRun(start, limit, limit, runIsRtl, c, x, top,
3980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                y, bottom, null, needWidth);
399e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
400e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
401e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
402e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Measures a unidirectional (but possibly multi-styled) run of text.
403e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
404bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
405e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
406e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset to measure to, between start and limit inclusive
407e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit of the run
408e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
409e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
410e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * run, can be null.
411e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width from the start of the run to the leading edge
412e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the character at offset, based on the run (not paragraph) direction
413e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
414bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float measureRun(int start, int offset, int limit, boolean runIsRtl,
415bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            FontMetricsInt fmi) {
416bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true);
417e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
418e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
419e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
420e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Walk the cursor through this line, skipping conjuncts and
421e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * zero-width characters.
422e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
423e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * <p>This function cannot properly walk the cursor off the ends of the line
424e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * since it does not know about any shaping on the previous/following line
425e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * that might affect the cursor position. Callers must either avoid these
426e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * situations or handle the result specially.
427e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
428e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param cursor the starting position of the cursor, between 0 and the
429e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * length of the line, inclusive
430e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param toLeft true if the caret is moving to the left.
431e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset.  If it is less than 0 or greater than the length
432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the line, the previous/following line should be examined to get the
433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * actual offset.
434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    int getOffsetToLeftRightOf(int cursor, boolean toLeft) {
436e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 1) The caret marks the leading edge of a character. The character
437e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // logically before it might be on a different level, and the active caret
438e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // position is on the character at the lower level. If that character
439e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // was the previous character, the caret is on its trailing edge.
440e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 2) Take this character/edge and move it in the indicated direction.
441e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // This gives you a new character and a new edge.
442e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 3) This position is between two visually adjacent characters.  One of
443e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // these might be at a lower level.  The active position is on the
444e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // character at the lower level.
445e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 4) If the active position is on the trailing edge of the character,
446e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // the new caret position is the following logical character, else it
447e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // is the character.
448e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
449e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineStart = 0;
450e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineEnd = mLen;
451e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean paraIsRtl = mDir == -1;
452e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
453e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
454e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1;
455e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean trailing = false;
456e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
457e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (cursor == lineStart) {
458e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = -2;
459e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else if (cursor == lineEnd) {
460e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = runs.length;
461e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
462e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // First, get information about the run containing the character with
463e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the active caret.
464e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          for (runIndex = 0; runIndex < runs.length; runIndex += 2) {
465e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runStart = lineStart + runs[runIndex];
466e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (cursor >= runStart) {
467e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK);
468e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (runLimit > lineEnd) {
469e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  runLimit = lineEnd;
470e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
471e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor < runLimit) {
472e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
473e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    Layout.RUN_LEVEL_MASK;
474e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (cursor == runStart) {
475e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // The caret is on a run boundary, see if we should
476e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // use the position on the trailing edge of the previous
477e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // logical character instead.
478e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit;
479e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int pos = cursor - 1;
480e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) {
481e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    prevRunStart = lineStart + runs[prevRunIndex];
482e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (pos >= prevRunStart) {
483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      prevRunLimit = prevRunStart +
484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK);
485e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (prevRunLimit > lineEnd) {
486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          prevRunLimit = lineEnd;
487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (pos < prevRunLimit) {
489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT)
490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            & Layout.RUN_LEVEL_MASK;
491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (prevRunLevel < runLevel) {
492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          // Start from logically previous character.
493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runIndex = prevRunIndex;
494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLevel = prevRunLevel;
495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runStart = prevRunStart;
496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLimit = prevRunLimit;
497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          trailing = true;
498e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          break;
499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
503e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
504e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
505e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
506e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
507e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
509e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // caret might be == lineEnd.  This is generally a space or paragraph
510e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // separator and has an associated run, but might be the end of
511e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // text, in which case it doesn't.  If that happens, we ran off the
512e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // end of the run list, and runIndex == runs.length.  In this case,
513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we are at a run boundary so we skip the below test.
514e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (runIndex != runs.length) {
515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean runIsRtl = (runLevel & 0x1) != 0;
516e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean advance = toLeft == runIsRtl;
517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor != (advance ? runLimit : runStart) || advance != trailing) {
518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // Moving within or into the run, so we can move logically.
5190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                  newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit,
5200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                          runIsRtl, cursor, advance);
521e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // If the new position is internal to the run, we're at the strong
522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // position already so we're finished.
523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  if (newCaret != (advance ? runLimit : runStart)) {
524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      return newCaret;
525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
526e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // If newCaret is -1, we're starting at a run boundary and crossing
531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // into another run. Otherwise we've arrived at a run boundary, and
532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to figure out which character to attach to.  Note we might
533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to run this twice, if we cross a run boundary and end up at
534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // another run boundary.
535e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        while (true) {
536e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          boolean advance = toLeft == paraIsRtl;
537e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          int otherRunIndex = runIndex + (advance ? 2 : -2);
538e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (otherRunIndex >= 0 && otherRunIndex < runs.length) {
539e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunStart = lineStart + runs[otherRunIndex];
540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLimit = otherRunStart +
541e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK);
542e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLimit > lineEnd) {
543e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                otherRunLimit = lineEnd;
544e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
546e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Layout.RUN_LEVEL_MASK;
547e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean otherRunIsRtl = (otherRunLevel & 1) != 0;
548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            advance = toLeft == otherRunIsRtl;
550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (newCaret == -1) {
5510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart,
5520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        otherRunLimit, otherRunIsRtl,
553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        advance ? otherRunStart : otherRunLimit, advance);
554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (newCaret == (advance ? otherRunLimit : otherRunStart)) {
555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // Crossed and ended up at a new boundary,
556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // repeat a second and final time.
557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runIndex = otherRunIndex;
558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runLevel = otherRunLevel;
559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    continue;
560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
562e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            // The new caret is at a boundary.
565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLevel < runLevel) {
566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // The strong character is in the other run.
567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? otherRunStart : otherRunLimit;
568e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            break;
570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret == -1) {
573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // We're walking off the end of the line.  The paragraph
574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // level is always equal to or lower than any internal level, so
575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // the boundaries get the strong caret.
5760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt              newCaret = advance ? mLen + 1 : -1;
577e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              break;
578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Else we've arrived at the end of the line.  That's a strong position.
581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // We might have arrived here by crossing over a run with no internal
582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // breaks and dropping out of the above loop before advancing one final
583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // time, so reset the caret.
584e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Note, we use '<=' below to handle a situation where the only run
585e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // on the line is a counter-directional run.  If we're not advancing,
586e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we can end up at the 'lineEnd' position but the caret we want is at
587e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the lineStart.
588e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret <= lineEnd) {
589e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? lineEnd : lineStart;
590e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
591e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          break;
592e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
593e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
594e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return newCaret;
595e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
596e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
598e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next valid offset within this directional run, skipping
599e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * conjuncts and zero-width characters.  This should not be called to walk
600cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunne     * off the end of the line, since the returned values might not be valid
6010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * on neighboring lines.  If the returned offset is less than zero or
6020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * greater than the line length, the offset should be recomputed on the
6030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * preceding or following line, respectively.
604e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
605e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
6060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runStart the start of the run
6070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runLimit the limit of the run
6080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runIsRtl true if the run is right-to-left
609e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset
610e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param after true if the new offset should logically follow the provided
611e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * offset
612e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset
613e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
6140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit,
6150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean runIsRtl, int offset, boolean after) {
616e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (runIndex < 0 || offset == (after ? mLen : 0)) {
6180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // Walking off end of line.  Since we don't know
6190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // what cursor positions are available on other lines, we can't
6200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // return accurate values.  These are a guess.
621e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (after) {
6220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart;
6230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart;
6250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
6260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        TextPaint wp = mWorkPaint;
6280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        wp.set(mPaint);
6290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanStart = runStart;
6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanLimit;
6320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mSpanned == null) {
6330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            spanLimit = runLimit;
6340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int target = after ? offset + 1 : offset;
6360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int limit = mStart + runLimit;
6370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            while (true) {
6380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit,
6390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        MetricAffectingSpan.class) - mStart;
6400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (spanLimit >= target) {
6410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    break;
642e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
6430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanStart = spanLimit;
6440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart,
6470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    mStart + spanLimit, MetricAffectingSpan.class);
6481e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
6490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (spans.length > 0) {
6510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ReplacementSpan replacement = null;
6520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = 0; j < spans.length; j++) {
6530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    MetricAffectingSpan span = spans[j];
6540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    if (span instanceof ReplacementSpan) {
6550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        replacement = (ReplacementSpan)span;
6560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    } else {
6570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateMeasureState(wp);
6580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
6590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
6600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (replacement != null) {
6620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // If we have a replacement span, we're moving either to
6630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // the start or end of this span.
6640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    return after ? spanLimit : spanStart;
665e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
666e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
667e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
668e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6690c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
6700c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
6710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mCharsValid) {
6720c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
6730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    flags, offset, cursorOpt);
6740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mText, mStart + spanStart,
676345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne                    mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart;
677e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
678e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
679e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
680e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
6810bb000931bb841e75903d655552d1626ae158707Gilles Debunne     * @param wp
6820bb000931bb841e75903d655552d1626ae158707Gilles Debunne     */
6830bb000931bb841e75903d655552d1626ae158707Gilles Debunne    private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) {
6840bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousTop     = fmi.top;
6850bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousAscent  = fmi.ascent;
6860bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousDescent = fmi.descent;
6870bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousBottom  = fmi.bottom;
6880bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousLeading = fmi.leading;
6890bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6900bb000931bb841e75903d655552d1626ae158707Gilles Debunne        wp.getFontMetricsInt(fmi);
6910bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6928a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio        updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,
6938a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousLeading);
6948a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio    }
6958a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
6968a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio    static void updateMetrics(FontMetricsInt fmi, int previousTop, int previousAscent,
6978a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousDescent, int previousBottom, int previousLeading) {
6980bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.top     = Math.min(fmi.top,     previousTop);
6990bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.ascent  = Math.min(fmi.ascent,  previousAscent);
7000bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.descent = Math.max(fmi.descent, previousDescent);
7010bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.bottom  = Math.max(fmi.bottom,  previousBottom);
7020bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.leading = Math.max(fmi.leading, previousLeading);
7030bb000931bb841e75903d655552d1626ae158707Gilles Debunne    }
7040bb000931bb841e75903d655552d1626ae158707Gilles Debunne
7050bb000931bb841e75903d655552d1626ae158707Gilles Debunne    /**
706e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering text.  The text must
707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * not include a tab or emoji.
708e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
709e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the working paint
710e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the text
7110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the text
712e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
713e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if rendering is not needed
714e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the run closest to the leading margin
715e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
716e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
717e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
718e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
719e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the run is needed
720e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
721e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
722e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
7230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private float handleText(TextPaint wp, int start, int end,
7240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl,
7250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            Canvas c, float x, int top, int y, int bottom,
726e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            FontMetricsInt fmi, boolean needWidth) {
727e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
728850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        // Get metrics first (even for empty strings or "0" width runs)
729850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        if (fmi != null) {
730850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio            expandMetricsFromPaint(fmi, wp);
731850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        }
732e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int runLen = end - start;
734850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        // No need to do anything if the run width is "0"
735850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        if (runLen == 0) {
736850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio            return 0f;
737850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        }
738850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio
739850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        float ret = 0;
740850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio
7410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int contextLen = contextEnd - contextStart;
742c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne        if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) {
7430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
744e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mCharsValid) {
7450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mChars, start, runLen,
7460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        contextStart, contextLen, flags, null, 0);
747e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
7480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                int delta = mStart;
7490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mText, delta + start,
7500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        delta + end, delta + contextStart, delta + contextEnd,
7510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        flags, null, 0);
752e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
753e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
754e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
755e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (c != null) {
756e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runIsRtl) {
757e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                x -= ret;
758e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
759e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
760e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (wp.bgColor != 0) {
761dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                int previousColor = wp.getColor();
762dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                Paint.Style previousStyle = wp.getStyle();
763dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
764e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setColor(wp.bgColor);
765e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setStyle(Paint.Style.FILL);
766e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                c.drawRect(x, top, x + ret, bottom, wp);
767e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
768dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setStyle(previousStyle);
769dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setColor(previousColor);
770dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne            }
771dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
772c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne            if (wp.underlineColor != 0) {
773dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h
774e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize();
775dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
776dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                int previousColor = wp.getColor();
777dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                Paint.Style previousStyle = wp.getStyle();
778e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                boolean previousAntiAlias = wp.isAntiAlias();
779dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
780dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setStyle(Paint.Style.FILL);
781e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                wp.setAntiAlias(true);
782e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin
783c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne                wp.setColor(wp.underlineColor);
784c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne                c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThickness, wp);
785dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
786dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setStyle(previousStyle);
787dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setColor(previousColor);
788e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                wp.setAntiAlias(previousAntiAlias);
789e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
7920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y + wp.baselineShift);
793e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
794e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
795e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
796e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
797e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
798e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
799e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering a replacement.
800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
801bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
802e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param replacement the replacement
803e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the work paint
804e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the run
805e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
806e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
807e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if not rendering
808e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the replacement closest to the leading margin
809e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
810e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
811e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
812e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
813e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the replacement is needed
814e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
815e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
816e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
817e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float handleReplacement(ReplacementSpan replacement, TextPaint wp,
818bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            int start, int limit, boolean runIsRtl, Canvas c,
819e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float x, int top, int y, int bottom, FontMetricsInt fmi,
8200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean needWidth) {
821e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
822e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ret = 0;
823e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textStart = mStart + start;
8250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textLimit = mStart + limit;
826e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (needWidth || (c != null && runIsRtl)) {
8288a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousTop = 0;
8298a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousAscent = 0;
8308a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousDescent = 0;
8318a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousBottom = 0;
8328a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousLeading = 0;
8338a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
8348a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            boolean needUpdateMetrics = (fmi != null);
8358a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
8368a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            if (needUpdateMetrics) {
8378a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousTop     = fmi.top;
8388a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousAscent  = fmi.ascent;
8398a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousDescent = fmi.descent;
8408a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousBottom  = fmi.bottom;
8418a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousLeading = fmi.leading;
8428a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            }
8438a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
8440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            ret = replacement.getSize(wp, mText, textStart, textLimit, fmi);
8458a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
8468a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            if (needUpdateMetrics) {
8478a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,
8488a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                        previousLeading);
8498a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            }
8500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
851e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (c != null) {
8530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (runIsRtl) {
8540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x -= ret;
855e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
8560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            replacement.draw(c, mText, textStart, textLimit,
8570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, top, y, bottom, wp);
858e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
859e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
860e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
861e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
862e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
863945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne    private static class SpanSet<E> {
864c1f44830809f0a8526855f13822702ea756214faGilles Debunne        int numberOfSpans;
865c1f44830809f0a8526855f13822702ea756214faGilles Debunne        E[] spans;
866c1f44830809f0a8526855f13822702ea756214faGilles Debunne        int[] spanStarts;
867c1f44830809f0a8526855f13822702ea756214faGilles Debunne        int[] spanEnds;
868c1f44830809f0a8526855f13822702ea756214faGilles Debunne        int[] spanFlags;
869c1f44830809f0a8526855f13822702ea756214faGilles Debunne        final Class<? extends E> classType;
870c1f44830809f0a8526855f13822702ea756214faGilles Debunne
871c1f44830809f0a8526855f13822702ea756214faGilles Debunne        SpanSet(Class<? extends E> type) {
872c1f44830809f0a8526855f13822702ea756214faGilles Debunne            classType = type;
873c1f44830809f0a8526855f13822702ea756214faGilles Debunne            numberOfSpans = 0;
874c1f44830809f0a8526855f13822702ea756214faGilles Debunne        }
875945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
876945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        @SuppressWarnings("unchecked")
877c1f44830809f0a8526855f13822702ea756214faGilles Debunne        public void init(Spanned spanned, int start, int limit) {
878c1f44830809f0a8526855f13822702ea756214faGilles Debunne            final E[] allSpans = spanned.getSpans(start, limit, classType);
879945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            final int length = allSpans.length;
880c1f44830809f0a8526855f13822702ea756214faGilles Debunne
881c1f44830809f0a8526855f13822702ea756214faGilles Debunne            if (length > 0 && (spans == null || spans.length < length)) {
882c1f44830809f0a8526855f13822702ea756214faGilles Debunne                // These arrays may end up being too large because of empty spans
883c1f44830809f0a8526855f13822702ea756214faGilles Debunne                spans = (E[]) Array.newInstance(classType, length);
884c1f44830809f0a8526855f13822702ea756214faGilles Debunne                spanStarts = new int[length];
885c1f44830809f0a8526855f13822702ea756214faGilles Debunne                spanEnds = new int[length];
886c1f44830809f0a8526855f13822702ea756214faGilles Debunne                spanFlags = new int[length];
887c1f44830809f0a8526855f13822702ea756214faGilles Debunne            }
888945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
889945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            int count = 0;
890945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            for (int i = 0; i < length; i++) {
891945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final E span = allSpans[i];
892945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
893945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int spanStart = spanned.getSpanStart(span);
894945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int spanEnd = spanned.getSpanEnd(span);
895945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if (spanStart == spanEnd) continue;
896945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
897945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int spanFlag = spanned.getSpanFlags(span);
898945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
899c1f44830809f0a8526855f13822702ea756214faGilles Debunne                spans[i] = span;
900c1f44830809f0a8526855f13822702ea756214faGilles Debunne                spanStarts[i] = spanStart;
901c1f44830809f0a8526855f13822702ea756214faGilles Debunne                spanEnds[i] = spanEnd;
902c1f44830809f0a8526855f13822702ea756214faGilles Debunne                spanFlags[i] = spanFlag;
903945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
904945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                count++;
905945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            }
906945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            numberOfSpans = count;
907945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        }
908945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
9098a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne        public boolean hasSpansIntersecting(int start, int end) {
9108a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne            for (int i = 0; i < numberOfSpans; i++) {
9118a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne                // equal test is valid since both intervals are not empty by construction
9128a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne                if (spanStarts[i] >= end || spanEnds[i] <= start) continue;
9138a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne                return true;
9148a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne            }
9158a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne            return false;
9168a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne        }
9178a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne
918945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        int getNextTransition(int start, int limit) {
919945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            for (int i = 0; i < numberOfSpans; i++) {
920945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int spanStart = spanStarts[i];
921945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int spanEnd = spanEnds[i];
922945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if (spanStart > start && spanStart < limit) limit = spanStart;
923945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if (spanEnd > start && spanEnd < limit) limit = spanEnd;
924945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            }
925945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            return limit;
926945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        }
927c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne
928c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne        public void recycle() {
929c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne            for (int i = 0; i < numberOfSpans; i++) {
930c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne                spans[i] = null; // prevent a leak: no reference kept when TextLine is recycled
931c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne            }
932c3fb7a11ad72c5e51ff93e1ad6958f843af0d5b1Gilles Debunne        }
933945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne    }
934945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
935e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
936e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for handling a unidirectional run.  The run must not
937e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * contain tabs or emoji but can contain styles.
938e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
939bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
940e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
9410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param measureLimit the offset to measure to, between start and limit inclusive
942e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
943e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
944e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null
945e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the end of the run closest to the leading margin
946e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
947e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
948e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
949e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
950e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width is required
951e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
952e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
953e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
954bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float handleRun(int start, int measureLimit,
955e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
9560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int bottom, FontMetricsInt fmi, boolean needWidth) {
957e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
958f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        // Case of an empty line, make sure we update fmi according to mPaint
959f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        if (start == measureLimit) {
960f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne            TextPaint wp = mWorkPaint;
961f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne            wp.set(mPaint);
96215c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            if (fmi != null) {
96315c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio                expandMetricsFromPaint(fmi, wp);
96415c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            }
96515c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            return 0f;
966f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        }
967f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne
968945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        if (mSpanned == null) {
969945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            TextPaint wp = mWorkPaint;
970945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            wp.set(mPaint);
971945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            final int mlimit = measureLimit;
972945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top,
973945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    y, bottom, fmi, needWidth || mlimit < measureLimit);
974945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        }
975945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
976c1f44830809f0a8526855f13822702ea756214faGilles Debunne        mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit);
977c1f44830809f0a8526855f13822702ea756214faGilles Debunne        mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit);
978945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
979e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // Shaping needs to take into account context up to metric boundaries,
980e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // but rendering needs to take into account character style boundaries.
9810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // So we iterate through metric runs to get metric bounds,
9820c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // then within each metric run iterate through character style runs
9830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // for the run bounds.
984945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        final float originalX = x;
9850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        for (int i = start, inext; i < measureLimit; i = inext) {
986e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextPaint wp = mWorkPaint;
987e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            wp.set(mPaint);
988e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
989c1f44830809f0a8526855f13822702ea756214faGilles Debunne            inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) -
990c1f44830809f0a8526855f13822702ea756214faGilles Debunne                    mStart;
991945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            int mlimit = Math.min(inext, measureLimit);
992945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
993945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            ReplacementSpan replacement = null;
994945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
995c1f44830809f0a8526855f13822702ea756214faGilles Debunne            for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) {
996945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT
997945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                // empty by construction. This special case in getSpans() explains the >= & <= tests
998c1f44830809f0a8526855f13822702ea756214faGilles Debunne                if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) ||
999c1f44830809f0a8526855f13822702ea756214faGilles Debunne                        (mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue;
1000c1f44830809f0a8526855f13822702ea756214faGilles Debunne                MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j];
1001945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if (span instanceof ReplacementSpan) {
1002945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    replacement = (ReplacementSpan)span;
1003945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                } else {
1004945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    // We might have a replacement that uses the draw
1005945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    // state, otherwise measure state would suffice.
1006945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    span.updateDrawState(wp);
1007e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
1008e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
1009e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1010945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            if (replacement != null) {
1011945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y,
1012945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                        bottom, fmi, needWidth || mlimit < measureLimit);
1013945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                continue;
1014945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            }
1015945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
1016945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            if (c == null) {
10170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x += handleText(wp, i, mlimit, i, inext, runIsRtl, c, x, top,
10180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        y, bottom, fmi, needWidth || mlimit < measureLimit);
10190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            } else {
10200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = i, jnext; j < mlimit; j = jnext) {
1021c1f44830809f0a8526855f13822702ea756214faGilles Debunne                    jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) -
1022945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                            mStart;
1023e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
10240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    wp.set(mPaint);
1025c1f44830809f0a8526855f13822702ea756214faGilles Debunne                    for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
1026945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                        // Intentionally using >= and <= as explained above
1027c1f44830809f0a8526855f13822702ea756214faGilles Debunne                        if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) ||
1028c1f44830809f0a8526855f13822702ea756214faGilles Debunne                                (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;
1029945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
1030c1f44830809f0a8526855f13822702ea756214faGilles Debunne                        CharacterStyle span = mCharacterStyleSpanSet.spans[k];
10310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateDrawState(wp);
1032e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
10330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
10340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
10350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            top, y, bottom, fmi, needWidth || jnext < measureLimit);
1036e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
1037e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
1038e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1039e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1040945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        return x - originalX;
1041e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1042e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1043e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
1044e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Render a text run with the set-up paint.
1045e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
1046e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas
1047e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the paint used to render the text
10480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param start the start of the run
10490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the run
10500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextStart the start of context for the run
10510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextEnd the end of the context for the run
1052e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
1053e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the x position of the left edge of the run
1054e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline of the run
1055e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
10560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private void drawTextRun(Canvas c, TextPaint wp, int start, int end,
10570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl, float x, int y) {
1058e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1059f47d7405bbcb25d7cdf89ebb059f41520fe9ab87Doug Felt        int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR;
1060e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
10610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int count = end - start;
10620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextCount = contextEnd - contextStart;
10630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mChars, start, count, contextStart, contextCount,
10640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y, flags, wp);
1065e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
10660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int delta = mStart;
10670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mText, delta + start, delta + end,
10680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    delta + contextStart, delta + contextEnd, x, y, flags, wp);
1069e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1070e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1071e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1072e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
1073e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the ascent of the text at start.  This is used for scaling
1074e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * emoji.
1075e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
1076e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param pos the line-relative position
1077e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the ascent of the text at start
1078e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
1079e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float ascent(int pos) {
1080e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mSpanned == null) {
1081e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
1082e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1083e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1084e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        pos += mStart;
1085945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1, MetricAffectingSpan.class);
1086e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (spans.length == 0) {
1087e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
1088e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1089e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1090e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextPaint wp = mWorkPaint;
1091e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        wp.set(mPaint);
1092e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (MetricAffectingSpan span : spans) {
1093e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            span.updateMeasureState(wp);
1094e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1095e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return wp.ascent();
1096e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1097e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1098e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
1099e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next tab position.
1100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
1101e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param h the (unsigned) offset from the leading margin
1102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the (unsigned) tab position after this offset
1103e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
1104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float nextTab(float h) {
1105c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (mTabs != null) {
1106c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return mTabs.nextTab(h);
1107e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1108c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return TabStops.nextDefaultStop(h, TAB_INCREMENT);
1109e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1110e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1111e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static final int TAB_INCREMENT = 20;
1112e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt}
1113