TextLine.java revision 8a5137a5aeba39cbc2c57c83ef79241b446d0cb7
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
33e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/**
34e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Represents a line of styled text, for measuring in visual order and
35e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * for rendering.
36e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
37e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Get a new instance using obtain(), and when finished with it, return it
38e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * to the pool using recycle().
39e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
40e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Call set to prepare the instance for use, then either draw, measure,
41e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * metrics, or caretToLeftRightOf.
42e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
43e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @hide
44e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */
45e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltclass TextLine {
46bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private static final boolean DEBUG = false;
47bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy
48e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private TextPaint mPaint;
49e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private CharSequence mText;
50e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mStart;
51e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mLen;
52e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mDir;
53e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Directions mDirections;
54e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mHasTabs;
55c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private TabStops mTabs;
56e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private char[] mChars;
57e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mCharsValid;
58e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Spanned mSpanned;
59345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne    private final TextPaint mWorkPaint = new TextPaint();
60e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
61bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private static final TextLine[] sCached = new TextLine[3];
62e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
63e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
64e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns a new TextLine from the shared pool.
65e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
66e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return an uninitialized TextLine
67e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
68e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine obtain() {
69e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl;
70bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        synchronized (sCached) {
71bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            for (int i = sCached.length; --i >= 0;) {
72bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                if (sCached[i] != null) {
73bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    tl = sCached[i];
74bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    sCached[i] = null;
75e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return tl;
76e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
77e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
78e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
79e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl = new TextLine();
80bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        if (DEBUG) {
81bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            Log.v("TLINE", "new: " + tl);
82bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        }
83e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return tl;
84e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
85e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
86e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
87e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Puts a TextLine back into the shared pool. Do not use this TextLine once
88e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * it has been returned.
89e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param tl the textLine
90e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return null, as a convenience from clearing references to the provided
91e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * TextLine
92e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
93e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine recycle(TextLine tl) {
94e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mText = null;
95e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mPaint = null;
96e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mDirections = null;
97bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        synchronized(sCached) {
98bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            for (int i = 0; i < sCached.length; ++i) {
99bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                if (sCached[i] == null) {
100bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    sCached[i] = tl;
101f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne                    break;
102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
103e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
105e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return null;
106e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
107e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
108e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
109e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Initializes a TextLine and prepares it for use.
110e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
111e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param paint the base paint for the line
112e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param text the text, can be Styled
113e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the line relative to the text
114e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the line relative to the text
115e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param dir the paragraph direction of this line
116e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param directions the directions information of this line
117e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param hasTabs true if the line might contain tabs or emoji
118c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param tabStops the tabStops. Can be null.
119e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
121c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            Directions directions, boolean hasTabs, TabStops tabStops) {
122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mPaint = paint;
123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mText = text;
124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mStart = start;
125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mLen = limit - start;
126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDir = dir;
127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDirections = directions;
1288059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if (mDirections == null) {
1298059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            throw new IllegalArgumentException("Directions cannot be null");
1308059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        }
131e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mHasTabs = hasTabs;
132e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mSpanned = null;
133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
134e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean hasReplacement = false;
135e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (text instanceof Spanned) {
136e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            mSpanned = (Spanned) text;
1371e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            ReplacementSpan[] spans = mSpanned.getSpans(start, limit, ReplacementSpan.class);
1381e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class);
1391e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            hasReplacement = spans.length > 0;
140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1421e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
144e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
145e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mChars == null || mChars.length < mLen) {
146e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                mChars = new char[ArrayUtils.idealCharArraySize(mLen)];
147e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
148e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextUtils.getChars(text, start, limit, mChars, 0);
1490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (hasReplacement) {
1500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Handle these all at once so we don't have to do it as we go.
1510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Replace the first character of each replacement run with the
1520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // object-replacement character and the remainder with zero width
1530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // non-break space aka BOM.  Cursor movement code skips these
1540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // zero-width characters.
1550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                char[] chars = mChars;
1560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int i = start, inext; i < limit; i = inext) {
1571e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    inext = mSpanned.nextSpanTransition(i, limit, ReplacementSpan.class);
1581e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    ReplacementSpan[] spans = mSpanned.getSpans(i, inext, ReplacementSpan.class);
1591e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class);
1601e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    if (spans.length > 0) {
1611e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                        // transition into a span
1620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        chars[i - start] = '\ufffc';
1630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        for (int j = i - start + 1, e = inext - start; j < e; ++j) {
1640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip
1650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        }
1660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
1670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
1680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
169e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
170c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        mTabs = tabStops;
171e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
172e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
173e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
174e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Renders the TextLine.
175e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
176e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to render on
177e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the leading margin position
178e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
179e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
180e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
181e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
182e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void draw(Canvas c, float x, int top, int y, int bottom) {
183e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
185bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                drawRun(c, 0, mLen, false, x, top, y, bottom, false);
186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
187e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
189bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                drawRun(c, 0, mLen, true, x, top, y, bottom, false);
190e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
193e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
195e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
196e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        RectF emojiRect = null;
197e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
198e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lastRunIndex = runs.length - 2;
199e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
200e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
211e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
212e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
213e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = mChars[j];
214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(mChars, j);
216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
225e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
226bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom,
227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            i != lastRunIndex || j != mLen);
228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    } else if (bm != null) {
232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bitmapHeight = bm.getHeight();
234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float scale = -bmAscent / bitmapHeight;
235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float width = bm.getWidth() * scale;
236e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (emojiRect == null) {
238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            emojiRect = new RectF();
239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        emojiRect.set(x + h, y + bmAscent,
241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                                x + h + width, y);
242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        c.drawBitmap(bm, null, emojiRect, mPaint);
243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += width;
244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns metrics information for the entire line.
254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives font metrics information, can be null
256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the line
257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float metrics(FontMetricsInt fmi) {
259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return measure(mLen, false, fmi);
260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns information about a position on the line.
264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the line-relative character offset, between 0 and the
266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * line length, inclusive
267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param trailing true to measure the trailing edge of the character
268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * before offset, false to measure the leading edge of the character
269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * at offset.
270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character, can be null.
272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed offset from the leading margin to the requested
273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character edge.
274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float measure(int offset, boolean trailing, FontMetricsInt fmi) {
276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int target = trailing ? offset - 1 : offset;
277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (target < 0) {
278e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return 0;
279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
280e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
283e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
285bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                return measureRun(0, offset, mLen, false, fmi);
286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
288bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                return measureRun(0, offset, mLen, true, fmi);
289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
290e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        char[] chars = mChars;
293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
298e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
299e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = chars[j];
309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(chars, j);
311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean inSegment = target >= segstart && target < j;
322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment && advance) {
325bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                        return h += measureRun(segstart, offset, j, runIsRtl, fmi);
326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
328bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    float w = measureRun(segstart, j, j, runIsRtl, fmi);
329e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    h += advance ? w : -w;
330e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment) {
332bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                        return h += measureRun(segstart, offset, j, runIsRtl, null);
333e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
334e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
335e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (offset == j) {
337e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
338e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (target == j) {
341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
343e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (bm != null) {
346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float wid = bm.getWidth() * -bmAscent / bm.getHeight();
348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += mDir * wid;
349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
357e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return h;
358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
360e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Draws a unidirectional (but possibly multi-styled) run of text.
362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
363bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to draw on
365e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start
366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit
367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
368e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the position of the run that is closest to the leading margin
369e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width value is required.
373e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run, based on the paragraph direction.
374e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Only valid if needWidth is true.
375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
376bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float drawRun(Canvas c, int start,
377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, float x, int top, int y, int bottom,
378e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean needWidth) {
379e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
380e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
381bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            float w = -measureRun(start, limit, limit, runIsRtl, null);
382bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            handleRun(start, limit, limit, runIsRtl, c, x + w, top,
3830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    y, bottom, null, false);
384e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return w;
385e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
386e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
387bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        return handleRun(start, limit, limit, runIsRtl, c, x, top,
3880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                y, bottom, null, needWidth);
389e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
390e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
391e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
392e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Measures a unidirectional (but possibly multi-styled) run of text.
393e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
394bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
396e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset to measure to, between start and limit inclusive
397e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit of the run
398e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
399e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
400e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * run, can be null.
401e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width from the start of the run to the leading edge
402e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the character at offset, based on the run (not paragraph) direction
403e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
404bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float measureRun(int start, int offset, int limit, boolean runIsRtl,
405bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            FontMetricsInt fmi) {
406bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true);
407e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
408e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
409e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
410e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Walk the cursor through this line, skipping conjuncts and
411e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * zero-width characters.
412e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
413e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * <p>This function cannot properly walk the cursor off the ends of the line
414e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * since it does not know about any shaping on the previous/following line
415e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * that might affect the cursor position. Callers must either avoid these
416e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * situations or handle the result specially.
417e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
418e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param cursor the starting position of the cursor, between 0 and the
419e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * length of the line, inclusive
420e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param toLeft true if the caret is moving to the left.
421e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset.  If it is less than 0 or greater than the length
422e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the line, the previous/following line should be examined to get the
423e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * actual offset.
424e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
425e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    int getOffsetToLeftRightOf(int cursor, boolean toLeft) {
426e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 1) The caret marks the leading edge of a character. The character
427e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // logically before it might be on a different level, and the active caret
428e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // position is on the character at the lower level. If that character
429e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // was the previous character, the caret is on its trailing edge.
430e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 2) Take this character/edge and move it in the indicated direction.
431e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // This gives you a new character and a new edge.
432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 3) This position is between two visually adjacent characters.  One of
433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // these might be at a lower level.  The active position is on the
434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // character at the lower level.
435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 4) If the active position is on the trailing edge of the character,
436e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // the new caret position is the following logical character, else it
437e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // is the character.
438e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
439e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineStart = 0;
440e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineEnd = mLen;
441e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean paraIsRtl = mDir == -1;
442e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
443e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
444e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1;
445e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean trailing = false;
446e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
447e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (cursor == lineStart) {
448e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = -2;
449e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else if (cursor == lineEnd) {
450e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = runs.length;
451e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
452e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // First, get information about the run containing the character with
453e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the active caret.
454e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          for (runIndex = 0; runIndex < runs.length; runIndex += 2) {
455e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runStart = lineStart + runs[runIndex];
456e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (cursor >= runStart) {
457e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK);
458e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (runLimit > lineEnd) {
459e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  runLimit = lineEnd;
460e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
461e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor < runLimit) {
462e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
463e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    Layout.RUN_LEVEL_MASK;
464e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (cursor == runStart) {
465e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // The caret is on a run boundary, see if we should
466e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // use the position on the trailing edge of the previous
467e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // logical character instead.
468e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit;
469e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int pos = cursor - 1;
470e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) {
471e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    prevRunStart = lineStart + runs[prevRunIndex];
472e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (pos >= prevRunStart) {
473e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      prevRunLimit = prevRunStart +
474e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK);
475e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (prevRunLimit > lineEnd) {
476e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          prevRunLimit = lineEnd;
477e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
478e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (pos < prevRunLimit) {
479e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT)
480e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            & Layout.RUN_LEVEL_MASK;
481e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (prevRunLevel < runLevel) {
482e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          // Start from logically previous character.
483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runIndex = prevRunIndex;
484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLevel = prevRunLevel;
485e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runStart = prevRunStart;
486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLimit = prevRunLimit;
487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          trailing = true;
488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          break;
489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
498e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // caret might be == lineEnd.  This is generally a space or paragraph
500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // separator and has an associated run, but might be the end of
501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // text, in which case it doesn't.  If that happens, we ran off the
502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // end of the run list, and runIndex == runs.length.  In this case,
503e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we are at a run boundary so we skip the below test.
504e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (runIndex != runs.length) {
505e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean runIsRtl = (runLevel & 0x1) != 0;
506e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean advance = toLeft == runIsRtl;
507e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor != (advance ? runLimit : runStart) || advance != trailing) {
508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // Moving within or into the run, so we can move logically.
5090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                  newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit,
5100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                          runIsRtl, cursor, advance);
511e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // If the new position is internal to the run, we're at the strong
512e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // position already so we're finished.
513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  if (newCaret != (advance ? runLimit : runStart)) {
514e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      return newCaret;
515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
516e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
520e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // If newCaret is -1, we're starting at a run boundary and crossing
521e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // into another run. Otherwise we've arrived at a run boundary, and
522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to figure out which character to attach to.  Note we might
523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to run this twice, if we cross a run boundary and end up at
524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // another run boundary.
525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        while (true) {
526e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          boolean advance = toLeft == paraIsRtl;
527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          int otherRunIndex = runIndex + (advance ? 2 : -2);
528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (otherRunIndex >= 0 && otherRunIndex < runs.length) {
529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunStart = lineStart + runs[otherRunIndex];
530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLimit = otherRunStart +
531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK);
532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLimit > lineEnd) {
533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                otherRunLimit = lineEnd;
534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
535e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
536e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Layout.RUN_LEVEL_MASK;
537e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean otherRunIsRtl = (otherRunLevel & 1) != 0;
538e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
539e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            advance = toLeft == otherRunIsRtl;
540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (newCaret == -1) {
5410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart,
5420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        otherRunLimit, otherRunIsRtl,
543e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        advance ? otherRunStart : otherRunLimit, advance);
544e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (newCaret == (advance ? otherRunLimit : otherRunStart)) {
545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // Crossed and ended up at a new boundary,
546e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // repeat a second and final time.
547e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runIndex = otherRunIndex;
548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runLevel = otherRunLevel;
549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    continue;
550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
551e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
552e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            // The new caret is at a boundary.
555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLevel < runLevel) {
556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // The strong character is in the other run.
557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? otherRunStart : otherRunLimit;
558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            break;
560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
562e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret == -1) {
563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // We're walking off the end of the line.  The paragraph
564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // level is always equal to or lower than any internal level, so
565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // the boundaries get the strong caret.
5660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt              newCaret = advance ? mLen + 1 : -1;
567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              break;
568e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Else we've arrived at the end of the line.  That's a strong position.
571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // We might have arrived here by crossing over a run with no internal
572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // breaks and dropping out of the above loop before advancing one final
573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // time, so reset the caret.
574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Note, we use '<=' below to handle a situation where the only run
575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // on the line is a counter-directional run.  If we're not advancing,
576e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we can end up at the 'lineEnd' position but the caret we want is at
577e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the lineStart.
578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret <= lineEnd) {
579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? lineEnd : lineStart;
580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          break;
582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
584e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return newCaret;
585e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
586e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
587e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
588e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next valid offset within this directional run, skipping
589e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * conjuncts and zero-width characters.  This should not be called to walk
590cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunne     * off the end of the line, since the returned values might not be valid
5910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * on neighboring lines.  If the returned offset is less than zero or
5920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * greater than the line length, the offset should be recomputed on the
5930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * preceding or following line, respectively.
594e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
595e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
5960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runStart the start of the run
5970c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runLimit the limit of the run
5980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runIsRtl true if the run is right-to-left
599e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset
600e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param after true if the new offset should logically follow the provided
601e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * offset
602e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset
603e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
6040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit,
6050c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean runIsRtl, int offset, boolean after) {
606e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (runIndex < 0 || offset == (after ? mLen : 0)) {
6080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // Walking off end of line.  Since we don't know
6090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // what cursor positions are available on other lines, we can't
6100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // return accurate values.  These are a guess.
611e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (after) {
6120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart;
6130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart;
6150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
6160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        TextPaint wp = mWorkPaint;
6180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        wp.set(mPaint);
6190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanStart = runStart;
6210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanLimit;
6220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mSpanned == null) {
6230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            spanLimit = runLimit;
6240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int target = after ? offset + 1 : offset;
6260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int limit = mStart + runLimit;
6270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            while (true) {
6280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit,
6290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        MetricAffectingSpan.class) - mStart;
6300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (spanLimit >= target) {
6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    break;
632e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
6330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanStart = spanLimit;
6340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart,
6370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    mStart + spanLimit, MetricAffectingSpan.class);
6381e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
6390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (spans.length > 0) {
6410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ReplacementSpan replacement = null;
6420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = 0; j < spans.length; j++) {
6430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    MetricAffectingSpan span = spans[j];
6440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    if (span instanceof ReplacementSpan) {
6450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        replacement = (ReplacementSpan)span;
6460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    } else {
6470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateMeasureState(wp);
6480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
6490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
6500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (replacement != null) {
6520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // If we have a replacement span, we're moving either to
6530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // the start or end of this span.
6540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    return after ? spanLimit : spanStart;
655e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
656e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
657e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
658e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
6600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
6610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mCharsValid) {
6620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
6630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    flags, offset, cursorOpt);
6640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mText, mStart + spanStart,
666345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne                    mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart;
667e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
668e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
669e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
670e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
6710bb000931bb841e75903d655552d1626ae158707Gilles Debunne     * @param wp
6720bb000931bb841e75903d655552d1626ae158707Gilles Debunne     */
6730bb000931bb841e75903d655552d1626ae158707Gilles Debunne    private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) {
6740bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousTop     = fmi.top;
6750bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousAscent  = fmi.ascent;
6760bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousDescent = fmi.descent;
6770bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousBottom  = fmi.bottom;
6780bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousLeading = fmi.leading;
6790bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6800bb000931bb841e75903d655552d1626ae158707Gilles Debunne        wp.getFontMetricsInt(fmi);
6810bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6828a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio        updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,
6838a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousLeading);
6848a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio    }
6858a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
6868a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio    static void updateMetrics(FontMetricsInt fmi, int previousTop, int previousAscent,
6878a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousDescent, int previousBottom, int previousLeading) {
6880bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.top     = Math.min(fmi.top,     previousTop);
6890bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.ascent  = Math.min(fmi.ascent,  previousAscent);
6900bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.descent = Math.max(fmi.descent, previousDescent);
6910bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.bottom  = Math.max(fmi.bottom,  previousBottom);
6920bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.leading = Math.max(fmi.leading, previousLeading);
6930bb000931bb841e75903d655552d1626ae158707Gilles Debunne    }
6940bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6950bb000931bb841e75903d655552d1626ae158707Gilles Debunne    /**
696e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering text.  The text must
697e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * not include a tab or emoji.
698e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
699e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the working paint
700e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the text
7010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the text
702e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
703e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if rendering is not needed
704e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the run closest to the leading margin
705e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
706e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
708e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
709e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the run is needed
710e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
711e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
712e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
7130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private float handleText(TextPaint wp, int start, int end,
7140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl,
7150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            Canvas c, float x, int top, int y, int bottom,
716e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            FontMetricsInt fmi, boolean needWidth) {
717e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
718850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        // Get metrics first (even for empty strings or "0" width runs)
719850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        if (fmi != null) {
720850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio            expandMetricsFromPaint(fmi, wp);
721850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        }
722e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int runLen = end - start;
724850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        // No need to do anything if the run width is "0"
725850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        if (runLen == 0) {
726850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio            return 0f;
727850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        }
728850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio
729850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        float ret = 0;
730850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio
7310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int contextLen = contextEnd - contextStart;
732c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne        if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) {
7330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
734e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mCharsValid) {
7350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mChars, start, runLen,
7360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        contextStart, contextLen, flags, null, 0);
737e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
7380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                int delta = mStart;
7390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mText, delta + start,
7400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        delta + end, delta + contextStart, delta + contextEnd,
7410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        flags, null, 0);
742e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
743e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
744e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
745e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (c != null) {
746e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runIsRtl) {
747e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                x -= ret;
748e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
749e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
750e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (wp.bgColor != 0) {
751dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                int previousColor = wp.getColor();
752dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                Paint.Style previousStyle = wp.getStyle();
753dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
754e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setColor(wp.bgColor);
755e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setStyle(Paint.Style.FILL);
756e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                c.drawRect(x, top, x + ret, bottom, wp);
757e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
758dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setStyle(previousStyle);
759dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setColor(previousColor);
760dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne            }
761dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
762c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne            if (wp.underlineColor != 0) {
763dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h
764e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize();
765dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
766dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                int previousColor = wp.getColor();
767dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                Paint.Style previousStyle = wp.getStyle();
768e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                boolean previousAntiAlias = wp.isAntiAlias();
769dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
770dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setStyle(Paint.Style.FILL);
771e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                wp.setAntiAlias(true);
772e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin
773c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne                wp.setColor(wp.underlineColor);
774c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne                c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThickness, wp);
775dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
776dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setStyle(previousStyle);
777dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setColor(previousColor);
778e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                wp.setAntiAlias(previousAntiAlias);
779e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
780e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
7820c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y + wp.baselineShift);
783e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
784e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
785e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
786e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
787e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
788e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
789e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering a replacement.
790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
791bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
792e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param replacement the replacement
793e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the work paint
794e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the run
795e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
796e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
797e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if not rendering
798e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the replacement closest to the leading margin
799e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
801e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
802e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
803e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the replacement is needed
804e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
805e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
806e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
807e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float handleReplacement(ReplacementSpan replacement, TextPaint wp,
808bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            int start, int limit, boolean runIsRtl, Canvas c,
809e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float x, int top, int y, int bottom, FontMetricsInt fmi,
8100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean needWidth) {
811e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
812e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ret = 0;
813e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textStart = mStart + start;
8150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textLimit = mStart + limit;
816e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (needWidth || (c != null && runIsRtl)) {
8188a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousTop = 0;
8198a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousAscent = 0;
8208a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousDescent = 0;
8218a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousBottom = 0;
8228a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            int previousLeading = 0;
8238a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
8248a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            boolean needUpdateMetrics = (fmi != null);
8258a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
8268a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            if (needUpdateMetrics) {
8278a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousTop     = fmi.top;
8288a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousAscent  = fmi.ascent;
8298a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousDescent = fmi.descent;
8308a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousBottom  = fmi.bottom;
8318a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                previousLeading = fmi.leading;
8328a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            }
8338a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
8340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            ret = replacement.getSize(wp, mText, textStart, textLimit, fmi);
8358a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio
8368a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            if (needUpdateMetrics) {
8378a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,
8388a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio                        previousLeading);
8398a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio            }
8400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
841e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (c != null) {
8430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (runIsRtl) {
8440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x -= ret;
845e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
8460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            replacement.draw(c, mText, textStart, textLimit,
8470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, top, y, bottom, wp);
848e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
849e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
850e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
851e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
852e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
853e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
854e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for handling a unidirectional run.  The run must not
855e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * contain tabs or emoji but can contain styles.
856e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
857bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
858e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
8590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param measureLimit the offset to measure to, between start and limit inclusive
860e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
861e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
862e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null
863e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the end of the run closest to the leading margin
864e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
865e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
866e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
867e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
868e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width is required
869e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
870e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
871e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
872bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float handleRun(int start, int measureLimit,
873e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
8740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int bottom, FontMetricsInt fmi, boolean needWidth) {
875e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
876f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        // Case of an empty line, make sure we update fmi according to mPaint
877f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        if (start == measureLimit) {
878f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne            TextPaint wp = mWorkPaint;
879f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne            wp.set(mPaint);
88015c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            if (fmi != null) {
88115c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio                expandMetricsFromPaint(fmi, wp);
88215c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            }
88315c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            return 0f;
884f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        }
885f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne
886e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // Shaping needs to take into account context up to metric boundaries,
887e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // but rendering needs to take into account character style boundaries.
8880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // So we iterate through metric runs to get metric bounds,
8890c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // then within each metric run iterate through character style runs
8900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // for the run bounds.
891e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ox = x;
8920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        for (int i = start, inext; i < measureLimit; i = inext) {
893e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextPaint wp = mWorkPaint;
894e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            wp.set(mPaint);
895e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int mlimit;
897e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mSpanned == null) {
898e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                inext = limit;
8990c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                mlimit = measureLimit;
900e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
901e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                inext = mSpanned.nextSpanTransition(mStart + i, mStart + limit,
902e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        MetricAffectingSpan.class) - mStart;
903e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
9040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                mlimit = inext < measureLimit ? inext : measureLimit;
905e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + i,
9060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        mStart + mlimit, MetricAffectingSpan.class);
9071e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
908e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
909e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (spans.length > 0) {
910e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    ReplacementSpan replacement = null;
911e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    for (int j = 0; j < spans.length; j++) {
912e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        MetricAffectingSpan span = spans[j];
913e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (span instanceof ReplacementSpan) {
914e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            replacement = (ReplacementSpan)span;
915e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else {
9160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            // We might have a replacement that uses the draw
9170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            // state, otherwise measure state would suffice.
9180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            span.updateDrawState(wp);
919e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
920e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
921e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
922e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (replacement != null) {
923bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                        x += handleReplacement(replacement, wp, i,
9240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                                mlimit, runIsRtl, c, x, top, y, bottom, fmi,
9250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                                needWidth || mlimit < measureLimit);
926e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        continue;
927e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
928e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
929e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
930e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
9310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (mSpanned == null || c == null) {
9320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x += handleText(wp, i, mlimit, i, inext, runIsRtl, c, x, top,
9330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        y, bottom, fmi, needWidth || mlimit < measureLimit);
9340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            } else {
9350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = i, jnext; j < mlimit; j = jnext) {
9360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    jnext = mSpanned.nextSpanTransition(mStart + j,
9370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            mStart + mlimit, CharacterStyle.class) - mStart;
938e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
9390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    CharacterStyle[] spans = mSpanned.getSpans(mStart + j,
9400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            mStart + jnext, CharacterStyle.class);
9411e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    spans = TextUtils.removeEmptySpans(spans, mSpanned, CharacterStyle.class);
942e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
9430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    wp.set(mPaint);
9440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    for (int k = 0; k < spans.length; k++) {
9450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        CharacterStyle span = spans[k];
9460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateDrawState(wp);
947e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
9480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
9490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
9500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            top, y, bottom, fmi, needWidth || jnext < measureLimit);
951e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
952e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
953e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
954e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
955e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return x - ox;
956e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
957e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
958e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
959e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Render a text run with the set-up paint.
960e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
961e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas
962e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the paint used to render the text
9630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param start the start of the run
9640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the run
9650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextStart the start of context for the run
9660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextEnd the end of the context for the run
967e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
968e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the x position of the left edge of the run
969e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline of the run
970e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
9710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private void drawTextRun(Canvas c, TextPaint wp, int start, int end,
9720c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl, float x, int y) {
973e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
974f47d7405bbcb25d7cdf89ebb059f41520fe9ab87Doug Felt        int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR;
975e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
9760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int count = end - start;
9770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextCount = contextEnd - contextStart;
9780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mChars, start, count, contextStart, contextCount,
9790c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y, flags, wp);
980e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
9810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int delta = mStart;
9820c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mText, delta + start, delta + end,
9830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    delta + contextStart, delta + contextEnd, x, y, flags, wp);
984e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
985e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
986e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
987e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
988e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the ascent of the text at start.  This is used for scaling
989e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * emoji.
990e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
991e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param pos the line-relative position
992e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the ascent of the text at start
993e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
994e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float ascent(int pos) {
995e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mSpanned == null) {
996e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
997e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
998e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
999e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        pos += mStart;
1000e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1,
1001e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                MetricAffectingSpan.class);
1002e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (spans.length == 0) {
1003e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
1004e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1005e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1006e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextPaint wp = mWorkPaint;
1007e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        wp.set(mPaint);
1008e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (MetricAffectingSpan span : spans) {
1009e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            span.updateMeasureState(wp);
1010e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1011e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return wp.ascent();
1012e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1013e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1014e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
1015e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next tab position.
1016e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
1017e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param h the (unsigned) offset from the leading margin
1018e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the (unsigned) tab position after this offset
1019e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
1020e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float nextTab(float h) {
1021c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (mTabs != null) {
1022c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return mTabs.nextTab(h);
1023e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1024c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return TabStops.nextDefaultStop(h, TAB_INCREMENT);
1025e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1026e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1027e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static final int TAB_INCREMENT = 20;
1028e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt}
1029