TextLine.java revision bc7cdb6783d059249133b1c0baf52c305c6b4a33
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 com.android.internal.util.ArrayUtils;
20e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
21e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Bitmap;
22e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Canvas;
23e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint;
24e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint.FontMetricsInt;
25cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunneimport android.graphics.RectF;
26e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.Layout.Directions;
27c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.Layout.TabStops;
28e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.CharacterStyle;
29e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.MetricAffectingSpan;
30e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.ReplacementSpan;
31e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.util.Log;
32e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
33e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/**
34e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Represents a line of styled text, for measuring in visual order and
35e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * for rendering.
36e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
37e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Get a new instance using obtain(), and when finished with it, return it
38e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * to the pool using recycle().
39e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
40e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Call set to prepare the instance for use, then either draw, measure,
41e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * metrics, or caretToLeftRightOf.
42e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt *
43e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @hide
44e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */
45e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltclass TextLine {
46bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private static final boolean DEBUG = false;
47bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy
48e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private TextPaint mPaint;
49e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private CharSequence mText;
50e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mStart;
51e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mLen;
52e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mDir;
53e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Directions mDirections;
54e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mHasTabs;
55c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private TabStops mTabs;
56e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private char[] mChars;
57e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mCharsValid;
58e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Spanned mSpanned;
59345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne    private final TextPaint mWorkPaint = new TextPaint();
60e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
61bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private static final TextLine[] sCached = new TextLine[3];
62e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
63e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
64e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns a new TextLine from the shared pool.
65e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
66e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return an uninitialized TextLine
67e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
68e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine obtain() {
69e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl;
70bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        synchronized (sCached) {
71bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            for (int i = sCached.length; --i >= 0;) {
72bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                if (sCached[i] != null) {
73bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    tl = sCached[i];
74bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    sCached[i] = null;
75e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return tl;
76e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
77e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
78e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
79e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl = new TextLine();
80bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        if (DEBUG) {
81bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            Log.v("TLINE", "new: " + tl);
82bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        }
83e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return tl;
84e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
85e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
86e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
87e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Puts a TextLine back into the shared pool. Do not use this TextLine once
88e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * it has been returned.
89e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param tl the textLine
90e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return null, as a convenience from clearing references to the provided
91e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * TextLine
92e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
93e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine recycle(TextLine tl) {
94e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mText = null;
95e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mPaint = null;
96e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mDirections = null;
97bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        synchronized(sCached) {
98bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            for (int i = 0; i < sCached.length; ++i) {
99bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                if (sCached[i] == null) {
100bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    sCached[i] = tl;
101f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne                    break;
102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
103e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
105e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return null;
106e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
107e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
108e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
109e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Initializes a TextLine and prepares it for use.
110e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
111e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param paint the base paint for the line
112e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param text the text, can be Styled
113e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the line relative to the text
114e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the line relative to the text
115e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param dir the paragraph direction of this line
116e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param directions the directions information of this line
117e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param hasTabs true if the line might contain tabs or emoji
118c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param tabStops the tabStops. Can be null.
119e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
121c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            Directions directions, boolean hasTabs, TabStops tabStops) {
122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mPaint = paint;
123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mText = text;
124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mStart = start;
125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mLen = limit - start;
126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDir = dir;
127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDirections = directions;
128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mHasTabs = hasTabs;
129e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mSpanned = null;
130e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
131e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean hasReplacement = false;
132e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (text instanceof Spanned) {
133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            mSpanned = (Spanned) text;
1341e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            ReplacementSpan[] spans = mSpanned.getSpans(start, limit, ReplacementSpan.class);
1351e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class);
1361e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            hasReplacement = spans.length > 0;
137e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
138e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
1391e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne        mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
142e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mChars == null || mChars.length < mLen) {
143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                mChars = new char[ArrayUtils.idealCharArraySize(mLen)];
144e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
145e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextUtils.getChars(text, start, limit, mChars, 0);
1460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (hasReplacement) {
1470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Handle these all at once so we don't have to do it as we go.
1480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Replace the first character of each replacement run with the
1490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // object-replacement character and the remainder with zero width
1500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // non-break space aka BOM.  Cursor movement code skips these
1510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // zero-width characters.
1520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                char[] chars = mChars;
1530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int i = start, inext; i < limit; i = inext) {
1541e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    inext = mSpanned.nextSpanTransition(i, limit, ReplacementSpan.class);
1551e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    ReplacementSpan[] spans = mSpanned.getSpans(i, inext, ReplacementSpan.class);
1561e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class);
1571e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    if (spans.length > 0) {
1581e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                        // transition into a span
1590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        chars[i - start] = '\ufffc';
1600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        for (int j = i - start + 1, e = inext - start; j < e; ++j) {
1610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip
1620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        }
1630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
1640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
1650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
166e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
167c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        mTabs = tabStops;
168e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
169e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
170e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
171e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Renders the TextLine.
172e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
173e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to render on
174e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the leading margin position
175e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
176e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
177e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
178e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
179e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void draw(Canvas c, float x, int top, int y, int bottom) {
180e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
181e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
182bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                drawRun(c, 0, mLen, false, x, top, y, bottom, false);
183e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
185e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
186bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                drawRun(c, 0, mLen, true, x, top, y, bottom, false);
187e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
190e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
193e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        RectF emojiRect = null;
194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
195e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lastRunIndex = runs.length - 2;
196e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
197e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
198e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
199e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
200e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = mChars[j];
211e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
212e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(mChars, j);
213e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
223bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom,
224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            i != lastRunIndex || j != mLen);
225e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    } else if (bm != null) {
229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bitmapHeight = bm.getHeight();
231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float scale = -bmAscent / bitmapHeight;
232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float width = bm.getWidth() * scale;
233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (emojiRect == null) {
235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            emojiRect = new RectF();
236e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        emojiRect.set(x + h, y + bmAscent,
238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                                x + h + width, y);
239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        c.drawBitmap(bm, null, emojiRect, mPaint);
240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += width;
241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns metrics information for the entire line.
251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives font metrics information, can be null
253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the line
254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float metrics(FontMetricsInt fmi) {
256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return measure(mLen, false, fmi);
257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns information about a position on the line.
261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the line-relative character offset, between 0 and the
263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * line length, inclusive
264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param trailing true to measure the trailing edge of the character
265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * before offset, false to measure the leading edge of the character
266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * at offset.
267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character, can be null.
269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed offset from the leading margin to the requested
270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character edge.
271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float measure(int offset, boolean trailing, FontMetricsInt fmi) {
273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int target = trailing ? offset - 1 : offset;
274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (target < 0) {
275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return 0;
276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
278e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
280e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
282bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                return measureRun(0, offset, mLen, false, fmi);
283e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
285bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                return measureRun(0, offset, mLen, true, fmi);
286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        char[] chars = mChars;
290e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
298e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
299e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = chars[j];
306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(chars, j);
308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean inSegment = target >= segstart && target < j;
319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment && advance) {
322bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                        return h += measureRun(segstart, offset, j, runIsRtl, fmi);
323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
325bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                    float w = measureRun(segstart, j, j, runIsRtl, fmi);
326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    h += advance ? w : -w;
327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
328e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment) {
329bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                        return h += measureRun(segstart, offset, j, runIsRtl, null);
330e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
332e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
333e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (offset == j) {
334e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
335e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
337e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (target == j) {
338e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (bm != null) {
343e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float wid = bm.getWidth() * -bmAscent / bm.getHeight();
345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += mDir * wid;
346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return h;
355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
357e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Draws a unidirectional (but possibly multi-styled) run of text.
359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
360bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to draw on
362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start
363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit
364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
365e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the position of the run that is closest to the leading margin
366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
368e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
369e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width value is required.
370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run, based on the paragraph direction.
371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Only valid if needWidth is true.
372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
373bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float drawRun(Canvas c, int start,
374e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, float x, int top, int y, int bottom,
375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean needWidth) {
376e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
378bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            float w = -measureRun(start, limit, limit, runIsRtl, null);
379bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            handleRun(start, limit, limit, runIsRtl, c, x + w, top,
3800c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    y, bottom, null, false);
381e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return w;
382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
383e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
384bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        return handleRun(start, limit, limit, runIsRtl, c, x, top,
3850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                y, bottom, null, needWidth);
386e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
387e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
388e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
389e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Measures a unidirectional (but possibly multi-styled) run of text.
390e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
391bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
392e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
393e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset to measure to, between start and limit inclusive
394e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit of the run
395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
396e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
397e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * run, can be null.
398e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width from the start of the run to the leading edge
399e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the character at offset, based on the run (not paragraph) direction
400e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
401bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float measureRun(int start, int offset, int limit, boolean runIsRtl,
402bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            FontMetricsInt fmi) {
403bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy        return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true);
404e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
405e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
406e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
407e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Walk the cursor through this line, skipping conjuncts and
408e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * zero-width characters.
409e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
410e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * <p>This function cannot properly walk the cursor off the ends of the line
411e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * since it does not know about any shaping on the previous/following line
412e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * that might affect the cursor position. Callers must either avoid these
413e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * situations or handle the result specially.
414e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
415e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param cursor the starting position of the cursor, between 0 and the
416e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * length of the line, inclusive
417e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param toLeft true if the caret is moving to the left.
418e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset.  If it is less than 0 or greater than the length
419e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the line, the previous/following line should be examined to get the
420e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * actual offset.
421e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
422e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    int getOffsetToLeftRightOf(int cursor, boolean toLeft) {
423e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 1) The caret marks the leading edge of a character. The character
424e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // logically before it might be on a different level, and the active caret
425e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // position is on the character at the lower level. If that character
426e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // was the previous character, the caret is on its trailing edge.
427e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 2) Take this character/edge and move it in the indicated direction.
428e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // This gives you a new character and a new edge.
429e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 3) This position is between two visually adjacent characters.  One of
430e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // these might be at a lower level.  The active position is on the
431e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // character at the lower level.
432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 4) If the active position is on the trailing edge of the character,
433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // the new caret position is the following logical character, else it
434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // is the character.
435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
436e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineStart = 0;
437e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineEnd = mLen;
438e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean paraIsRtl = mDir == -1;
439e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
440e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
441e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1;
442e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean trailing = false;
443e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
444e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (cursor == lineStart) {
445e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = -2;
446e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else if (cursor == lineEnd) {
447e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = runs.length;
448e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
449e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // First, get information about the run containing the character with
450e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the active caret.
451e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          for (runIndex = 0; runIndex < runs.length; runIndex += 2) {
452e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runStart = lineStart + runs[runIndex];
453e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (cursor >= runStart) {
454e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK);
455e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (runLimit > lineEnd) {
456e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  runLimit = lineEnd;
457e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
458e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor < runLimit) {
459e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
460e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    Layout.RUN_LEVEL_MASK;
461e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (cursor == runStart) {
462e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // The caret is on a run boundary, see if we should
463e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // use the position on the trailing edge of the previous
464e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // logical character instead.
465e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit;
466e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int pos = cursor - 1;
467e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) {
468e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    prevRunStart = lineStart + runs[prevRunIndex];
469e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (pos >= prevRunStart) {
470e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      prevRunLimit = prevRunStart +
471e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK);
472e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (prevRunLimit > lineEnd) {
473e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          prevRunLimit = lineEnd;
474e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
475e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (pos < prevRunLimit) {
476e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT)
477e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            & Layout.RUN_LEVEL_MASK;
478e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (prevRunLevel < runLevel) {
479e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          // Start from logically previous character.
480e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runIndex = prevRunIndex;
481e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLevel = prevRunLevel;
482e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runStart = prevRunStart;
483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLimit = prevRunLimit;
484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          trailing = true;
485e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          break;
486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // caret might be == lineEnd.  This is generally a space or paragraph
497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // separator and has an associated run, but might be the end of
498e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // text, in which case it doesn't.  If that happens, we ran off the
499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // end of the run list, and runIndex == runs.length.  In this case,
500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we are at a run boundary so we skip the below test.
501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (runIndex != runs.length) {
502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean runIsRtl = (runLevel & 0x1) != 0;
503e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean advance = toLeft == runIsRtl;
504e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor != (advance ? runLimit : runStart) || advance != trailing) {
505e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // Moving within or into the run, so we can move logically.
5060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                  newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit,
5070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                          runIsRtl, cursor, advance);
508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // If the new position is internal to the run, we're at the strong
509e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // position already so we're finished.
510e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  if (newCaret != (advance ? runLimit : runStart)) {
511e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      return newCaret;
512e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
514e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
516e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // If newCaret is -1, we're starting at a run boundary and crossing
518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // into another run. Otherwise we've arrived at a run boundary, and
519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to figure out which character to attach to.  Note we might
520e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to run this twice, if we cross a run boundary and end up at
521e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // another run boundary.
522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        while (true) {
523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          boolean advance = toLeft == paraIsRtl;
524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          int otherRunIndex = runIndex + (advance ? 2 : -2);
525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (otherRunIndex >= 0 && otherRunIndex < runs.length) {
526e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunStart = lineStart + runs[otherRunIndex];
527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLimit = otherRunStart +
528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK);
529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLimit > lineEnd) {
530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                otherRunLimit = lineEnd;
531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Layout.RUN_LEVEL_MASK;
534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean otherRunIsRtl = (otherRunLevel & 1) != 0;
535e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
536e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            advance = toLeft == otherRunIsRtl;
537e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (newCaret == -1) {
5380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart,
5390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        otherRunLimit, otherRunIsRtl,
540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        advance ? otherRunStart : otherRunLimit, advance);
541e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (newCaret == (advance ? otherRunLimit : otherRunStart)) {
542e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // Crossed and ended up at a new boundary,
543e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // repeat a second and final time.
544e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runIndex = otherRunIndex;
545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runLevel = otherRunLevel;
546e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    continue;
547e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
551e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            // The new caret is at a boundary.
552e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLevel < runLevel) {
553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // The strong character is in the other run.
554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? otherRunStart : otherRunLimit;
555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            break;
557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret == -1) {
560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // We're walking off the end of the line.  The paragraph
561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // level is always equal to or lower than any internal level, so
562e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // the boundaries get the strong caret.
5630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt              newCaret = advance ? mLen + 1 : -1;
564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              break;
565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Else we've arrived at the end of the line.  That's a strong position.
568e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // We might have arrived here by crossing over a run with no internal
569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // breaks and dropping out of the above loop before advancing one final
570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // time, so reset the caret.
571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Note, we use '<=' below to handle a situation where the only run
572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // on the line is a counter-directional run.  If we're not advancing,
573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we can end up at the 'lineEnd' position but the caret we want is at
574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the lineStart.
575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret <= lineEnd) {
576e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? lineEnd : lineStart;
577e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          break;
579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return newCaret;
582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
584e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
585e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next valid offset within this directional run, skipping
586e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * conjuncts and zero-width characters.  This should not be called to walk
587cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunne     * off the end of the line, since the returned values might not be valid
5880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * on neighboring lines.  If the returned offset is less than zero or
5890c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * greater than the line length, the offset should be recomputed on the
5900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * preceding or following line, respectively.
591e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
592e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
5930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runStart the start of the run
5940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runLimit the limit of the run
5950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runIsRtl true if the run is right-to-left
596e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset
597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param after true if the new offset should logically follow the provided
598e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * offset
599e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset
600e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
6010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit,
6020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean runIsRtl, int offset, boolean after) {
603e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (runIndex < 0 || offset == (after ? mLen : 0)) {
6050c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // Walking off end of line.  Since we don't know
6060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // what cursor positions are available on other lines, we can't
6070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // return accurate values.  These are a guess.
608e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (after) {
6090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart;
6100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart;
6120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
6130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        TextPaint wp = mWorkPaint;
6150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        wp.set(mPaint);
6160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanStart = runStart;
6180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanLimit;
6190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mSpanned == null) {
6200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            spanLimit = runLimit;
6210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int target = after ? offset + 1 : offset;
6230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int limit = mStart + runLimit;
6240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            while (true) {
6250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit,
6260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        MetricAffectingSpan.class) - mStart;
6270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (spanLimit >= target) {
6280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    break;
629e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
6300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanStart = spanLimit;
6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart,
6340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    mStart + spanLimit, MetricAffectingSpan.class);
6351e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne            spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
6360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (spans.length > 0) {
6380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ReplacementSpan replacement = null;
6390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = 0; j < spans.length; j++) {
6400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    MetricAffectingSpan span = spans[j];
6410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    if (span instanceof ReplacementSpan) {
6420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        replacement = (ReplacementSpan)span;
6430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    } else {
6440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateMeasureState(wp);
6450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
6460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
6470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (replacement != null) {
6490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // If we have a replacement span, we're moving either to
6500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // the start or end of this span.
6510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    return after ? spanLimit : spanStart;
652e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
653e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
654e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
655e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
6570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
6580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mCharsValid) {
6590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
6600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    flags, offset, cursorOpt);
6610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mText, mStart + spanStart,
663345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne                    mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart;
664e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
665e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
666e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
667e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
6680bb000931bb841e75903d655552d1626ae158707Gilles Debunne     * @param wp
6690bb000931bb841e75903d655552d1626ae158707Gilles Debunne     */
6700bb000931bb841e75903d655552d1626ae158707Gilles Debunne    private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) {
6710bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousTop     = fmi.top;
6720bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousAscent  = fmi.ascent;
6730bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousDescent = fmi.descent;
6740bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousBottom  = fmi.bottom;
6750bb000931bb841e75903d655552d1626ae158707Gilles Debunne        final int previousLeading = fmi.leading;
6760bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6770bb000931bb841e75903d655552d1626ae158707Gilles Debunne        wp.getFontMetricsInt(fmi);
6780bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6790bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.top     = Math.min(fmi.top,     previousTop);
6800bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.ascent  = Math.min(fmi.ascent,  previousAscent);
6810bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.descent = Math.max(fmi.descent, previousDescent);
6820bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.bottom  = Math.max(fmi.bottom,  previousBottom);
6830bb000931bb841e75903d655552d1626ae158707Gilles Debunne        fmi.leading = Math.max(fmi.leading, previousLeading);
6840bb000931bb841e75903d655552d1626ae158707Gilles Debunne    }
6850bb000931bb841e75903d655552d1626ae158707Gilles Debunne
6860bb000931bb841e75903d655552d1626ae158707Gilles Debunne    /**
687e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering text.  The text must
688e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * not include a tab or emoji.
689e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
690e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the working paint
691e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the text
6920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the text
693e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
694e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if rendering is not needed
695e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the run closest to the leading margin
696e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
697e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
698e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
699e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
700e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the run is needed
701e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
702e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
703e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
7040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private float handleText(TextPaint wp, int start, int end,
7050c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl,
7060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            Canvas c, float x, int top, int y, int bottom,
707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            FontMetricsInt fmi, boolean needWidth) {
708e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
709e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ret = 0;
710e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int runLen = end - start;
7120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int contextLen = contextEnd - contextStart;
713e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (needWidth || (c != null && (wp.bgColor != 0 || runIsRtl))) {
7140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
715e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mCharsValid) {
7160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mChars, start, runLen,
7170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        contextStart, contextLen, flags, null, 0);
718e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
7190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                int delta = mStart;
7200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mText, delta + start,
7210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        delta + end, delta + contextStart, delta + contextEnd,
7220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        flags, null, 0);
723e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
724e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
725e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
726e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (fmi != null) {
7270bb000931bb841e75903d655552d1626ae158707Gilles Debunne            expandMetricsFromPaint(fmi, wp);
728e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
729e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
730e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (c != null) {
731e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runIsRtl) {
732e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                x -= ret;
733e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
734e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
735e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (wp.bgColor != 0) {
736e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int color = wp.getColor();
737e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Paint.Style s = wp.getStyle();
738e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setColor(wp.bgColor);
739e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setStyle(Paint.Style.FILL);
740e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
741e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                c.drawRect(x, top, x + ret, bottom, wp);
742e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
743e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setStyle(s);
744e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setColor(color);
745e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
746e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
7480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y + wp.baselineShift);
749e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
750e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
751e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
752e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
753e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
754e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
755e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering a replacement.
756e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
757bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
758e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param replacement the replacement
759e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the work paint
760e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the run
761e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
762e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
763e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if not rendering
764e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the replacement closest to the leading margin
765e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
766e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
767e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
768e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
769e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the replacement is needed
770e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
771e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
772e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
773e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float handleReplacement(ReplacementSpan replacement, TextPaint wp,
774bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy            int start, int limit, boolean runIsRtl, Canvas c,
775e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float x, int top, int y, int bottom, FontMetricsInt fmi,
7760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean needWidth) {
777e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
778e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ret = 0;
779e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7800c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textStart = mStart + start;
7810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textLimit = mStart + limit;
782e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (needWidth || (c != null && runIsRtl)) {
7840c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            ret = replacement.getSize(wp, mText, textStart, textLimit, fmi);
7850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
786e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7870c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (c != null) {
7880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (runIsRtl) {
7890c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x -= ret;
790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
7910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            replacement.draw(c, mText, textStart, textLimit,
7920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, top, y, bottom, wp);
793e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
794e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
795e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
796e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
797e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
798e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
799e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for handling a unidirectional run.  The run must not
800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * contain tabs or emoji but can contain styles.
801e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
802bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy     *
803e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
8040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param measureLimit the offset to measure to, between start and limit inclusive
805e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
806e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
807e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null
808e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the end of the run closest to the leading margin
809e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
810e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
811e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
812e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
813e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width is required
814e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
815e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
816e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
817bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy    private float handleRun(int start, int measureLimit,
818e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
8190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int bottom, FontMetricsInt fmi, boolean needWidth) {
820e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
821f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        // Case of an empty line, make sure we update fmi according to mPaint
822f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        if (start == measureLimit) {
823f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne            TextPaint wp = mWorkPaint;
824f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne            wp.set(mPaint);
825f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne            return handleText(wp, 0, 0, 0, 0, runIsRtl, c, x, top, y, bottom, fmi, needWidth);
826f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne        }
827f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne
828e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // Shaping needs to take into account context up to metric boundaries,
829e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // but rendering needs to take into account character style boundaries.
8300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // So we iterate through metric runs to get metric bounds,
8310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // then within each metric run iterate through character style runs
8320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // for the run bounds.
833e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ox = x;
8340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        for (int i = start, inext; i < measureLimit; i = inext) {
835e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextPaint wp = mWorkPaint;
836e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            wp.set(mPaint);
837e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int mlimit;
839e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mSpanned == null) {
840e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                inext = limit;
8410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                mlimit = measureLimit;
842e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
843e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                inext = mSpanned.nextSpanTransition(mStart + i, mStart + limit,
844e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        MetricAffectingSpan.class) - mStart;
845e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                mlimit = inext < measureLimit ? inext : measureLimit;
847e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + i,
8480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        mStart + mlimit, MetricAffectingSpan.class);
8491e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
850e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
851e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (spans.length > 0) {
852e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    ReplacementSpan replacement = null;
853e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    for (int j = 0; j < spans.length; j++) {
854e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        MetricAffectingSpan span = spans[j];
855e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (span instanceof ReplacementSpan) {
856e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            replacement = (ReplacementSpan)span;
857e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else {
8580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            // We might have a replacement that uses the draw
8590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            // state, otherwise measure state would suffice.
8600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            span.updateDrawState(wp);
861e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
862e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
863e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
864e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (replacement != null) {
865bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy                        x += handleReplacement(replacement, wp, i,
8660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                                mlimit, runIsRtl, c, x, top, y, bottom, fmi,
8670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                                needWidth || mlimit < measureLimit);
868e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        continue;
869e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
870e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
871e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
872e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (mSpanned == null || c == null) {
8740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x += handleText(wp, i, mlimit, i, inext, runIsRtl, c, x, top,
8750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        y, bottom, fmi, needWidth || mlimit < measureLimit);
8760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            } else {
8770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = i, jnext; j < mlimit; j = jnext) {
8780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    jnext = mSpanned.nextSpanTransition(mStart + j,
8790c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            mStart + mlimit, CharacterStyle.class) - mStart;
880e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8810c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    CharacterStyle[] spans = mSpanned.getSpans(mStart + j,
8820c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            mStart + jnext, CharacterStyle.class);
8831e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne                    spans = TextUtils.removeEmptySpans(spans, mSpanned, CharacterStyle.class);
884e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    wp.set(mPaint);
8860c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    for (int k = 0; k < spans.length; k++) {
8870c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        CharacterStyle span = spans[k];
8880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateDrawState(wp);
889e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
8900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
8910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
8920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            top, y, bottom, fmi, needWidth || jnext < measureLimit);
893e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
894e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
895e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
896e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
897e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return x - ox;
898e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
899e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
900e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
901e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Render a text run with the set-up paint.
902e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
903e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas
904e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the paint used to render the text
9050c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param start the start of the run
9060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the run
9070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextStart the start of context for the run
9080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextEnd the end of the context for the run
909e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
910e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the x position of the left edge of the run
911e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline of the run
912e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
9130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private void drawTextRun(Canvas c, TextPaint wp, int start, int end,
9140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl, float x, int y) {
915e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
916f47d7405bbcb25d7cdf89ebb059f41520fe9ab87Doug Felt        int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR;
917e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
9180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int count = end - start;
9190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextCount = contextEnd - contextStart;
9200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mChars, start, count, contextStart, contextCount,
9210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y, flags, wp);
922e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
9230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int delta = mStart;
9240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mText, delta + start, delta + end,
9250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    delta + contextStart, delta + contextEnd, x, y, flags, wp);
926e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
927e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
928e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
929e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
930e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the ascent of the text at start.  This is used for scaling
931e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * emoji.
932e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
933e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param pos the line-relative position
934e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the ascent of the text at start
935e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
936e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float ascent(int pos) {
937e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mSpanned == null) {
938e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
939e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
940e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
941e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        pos += mStart;
942e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1,
943e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                MetricAffectingSpan.class);
944e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (spans.length == 0) {
945e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
946e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
947e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
948e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextPaint wp = mWorkPaint;
949e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        wp.set(mPaint);
950e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (MetricAffectingSpan span : spans) {
951e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            span.updateMeasureState(wp);
952e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
953e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return wp.ascent();
954e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
955e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
956e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
957e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next tab position.
958e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
959e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param h the (unsigned) offset from the leading margin
960e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the (unsigned) tab position after this offset
961e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
962e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float nextTab(float h) {
963c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (mTabs != null) {
964c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return mTabs.nextTab(h);
965e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
966c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return TabStops.nextDefaultStop(h, TAB_INCREMENT);
967e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
968e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
969e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static final int TAB_INCREMENT = 20;
970e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt}
971