TextLine.java revision 945ee9b1661e60e0074d4f16f61fc147c728c6bf
1e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/*
2e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Copyright (C) 2010 The Android Open Source Project
3e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
4e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Licensed under the Apache License, Version 2.0 (the "License");
5e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * you may not use this file except in compliance with the License.
6e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * You may obtain a copy of the License at
7e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
8e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *      http://www.apache.org/licenses/LICENSE-2.0
9e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
10e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Unless required by applicable law or agreed to in writing, software
11e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * distributed under the License is distributed on an "AS IS" BASIS,
12e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * See the License for the specific language governing permissions and
14e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * limitations under the License.
15e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */
16e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
17e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltpackage android.text;
18e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
19e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Bitmap;
20e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Canvas;
21e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint;
22e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint.FontMetricsInt;
23cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunneimport android.graphics.RectF;
24e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.Layout.Directions;
25c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.Layout.TabStops;
26e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.CharacterStyle;
27e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.MetricAffectingSpan;
28e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.ReplacementSpan;
29e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.util.Log;
30e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
31dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunneimport com.android.internal.util.ArrayUtils;
32dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
33945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunneimport java.lang.reflect.Array;
34945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
35e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/**
36e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Represents a line of styled text, for measuring in visual order and
37e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * for rendering.
38e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
39e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Get a new instance using obtain(), and when finished with it, return it
40e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * to the pool using recycle().
41e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
42e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Call set to prepare the instance for use, then either draw, measure,
43e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * metrics, or caretToLeftRightOf.
44e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
45e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @hide
46e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */
47e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltclass TextLine {
48bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private static final boolean DEBUG = false;
49bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy
50e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private TextPaint mPaint;
51e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private CharSequence mText;
52e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mStart;
53e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mLen;
54e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mDir;
55e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Directions mDirections;
56e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mHasTabs;
57c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private TabStops mTabs;
58e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private char[] mChars;
59e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mCharsValid;
60e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Spanned mSpanned;
61345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne    private final TextPaint mWorkPaint = new TextPaint();
62e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
63bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private static final TextLine[] sCached = new TextLine[3];
64e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
65e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
66e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns a new TextLine from the shared pool.
67e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
68e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return an uninitialized TextLine
69e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
70e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine obtain() {
71e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl;
72bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        synchronized (sCached) {
73bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            for (int i = sCached.length; --i >= 0;) {
74bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                if (sCached[i] != null) {
75bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    tl = sCached[i];
76bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    sCached[i] = null;
77e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return tl;
78e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
79e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
80e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
81e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl = new TextLine();
82bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        if (DEBUG) {
83bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            Log.v("TLINE", "new: " + tl);
84bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        }
85e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return tl;
86e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
87e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
88e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
89e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Puts a TextLine back into the shared pool. Do not use this TextLine once
90e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * it has been returned.
91e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param tl the textLine
92e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return null, as a convenience from clearing references to the provided
93e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * TextLine
94e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
95e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine recycle(TextLine tl) {
96e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mText = null;
97e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mPaint = null;
98e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mDirections = null;
99bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        synchronized(sCached) {
100bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            for (int i = 0; i < sCached.length; ++i) {
101bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                if (sCached[i] == null) {
102bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    sCached[i] = tl;
103f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne                    break;
104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
105e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
106e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
107e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return null;
108e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
109e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
110e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
111e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Initializes a TextLine and prepares it for use.
112e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
113e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param paint the base paint for the line
114e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param text the text, can be Styled
115e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the line relative to the text
116e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the line relative to the text
117e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param dir the paragraph direction of this line
118e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param directions the directions information of this line
119e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param hasTabs true if the line might contain tabs or emoji
120c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param tabStops the tabStops. Can be null.
121e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
123c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            Directions directions, boolean hasTabs, TabStops tabStops) {
124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mPaint = paint;
125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mText = text;
126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mStart = start;
127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mLen = limit - start;
128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDir = dir;
129e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDirections = directions;
1308059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if (mDirections == null) {
1318059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            throw new IllegalArgumentException("Directions cannot be null");
1328059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        }
133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mHasTabs = hasTabs;
134e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mSpanned = null;
135e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
136e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean hasReplacement = false;
137e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (text instanceof Spanned) {
138e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            mSpanned = (Spanned) text;
1391e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            ReplacementSpan[] spans = mSpanned.getSpans(start, limit, ReplacementSpan.class);
1401e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class);
1411e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            hasReplacement = spans.length > 0;
142e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1441e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
145e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
146e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
147e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mChars == null || mChars.length < mLen) {
148e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                mChars = new char[ArrayUtils.idealCharArraySize(mLen)];
149e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
150e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextUtils.getChars(text, start, limit, mChars, 0);
1510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (hasReplacement) {
1520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Handle these all at once so we don't have to do it as we go.
1530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Replace the first character of each replacement run with the
1540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // object-replacement character and the remainder with zero width
1550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // non-break space aka BOM.  Cursor movement code skips these
1560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // zero-width characters.
1570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                char[] chars = mChars;
1580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int i = start, inext; i < limit; i = inext) {
1591e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    inext = mSpanned.nextSpanTransition(i, limit, ReplacementSpan.class);
1601e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    ReplacementSpan[] spans = mSpanned.getSpans(i, inext, ReplacementSpan.class);
1611e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class);
1621e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    if (spans.length > 0) {
1631e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                        // transition into a span
1640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        chars[i - start] = '\ufffc';
1650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        for (int j = i - start + 1, e = inext - start; j < e; ++j) {
1660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip
1670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        }
1680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
1690c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
1700c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
171e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
172c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        mTabs = tabStops;
173e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
174e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
175e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
176e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Renders the TextLine.
177e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
178e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to render on
179e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the leading margin position
180e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
181e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
182e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
183e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void draw(Canvas c, float x, int top, int y, int bottom) {
185e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
187bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                drawRun(c, 0, mLen, false, x, top, y, bottom, false);
188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
190e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
191bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                drawRun(c, 0, mLen, true, x, top, y, bottom, false);
192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
193e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
195e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
196e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
197e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
198e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        RectF emojiRect = null;
199e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
200e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lastRunIndex = runs.length - 2;
201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
211e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
212e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
213e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = mChars[j];
216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(mChars, j);
218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
225e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
228bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom,
229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            i != lastRunIndex || j != mLen);
230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    } else if (bm != null) {
234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bitmapHeight = bm.getHeight();
236e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float scale = -bmAscent / bitmapHeight;
237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float width = bm.getWidth() * scale;
238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (emojiRect == null) {
240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            emojiRect = new RectF();
241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        emojiRect.set(x + h, y + bmAscent,
243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                                x + h + width, y);
244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        c.drawBitmap(bm, null, emojiRect, mPaint);
245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += width;
246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns metrics information for the entire line.
256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives font metrics information, can be null
258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the line
259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float metrics(FontMetricsInt fmi) {
261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return measure(mLen, false, fmi);
262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns information about a position on the line.
266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the line-relative character offset, between 0 and the
268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * line length, inclusive
269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param trailing true to measure the trailing edge of the character
270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * before offset, false to measure the leading edge of the character
271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * at offset.
272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character, can be null.
274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed offset from the leading margin to the requested
275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character edge.
276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float measure(int offset, boolean trailing, FontMetricsInt fmi) {
278e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int target = trailing ? offset - 1 : offset;
279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (target < 0) {
280e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return 0;
281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
283e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
285e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
287bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                return measureRun(0, offset, mLen, false, fmi);
288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
290bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                return measureRun(0, offset, mLen, true, fmi);
291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        char[] chars = mChars;
295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
298e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
299e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = chars[j];
311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(chars, j);
313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean inSegment = target >= segstart && target < j;
324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment && advance) {
327bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                        return h += measureRun(segstart, offset, j, runIsRtl, fmi);
328e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
329e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
330bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    float w = measureRun(segstart, j, j, runIsRtl, fmi);
331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    h += advance ? w : -w;
332e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
333e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment) {
334bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                        return h += measureRun(segstart, offset, j, runIsRtl, null);
335e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
337e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
338e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (offset == j) {
339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (target == j) {
343e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (bm != null) {
348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float wid = bm.getWidth() * -bmAscent / bm.getHeight();
350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += mDir * wid;
351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
357e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return h;
360e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Draws a unidirectional (but possibly multi-styled) run of text.
364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
365bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to draw on
367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start
368e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit
369e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the position of the run that is closest to the leading margin
371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
373e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
374e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width value is required.
375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run, based on the paragraph direction.
376e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Only valid if needWidth is true.
377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
378bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float drawRun(Canvas c, int start,
379e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, float x, int top, int y, int bottom,
380e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean needWidth) {
381e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
383bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            float w = -measureRun(start, limit, limit, runIsRtl, null);
384bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            handleRun(start, limit, limit, runIsRtl, c, x + w, top,
3850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    y, bottom, null, false);
386e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return w;
387e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
388e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
389bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        return handleRun(start, limit, limit, runIsRtl, c, x, top,
3900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                y, bottom, null, needWidth);
391e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
392e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
393e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
394e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Measures a unidirectional (but possibly multi-styled) run of text.
395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
396bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
397e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
398e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset to measure to, between start and limit inclusive
399e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit of the run
400e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
401e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
402e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * run, can be null.
403e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width from the start of the run to the leading edge
404e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the character at offset, based on the run (not paragraph) direction
405e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
406bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float measureRun(int start, int offset, int limit, boolean runIsRtl,
407bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            FontMetricsInt fmi) {
408bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true);
409e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
410e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
411e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
412e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Walk the cursor through this line, skipping conjuncts and
413e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * zero-width characters.
414e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
415e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * <p>This function cannot properly walk the cursor off the ends of the line
416e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * since it does not know about any shaping on the previous/following line
417e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * that might affect the cursor position. Callers must either avoid these
418e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * situations or handle the result specially.
419e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
420e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param cursor the starting position of the cursor, between 0 and the
421e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * length of the line, inclusive
422e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param toLeft true if the caret is moving to the left.
423e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset.  If it is less than 0 or greater than the length
424e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the line, the previous/following line should be examined to get the
425e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * actual offset.
426e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
427e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    int getOffsetToLeftRightOf(int cursor, boolean toLeft) {
428e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 1) The caret marks the leading edge of a character. The character
429e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // logically before it might be on a different level, and the active caret
430e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // position is on the character at the lower level. If that character
431e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // was the previous character, the caret is on its trailing edge.
432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 2) Take this character/edge and move it in the indicated direction.
433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // This gives you a new character and a new edge.
434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 3) This position is between two visually adjacent characters.  One of
435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // these might be at a lower level.  The active position is on the
436e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // character at the lower level.
437e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 4) If the active position is on the trailing edge of the character,
438e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // the new caret position is the following logical character, else it
439e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // is the character.
440e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
441e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineStart = 0;
442e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineEnd = mLen;
443e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean paraIsRtl = mDir == -1;
444e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
445e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
446e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1;
447e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean trailing = false;
448e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
449e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (cursor == lineStart) {
450e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = -2;
451e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else if (cursor == lineEnd) {
452e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = runs.length;
453e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
454e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // First, get information about the run containing the character with
455e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the active caret.
456e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          for (runIndex = 0; runIndex < runs.length; runIndex += 2) {
457e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runStart = lineStart + runs[runIndex];
458e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (cursor >= runStart) {
459e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK);
460e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (runLimit > lineEnd) {
461e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  runLimit = lineEnd;
462e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
463e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor < runLimit) {
464e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
465e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    Layout.RUN_LEVEL_MASK;
466e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (cursor == runStart) {
467e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // The caret is on a run boundary, see if we should
468e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // use the position on the trailing edge of the previous
469e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // logical character instead.
470e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit;
471e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int pos = cursor - 1;
472e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) {
473e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    prevRunStart = lineStart + runs[prevRunIndex];
474e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (pos >= prevRunStart) {
475e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      prevRunLimit = prevRunStart +
476e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK);
477e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (prevRunLimit > lineEnd) {
478e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          prevRunLimit = lineEnd;
479e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
480e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (pos < prevRunLimit) {
481e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT)
482e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            & Layout.RUN_LEVEL_MASK;
483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (prevRunLevel < runLevel) {
484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          // Start from logically previous character.
485e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runIndex = prevRunIndex;
486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLevel = prevRunLevel;
487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runStart = prevRunStart;
488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLimit = prevRunLimit;
489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          trailing = true;
490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          break;
491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
498e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // caret might be == lineEnd.  This is generally a space or paragraph
502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // separator and has an associated run, but might be the end of
503e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // text, in which case it doesn't.  If that happens, we ran off the
504e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // end of the run list, and runIndex == runs.length.  In this case,
505e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we are at a run boundary so we skip the below test.
506e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (runIndex != runs.length) {
507e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean runIsRtl = (runLevel & 0x1) != 0;
508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean advance = toLeft == runIsRtl;
509e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor != (advance ? runLimit : runStart) || advance != trailing) {
510e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // Moving within or into the run, so we can move logically.
5110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                  newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit,
5120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                          runIsRtl, cursor, advance);
513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // If the new position is internal to the run, we're at the strong
514e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // position already so we're finished.
515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  if (newCaret != (advance ? runLimit : runStart)) {
516e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      return newCaret;
517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
520e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
521e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // If newCaret is -1, we're starting at a run boundary and crossing
523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // into another run. Otherwise we've arrived at a run boundary, and
524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to figure out which character to attach to.  Note we might
525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to run this twice, if we cross a run boundary and end up at
526e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // another run boundary.
527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        while (true) {
528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          boolean advance = toLeft == paraIsRtl;
529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          int otherRunIndex = runIndex + (advance ? 2 : -2);
530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (otherRunIndex >= 0 && otherRunIndex < runs.length) {
531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunStart = lineStart + runs[otherRunIndex];
532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLimit = otherRunStart +
533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK);
534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLimit > lineEnd) {
535e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                otherRunLimit = lineEnd;
536e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
537e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
538e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Layout.RUN_LEVEL_MASK;
539e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean otherRunIsRtl = (otherRunLevel & 1) != 0;
540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
541e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            advance = toLeft == otherRunIsRtl;
542e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (newCaret == -1) {
5430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart,
5440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        otherRunLimit, otherRunIsRtl,
545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        advance ? otherRunStart : otherRunLimit, advance);
546e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (newCaret == (advance ? otherRunLimit : otherRunStart)) {
547e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // Crossed and ended up at a new boundary,
548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // repeat a second and final time.
549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runIndex = otherRunIndex;
550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runLevel = otherRunLevel;
551e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    continue;
552e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            // The new caret is at a boundary.
557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLevel < runLevel) {
558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // The strong character is in the other run.
559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? otherRunStart : otherRunLimit;
560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            break;
562e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret == -1) {
565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // We're walking off the end of the line.  The paragraph
566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // level is always equal to or lower than any internal level, so
567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // the boundaries get the strong caret.
5680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt              newCaret = advance ? mLen + 1 : -1;
569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              break;
570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Else we've arrived at the end of the line.  That's a strong position.
573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // We might have arrived here by crossing over a run with no internal
574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // breaks and dropping out of the above loop before advancing one final
575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // time, so reset the caret.
576e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Note, we use '<=' below to handle a situation where the only run
577e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // on the line is a counter-directional run.  If we're not advancing,
578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we can end up at the 'lineEnd' position but the caret we want is at
579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the lineStart.
580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret <= lineEnd) {
581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? lineEnd : lineStart;
582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          break;
584e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
585e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
586e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return newCaret;
587e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
588e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
589e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
590e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next valid offset within this directional run, skipping
591e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * conjuncts and zero-width characters.  This should not be called to walk
592cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunne     * off the end of the line, since the returned values might not be valid
5930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * on neighboring lines.  If the returned offset is less than zero or
5940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * greater than the line length, the offset should be recomputed on the
5950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * preceding or following line, respectively.
596e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
5980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runStart the start of the run
5990c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runLimit the limit of the run
6000c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runIsRtl true if the run is right-to-left
601e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset
602e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param after true if the new offset should logically follow the provided
603e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * offset
604e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset
605e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
6060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit,
6070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean runIsRtl, int offset, boolean after) {
608e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (runIndex < 0 || offset == (after ? mLen : 0)) {
6100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // Walking off end of line.  Since we don't know
6110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // what cursor positions are available on other lines, we can't
6120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // return accurate values.  These are a guess.
613e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (after) {
6140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart;
6150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart;
6170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
6180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        TextPaint wp = mWorkPaint;
6200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        wp.set(mPaint);
6210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanStart = runStart;
6230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanLimit;
6240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mSpanned == null) {
6250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            spanLimit = runLimit;
6260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int target = after ? offset + 1 : offset;
6280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int limit = mStart + runLimit;
6290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            while (true) {
6300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit,
6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        MetricAffectingSpan.class) - mStart;
6320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (spanLimit >= target) {
6330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    break;
634e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
6350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanStart = spanLimit;
6360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart,
6390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    mStart + spanLimit, MetricAffectingSpan.class);
6401e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
6410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (spans.length > 0) {
6430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ReplacementSpan replacement = null;
6440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = 0; j < spans.length; j++) {
6450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    MetricAffectingSpan span = spans[j];
6460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    if (span instanceof ReplacementSpan) {
6470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        replacement = (ReplacementSpan)span;
6480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    } else {
6490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateMeasureState(wp);
6500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
6510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
6520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (replacement != null) {
6540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // If we have a replacement span, we're moving either to
6550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // the start or end of this span.
6560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    return after ? spanLimit : spanStart;
657e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
658e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
659e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
660e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
6620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
6630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mCharsValid) {
6640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
6650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    flags, offset, cursorOpt);
6660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mText, mStart + spanStart,
668345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne                    mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart;
669e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
670e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
671e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
672e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
6730bb000931bb841e75903d655552d1626ae158707Gilles Debunne     * @param wp
6740bb000931bb841e75903d655552d1626ae158707Gilles Debunne     */
6750bb000931bb841e75903d655552d1626ae158707Gilles Debunne    private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) {
6760bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousTop     = fmi.top;
6770bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousAscent  = fmi.ascent;
6780bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousDescent = fmi.descent;
6790bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousBottom  = fmi.bottom;
6800bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousLeading = fmi.leading;
6810bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6820bb000931bb841e75903d655552d1626ae158707Gilles Debunne        wp.getFontMetricsInt(fmi);
6830bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6840bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.top     = Math.min(fmi.top,     previousTop);
6850bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.ascent  = Math.min(fmi.ascent,  previousAscent);
6860bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.descent = Math.max(fmi.descent, previousDescent);
6870bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.bottom  = Math.max(fmi.bottom,  previousBottom);
6880bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.leading = Math.max(fmi.leading, previousLeading);
6890bb000931bb841e75903d655552d1626ae158707Gilles Debunne    }
6900bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6910bb000931bb841e75903d655552d1626ae158707Gilles Debunne    /**
692e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering text.  The text must
693e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * not include a tab or emoji.
694e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
695e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the working paint
696e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the text
6970c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the text
698e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
699e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if rendering is not needed
700e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the run closest to the leading margin
701e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
702e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
703e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
704e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
705e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the run is needed
706e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
708e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
7090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private float handleText(TextPaint wp, int start, int end,
7100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl,
7110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            Canvas c, float x, int top, int y, int bottom,
712e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            FontMetricsInt fmi, boolean needWidth) {
713e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
714850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        // Get metrics first (even for empty strings or "0" width runs)
715850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        if (fmi != null) {
716850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio            expandMetricsFromPaint(fmi, wp);
717850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        }
718e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int runLen = end - start;
720850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        // No need to do anything if the run width is "0"
721850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        if (runLen == 0) {
722850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio            return 0f;
723850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        }
724850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio
725850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio        float ret = 0;
726850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio
7270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int contextLen = contextEnd - contextStart;
728c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne        if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) {
7290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
730e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mCharsValid) {
7310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mChars, start, runLen,
7320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        contextStart, contextLen, flags, null, 0);
733e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
7340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                int delta = mStart;
7350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mText, delta + start,
7360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        delta + end, delta + contextStart, delta + contextEnd,
7370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        flags, null, 0);
738e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
739e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
740e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
741e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (c != null) {
742e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runIsRtl) {
743e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                x -= ret;
744e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
745e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
746e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (wp.bgColor != 0) {
747dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                int previousColor = wp.getColor();
748dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                Paint.Style previousStyle = wp.getStyle();
749dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
750e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setColor(wp.bgColor);
751e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setStyle(Paint.Style.FILL);
752e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                c.drawRect(x, top, x + ret, bottom, wp);
753e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
754dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setStyle(previousStyle);
755dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setColor(previousColor);
756dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne            }
757dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
758c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne            if (wp.underlineColor != 0) {
759dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h
760e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize();
761dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
762dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                int previousColor = wp.getColor();
763dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                Paint.Style previousStyle = wp.getStyle();
764e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                boolean previousAntiAlias = wp.isAntiAlias();
765dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
766dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setStyle(Paint.Style.FILL);
767e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                wp.setAntiAlias(true);
768e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin
769c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne                wp.setColor(wp.underlineColor);
770c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne                c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThickness, wp);
771dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne
772dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setStyle(previousStyle);
773dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne                wp.setColor(previousColor);
774e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin                wp.setAntiAlias(previousAntiAlias);
775e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
776e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
7780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y + wp.baselineShift);
779e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
780e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
781e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
782e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
783e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
784e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
785e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering a replacement.
786e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
787bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
788e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param replacement the replacement
789e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the work paint
790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the run
791e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
792e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
793e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if not rendering
794e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the replacement closest to the leading margin
795e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
796e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
797e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
798e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
799e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the replacement is needed
800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
801e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
802e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
803e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float handleReplacement(ReplacementSpan replacement, TextPaint wp,
804bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            int start, int limit, boolean runIsRtl, Canvas c,
805e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float x, int top, int y, int bottom, FontMetricsInt fmi,
8060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean needWidth) {
807e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
808e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ret = 0;
809e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textStart = mStart + start;
8110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textLimit = mStart + limit;
812e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (needWidth || (c != null && runIsRtl)) {
8140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            ret = replacement.getSize(wp, mText, textStart, textLimit, fmi);
8150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
816e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (c != null) {
8180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (runIsRtl) {
8190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x -= ret;
820e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
8210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            replacement.draw(c, mText, textStart, textLimit,
8220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, top, y, bottom, wp);
823e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
824e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
825e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
826e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
827e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
828945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne    private static class SpanSet<E> {
829945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        final int numberOfSpans;
830945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        final E[] spans;
831945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        final int[] spanStarts;
832945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        final int[] spanEnds;
833945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        final int[] spanFlags;
834945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
835945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        @SuppressWarnings("unchecked")
836945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        SpanSet(Spanned spanned, int start, int limit, Class<? extends E> type) {
837945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            final E[] allSpans = spanned.getSpans(start, limit, type);
838945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            final int length = allSpans.length;
839945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            // These arrays may end up being too large because of empty spans
840945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            spans = (E[]) Array.newInstance(type, length);
841945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            spanStarts = new int[length];
842945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            spanEnds = new int[length];
843945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            spanFlags = new int[length];
844945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
845945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            int count = 0;
846945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            for (int i = 0; i < length; i++) {
847945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final E span = allSpans[i];
848945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
849945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int spanStart = spanned.getSpanStart(span);
850945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int spanEnd = spanned.getSpanEnd(span);
851945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if (spanStart == spanEnd) continue;
852945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
853945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int spanFlag = spanned.getSpanFlags(span);
854945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int priority = spanFlag & Spanned.SPAN_PRIORITY;
855945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if (priority != 0 && count != 0) {
856945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    int j;
857945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
858945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    for (j = 0; j < count; j++) {
859945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                        final int otherPriority = spanFlags[j] & Spanned.SPAN_PRIORITY;
860945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                        if (priority > otherPriority) break;
861945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    }
862945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
863945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    System.arraycopy(spans, j, spans, j + 1, count - j);
864945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    System.arraycopy(spanStarts, j, spanStarts, j + 1, count - j);
865945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    System.arraycopy(spanEnds, j, spanEnds, j + 1, count - j);
866945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    System.arraycopy(spanFlags, j, spanFlags, j + 1, count - j);
867945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
868945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    spans[j] = span;
869945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    spanStarts[j] = spanStart;
870945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    spanEnds[j] = spanEnd;
871945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    spanFlags[j] = spanFlag;
872945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                } else {
873945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    spans[i] = span;
874945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    spanStarts[i] = spanStart;
875945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    spanEnds[i] = spanEnd;
876945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    spanFlags[i] = spanFlag;
877945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                }
878945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
879945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                count++;
880945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            }
881945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            numberOfSpans = count;
882945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        }
883945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
884945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        int getNextTransition(int start, int limit) {
885945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            for (int i = 0; i < numberOfSpans; i++) {
886945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int spanStart = spanStarts[i];
887945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                final int spanEnd = spanEnds[i];
888945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if (spanStart > start && spanStart < limit) limit = spanStart;
889945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if (spanEnd > start && spanEnd < limit) limit = spanEnd;
890945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            }
891945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            return limit;
892945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        }
893945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne    }
894945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
895e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
896e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for handling a unidirectional run.  The run must not
897e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * contain tabs or emoji but can contain styles.
898e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
899bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
900e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
9010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param measureLimit the offset to measure to, between start and limit inclusive
902e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
903e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
904e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null
905e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the end of the run closest to the leading margin
906e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
907e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
908e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
909e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
910e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width is required
911e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
912e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
913e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
914bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float handleRun(int start, int measureLimit,
915e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
9160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int bottom, FontMetricsInt fmi, boolean needWidth) {
917e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
918f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        // Case of an empty line, make sure we update fmi according to mPaint
919f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        if (start == measureLimit) {
920f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne            TextPaint wp = mWorkPaint;
921f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne            wp.set(mPaint);
92215c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            if (fmi != null) {
92315c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio                expandMetricsFromPaint(fmi, wp);
92415c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            }
92515c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio            return 0f;
926f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        }
927f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne
928945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        if (mSpanned == null) {
929945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            TextPaint wp = mWorkPaint;
930945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            wp.set(mPaint);
931945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            final int mlimit = measureLimit;
932945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top,
933945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    y, bottom, fmi, needWidth || mlimit < measureLimit);
934945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        }
935945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
936945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        final SpanSet<MetricAffectingSpan> metricAffectingSpans = new SpanSet<MetricAffectingSpan>(
937945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                mSpanned, mStart + start, mStart + limit, MetricAffectingSpan.class);
938945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        final SpanSet<CharacterStyle> characterStyleSpans = new SpanSet<CharacterStyle>(
939945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    mSpanned, mStart + start, mStart + limit, CharacterStyle.class);
940945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
941e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // Shaping needs to take into account context up to metric boundaries,
942e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // but rendering needs to take into account character style boundaries.
9430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // So we iterate through metric runs to get metric bounds,
9440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // then within each metric run iterate through character style runs
9450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // for the run bounds.
946945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        final float originalX = x;
9470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        for (int i = start, inext; i < measureLimit; i = inext) {
948e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextPaint wp = mWorkPaint;
949e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            wp.set(mPaint);
950e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
951945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            inext = metricAffectingSpans.getNextTransition(mStart + i, mStart + limit) - mStart;
952945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            int mlimit = Math.min(inext, measureLimit);
953945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
954945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            ReplacementSpan replacement = null;
955945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
956945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            for (int j = 0; j < metricAffectingSpans.numberOfSpans; j++) {
957945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT
958945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                // empty by construction. This special case in getSpans() explains the >= & <= tests
959945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if ((metricAffectingSpans.spanStarts[j] >= mStart + mlimit) ||
960945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                        (metricAffectingSpans.spanEnds[j] <= mStart + i)) continue;
961945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                MetricAffectingSpan span = metricAffectingSpans.spans[j];
962945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                if (span instanceof ReplacementSpan) {
963945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    replacement = (ReplacementSpan)span;
964945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                } else {
965945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    // We might have a replacement that uses the draw
966945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    // state, otherwise measure state would suffice.
967945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    span.updateDrawState(wp);
968e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
969e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
970e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
971945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            if (replacement != null) {
972945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y,
973945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                        bottom, fmi, needWidth || mlimit < measureLimit);
974945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                continue;
975945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            }
976945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
977945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne            if (c == null) {
9780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x += handleText(wp, i, mlimit, i, inext, runIsRtl, c, x, top,
9790c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        y, bottom, fmi, needWidth || mlimit < measureLimit);
9800c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            } else {
9810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = i, jnext; j < mlimit; j = jnext) {
982945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    jnext = characterStyleSpans.getNextTransition(mStart + j, mStart + mlimit) -
983945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                            mStart;
984e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
9850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    wp.set(mPaint);
986945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                    for (int k = 0; k < characterStyleSpans.numberOfSpans; k++) {
987945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                        // Intentionally using >= and <= as explained above
988945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                        if ((characterStyleSpans.spanStarts[k] >= mStart + jnext) ||
989945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                                (characterStyleSpans.spanEnds[k] <= mStart + j)) continue;
990945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne
991945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne                        CharacterStyle span = characterStyleSpans.spans[k];
9920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateDrawState(wp);
993e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
9940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
9950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
9960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            top, y, bottom, fmi, needWidth || jnext < measureLimit);
997e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
998e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
999e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1000e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1001945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        return x - originalX;
1002e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1003e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1004e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
1005e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Render a text run with the set-up paint.
1006e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
1007e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas
1008e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the paint used to render the text
10090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param start the start of the run
10100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the run
10110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextStart the start of context for the run
10120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextEnd the end of the context for the run
1013e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
1014e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the x position of the left edge of the run
1015e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline of the run
1016e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
10170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private void drawTextRun(Canvas c, TextPaint wp, int start, int end,
10180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl, float x, int y) {
1019e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1020f47d7405bbcb25d7cdf89ebb059f41520fe9ab87Doug Felt        int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR;
1021e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
10220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int count = end - start;
10230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextCount = contextEnd - contextStart;
10240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mChars, start, count, contextStart, contextCount,
10250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y, flags, wp);
1026e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
10270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int delta = mStart;
10280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mText, delta + start, delta + end,
10290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    delta + contextStart, delta + contextEnd, x, y, flags, wp);
1030e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1031e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1032e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1033e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
1034e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the ascent of the text at start.  This is used for scaling
1035e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * emoji.
1036e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
1037e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param pos the line-relative position
1038e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the ascent of the text at start
1039e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
1040e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float ascent(int pos) {
1041e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mSpanned == null) {
1042e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
1043e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1044e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1045e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        pos += mStart;
1046945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne        MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1, MetricAffectingSpan.class);
1047e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (spans.length == 0) {
1048e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
1049e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1050e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1051e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextPaint wp = mWorkPaint;
1052e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        wp.set(mPaint);
1053e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (MetricAffectingSpan span : spans) {
1054e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            span.updateMeasureState(wp);
1055e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1056e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return wp.ascent();
1057e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1058e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1059e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
1060e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next tab position.
1061e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
1062e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param h the (unsigned) offset from the leading margin
1063e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the (unsigned) tab position after this offset
1064e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
1065e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float nextTab(float h) {
1066c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (mTabs != null) {
1067c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return mTabs.nextTab(h);
1068e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
1069c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return TabStops.nextDefaultStop(h, TAB_INCREMENT);
1070e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
1071e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1072e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static final int TAB_INCREMENT = 20;
1073e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt}
1074