TextLine.java revision 345cb03315a0813ec57e44f97fc3fa4af6b3c309
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.RectF;
25e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint.FontMetricsInt;
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 {
46e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private TextPaint mPaint;
47e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private CharSequence mText;
48e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mStart;
49e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mLen;
50e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private int mDir;
51e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Directions mDirections;
52e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mHasTabs;
53c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private TabStops mTabs;
54e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private char[] mChars;
55e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private boolean mCharsValid;
56e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private Spanned mSpanned;
57345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne    private final TextPaint mWorkPaint = new TextPaint();
58e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
59e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static TextLine[] cached = new TextLine[3];
60e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
61e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
62e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns a new TextLine from the shared pool.
63e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
64e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return an uninitialized TextLine
65e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
66e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine obtain() {
67e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextLine tl;
68e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        synchronized (cached) {
69e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int i = cached.length; --i >= 0;) {
70e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (cached[i] != null) {
71e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    tl = cached[i];
72e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    cached[i] = null;
73e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    return tl;
74e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
75e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
76e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
77e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl = new TextLine();
78e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        Log.e("TLINE", "new: " + tl);
79e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return tl;
80e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
81e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
82e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
83e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Puts a TextLine back into the shared pool. Do not use this TextLine once
84e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * it has been returned.
85e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param tl the textLine
86e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return null, as a convenience from clearing references to the provided
87e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * TextLine
88e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
89e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    static TextLine recycle(TextLine tl) {
90e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mText = null;
91e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mPaint = null;
92e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        tl.mDirections = null;
93e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (tl.mLen < 250) {
94e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            synchronized(cached) {
95e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                for (int i = 0; i < cached.length; ++i) {
96e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (cached[i] == null) {
97e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        cached[i] = tl;
98e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        break;
99e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
101e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
103e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return null;
104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
105e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
106e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
107e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Initializes a TextLine and prepares it for use.
108e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
109e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param paint the base paint for the line
110e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param text the text, can be Styled
111e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the line relative to the text
112e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the line relative to the text
113e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param dir the paragraph direction of this line
114e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param directions the directions information of this line
115e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param hasTabs true if the line might contain tabs or emoji
116c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt     * @param tabStops the tabStops. Can be null.
117e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
118e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
119c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            Directions directions, boolean hasTabs, TabStops tabStops) {
120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mPaint = paint;
121e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mText = text;
122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mStart = start;
123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mLen = limit - start;
124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDir = dir;
125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mDirections = directions;
126e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mHasTabs = hasTabs;
127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mSpanned = null;
128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
129e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean hasReplacement = false;
130e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (text instanceof Spanned) {
131e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            mSpanned = (Spanned) text;
132e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            hasReplacement = mSpanned.getSpans(start, limit,
133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    ReplacementSpan.class).length > 0;
134e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
135e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
136e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mCharsValid = hasReplacement || hasTabs ||
137e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
138e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
139e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mChars == null || mChars.length < mLen) {
141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                mChars = new char[ArrayUtils.idealCharArraySize(mLen)];
142e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextUtils.getChars(text, start, limit, mChars, 0);
1440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (hasReplacement) {
1450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Handle these all at once so we don't have to do it as we go.
1460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // Replace the first character of each replacement run with the
1470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // object-replacement character and the remainder with zero width
1480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // non-break space aka BOM.  Cursor movement code skips these
1490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                // zero-width characters.
1500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                char[] chars = mChars;
1510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int i = start, inext; i < limit; i = inext) {
1520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    inext = mSpanned.nextSpanTransition(i, limit,
1530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            ReplacementSpan.class);
1540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    if (mSpanned.getSpans(i, inext, ReplacementSpan.class)
1550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            .length > 0) { // transition into a span
1560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        chars[i - start] = '\ufffc';
1570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        for (int j = i - start + 1, e = inext - start; j < e; ++j) {
1580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip
1590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        }
1600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
1610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
1620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
163e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
164c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        mTabs = tabStops;
165e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
166e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
167e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
168e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Renders the TextLine.
169e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
170e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to render on
171e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the leading margin position
172e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
173e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
174e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
175e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
176e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    void draw(Canvas c, float x, int top, int y, int bottom) {
177e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
178e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
179e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                drawRun(c, 0, 0, mLen, false, x, top, y, bottom, false);
180e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
181e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
182e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
183e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                drawRun(c, 0, 0, mLen, true, x, top, y, bottom, false);
184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                return;
185e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
187e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
190e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        RectF emojiRect = null;
191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lastRunIndex = runs.length - 2;
193e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
195e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
196e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
197e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
198e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
199e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
200e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            char[] chars = mChars;
203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = mChars[j];
209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(mChars, j);
211e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
212e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
213e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    h += drawRun(c, i, segstart, j, runIsRtl, x+h, top, y, bottom,
222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            i != lastRunIndex || j != mLen);
223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
225e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    } else if (bm != null) {
227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bitmapHeight = bm.getHeight();
229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float scale = -bmAscent / bitmapHeight;
230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float width = bm.getWidth() * scale;
231e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (emojiRect == null) {
233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            emojiRect = new RectF();
234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        emojiRect.set(x + h, y + bmAscent,
236e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                                x + h + width, y);
237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        c.drawBitmap(bm, null, emojiRect, mPaint);
238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += width;
239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns metrics information for the entire line.
249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives font metrics information, can be null
251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the line
252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float metrics(FontMetricsInt fmi) {
254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return measure(mLen, false, fmi);
255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns information about a position on the line.
259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the line-relative character offset, between 0 and the
261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * line length, inclusive
262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param trailing true to measure the trailing edge of the character
263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * before offset, false to measure the leading edge of the character
264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * at offset.
265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character, can be null.
267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed offset from the leading margin to the requested
268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * character edge.
269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float measure(int offset, boolean trailing, FontMetricsInt fmi) {
271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int target = trailing ? offset - 1 : offset;
272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (target < 0) {
273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return 0;
274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float h = 0;
277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
278e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (!mHasTabs) {
279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
2800c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                return measureRun(0, 0, offset, mLen, false, fmi);
281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
2830b9d2ca6b6fc4574898cfff6acdee821d4723a96Doug Felt                return measureRun(0, 0, offset, mLen, true, fmi);
284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
285e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        char[] chars = mChars;
288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (int i = 0; i < runs.length; i += 2) {
290e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runStart = runs[i];
291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runLimit > mLen) {
293e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLimit = mLen;
294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int segstart = runStart;
298e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
299e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int codept = 0;
300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Bitmap bm = null;
301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (mHasTabs && j < runLimit) {
303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    codept = chars[j];
304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        codept = Character.codePointAt(chars, j);
306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else if (codept > 0xffff) {
309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            ++j;
310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            continue;
311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (j == runLimit || codept == '\t' || bm != null) {
316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean inSegment = target >= segstart && target < j;
317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment && advance) {
320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        return h += measureRun(i, segstart, offset, j, runIsRtl, fmi);
321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    float w = measureRun(i, segstart, j, j, runIsRtl, fmi);
324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    h += advance ? w : -w;
325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (inSegment) {
327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        return h += measureRun(i, segstart, offset, j, runIsRtl, null);
328e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
329e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
330e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (codept == '\t') {
331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (offset == j) {
332e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
333e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
334e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h = mDir * nextTab(h * mDir);
335e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (target == j) {
336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            return h;
337e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
338e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (bm != null) {
341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float bmAscent = ascent(j);
342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        float wid = bm.getWidth() * -bmAscent / bm.getHeight();
343e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        h += mDir * wid;
344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        j++;
345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    segstart = j + 1;
348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return h;
353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Draws a unidirectional (but possibly multi-styled) run of text.
357e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas to draw on
359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the index of this directional run
360e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start
361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit
362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the position of the run that is closest to the leading margin
364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
365e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width value is required.
368e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run, based on the paragraph direction.
369e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Only valid if needWidth is true.
370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float drawRun(Canvas c, int runIndex, int start,
372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, float x, int top, int y, int bottom,
373e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean needWidth) {
374e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
376e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float w = -measureRun(runIndex, start, limit, limit, runIsRtl, null);
377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            handleRun(runIndex, start, limit, limit, runIsRtl, c, x + w, top,
3780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    y, bottom, null, false);
379e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return w;
380e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
381e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return handleRun(runIndex, start, limit, limit, runIsRtl, c, x, top,
3830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                y, bottom, null, needWidth);
384e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
385e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
386e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
387e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Measures a unidirectional (but possibly multi-styled) run of text.
388e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
389e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
390e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
391e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset to measure to, between start and limit inclusive
392e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the line-relative limit of the run
393e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
394e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information about the requested
395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * run, can be null.
396e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width from the start of the run to the leading edge
397e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the character at offset, based on the run (not paragraph) direction
398e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
399e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float measureRun(int runIndex, int start,
400e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int offset, int limit, boolean runIsRtl, FontMetricsInt fmi) {
401e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return handleRun(runIndex, start, offset, limit, runIsRtl, null,
4020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                0, 0, 0, 0, fmi, true);
403e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
404e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
405e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
406e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Walk the cursor through this line, skipping conjuncts and
407e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * zero-width characters.
408e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
409e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * <p>This function cannot properly walk the cursor off the ends of the line
410e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * since it does not know about any shaping on the previous/following line
411e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * that might affect the cursor position. Callers must either avoid these
412e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * situations or handle the result specially.
413e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
414e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param cursor the starting position of the cursor, between 0 and the
415e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * length of the line, inclusive
416e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param toLeft true if the caret is moving to the left.
417e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset.  If it is less than 0 or greater than the length
418e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * of the line, the previous/following line should be examined to get the
419e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * actual offset.
420e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
421e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    int getOffsetToLeftRightOf(int cursor, boolean toLeft) {
422e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 1) The caret marks the leading edge of a character. The character
423e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // logically before it might be on a different level, and the active caret
424e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // position is on the character at the lower level. If that character
425e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // was the previous character, the caret is on its trailing edge.
426e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 2) Take this character/edge and move it in the indicated direction.
427e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // This gives you a new character and a new edge.
428e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 3) This position is between two visually adjacent characters.  One of
429e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // these might be at a lower level.  The active position is on the
430e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // character at the lower level.
431e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // 4) If the active position is on the trailing edge of the character,
432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // the new caret position is the following logical character, else it
433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // is the character.
434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineStart = 0;
436e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int lineEnd = mLen;
437e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean paraIsRtl = mDir == -1;
438e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int[] runs = mDirections.mDirections;
439e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
440e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1;
441e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        boolean trailing = false;
442e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
443e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (cursor == lineStart) {
444e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = -2;
445e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else if (cursor == lineEnd) {
446e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runIndex = runs.length;
447e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
448e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // First, get information about the run containing the character with
449e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the active caret.
450e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          for (runIndex = 0; runIndex < runs.length; runIndex += 2) {
451e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            runStart = lineStart + runs[runIndex];
452e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (cursor >= runStart) {
453e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK);
454e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (runLimit > lineEnd) {
455e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  runLimit = lineEnd;
456e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
457e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor < runLimit) {
458e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
459e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    Layout.RUN_LEVEL_MASK;
460e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (cursor == runStart) {
461e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // The caret is on a run boundary, see if we should
462e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // use the position on the trailing edge of the previous
463e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // logical character instead.
464e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit;
465e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  int pos = cursor - 1;
466e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) {
467e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    prevRunStart = lineStart + runs[prevRunIndex];
468e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (pos >= prevRunStart) {
469e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      prevRunLimit = prevRunStart +
470e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK);
471e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (prevRunLimit > lineEnd) {
472e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          prevRunLimit = lineEnd;
473e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
474e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      if (pos < prevRunLimit) {
475e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT)
476e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            & Layout.RUN_LEVEL_MASK;
477e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (prevRunLevel < runLevel) {
478e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          // Start from logically previous character.
479e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runIndex = prevRunIndex;
480e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLevel = prevRunLevel;
481e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runStart = prevRunStart;
482e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          runLimit = prevRunLimit;
483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          trailing = true;
484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                          break;
485e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      }
487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // caret might be == lineEnd.  This is generally a space or paragraph
496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // separator and has an associated run, but might be the end of
497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // text, in which case it doesn't.  If that happens, we ran off the
498e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // end of the run list, and runIndex == runs.length.  In this case,
499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we are at a run boundary so we skip the below test.
500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (runIndex != runs.length) {
501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean runIsRtl = (runLevel & 0x1) != 0;
502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              boolean advance = toLeft == runIsRtl;
503e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              if (cursor != (advance ? runLimit : runStart) || advance != trailing) {
504e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // Moving within or into the run, so we can move logically.
5050c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                  newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit,
5060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                          runIsRtl, cursor, advance);
507e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // If the new position is internal to the run, we're at the strong
508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  // position already so we're finished.
509e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  if (newCaret != (advance ? runLimit : runStart)) {
510e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                      return newCaret;
511e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                  }
512e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              }
513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
514e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
515e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
516e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // If newCaret is -1, we're starting at a run boundary and crossing
517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // into another run. Otherwise we've arrived at a run boundary, and
518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to figure out which character to attach to.  Note we might
519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // need to run this twice, if we cross a run boundary and end up at
520e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // another run boundary.
521e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        while (true) {
522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          boolean advance = toLeft == paraIsRtl;
523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          int otherRunIndex = runIndex + (advance ? 2 : -2);
524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (otherRunIndex >= 0 && otherRunIndex < runs.length) {
525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunStart = lineStart + runs[otherRunIndex];
526e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLimit = otherRunStart +
527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK);
528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLimit > lineEnd) {
529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                otherRunLimit = lineEnd;
530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Layout.RUN_LEVEL_MASK;
533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean otherRunIsRtl = (otherRunLevel & 1) != 0;
534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
535e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            advance = toLeft == otherRunIsRtl;
536e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (newCaret == -1) {
5370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart,
5380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        otherRunLimit, otherRunIsRtl,
539e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        advance ? otherRunStart : otherRunLimit, advance);
540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (newCaret == (advance ? otherRunLimit : otherRunStart)) {
541e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // Crossed and ended up at a new boundary,
542e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    // repeat a second and final time.
543e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runIndex = otherRunIndex;
544e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    runLevel = otherRunLevel;
545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    continue;
546e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
547e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                break;
548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            // The new caret is at a boundary.
551e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (otherRunLevel < runLevel) {
552e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // The strong character is in the other run.
553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? otherRunStart : otherRunLimit;
554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            break;
556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret == -1) {
559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // We're walking off the end of the line.  The paragraph
560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // level is always equal to or lower than any internal level, so
561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              // the boundaries get the strong caret.
5620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt              newCaret = advance ? mLen + 1 : -1;
563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              break;
564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Else we've arrived at the end of the line.  That's a strong position.
567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // We might have arrived here by crossing over a run with no internal
568e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // breaks and dropping out of the above loop before advancing one final
569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // time, so reset the caret.
570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // Note, we use '<=' below to handle a situation where the only run
571e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // on the line is a counter-directional run.  If we're not advancing,
572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // we can end up at the 'lineEnd' position but the caret we want is at
573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          // the lineStart.
574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          if (newCaret <= lineEnd) {
575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt              newCaret = advance ? lineEnd : lineStart;
576e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          }
577e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt          break;
578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return newCaret;
581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
584e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next valid offset within this directional run, skipping
585e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * conjuncts and zero-width characters.  This should not be called to walk
5860c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * off the end of the line, since the the returned values might not be valid
5870c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * on neighboring lines.  If the returned offset is less than zero or
5880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * greater than the line length, the offset should be recomputed on the
5890c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * preceding or following line, respectively.
590e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
591e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
5920c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runStart the start of the run
5930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runLimit the limit of the run
5940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param runIsRtl true if the run is right-to-left
595e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param offset the offset
596e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param after true if the new offset should logically follow the provided
597e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * offset
598e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the new offset
599e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
6000c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit,
6010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean runIsRtl, int offset, boolean after) {
602e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (runIndex < 0 || offset == (after ? mLen : 0)) {
6040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // Walking off end of line.  Since we don't know
6050c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // what cursor positions are available on other lines, we can't
6060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            // return accurate values.  These are a guess.
607e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (after) {
6080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart;
6090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart;
6110c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
6120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        TextPaint wp = mWorkPaint;
6140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        wp.set(mPaint);
6150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6160c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanStart = runStart;
6170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int spanLimit;
6180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mSpanned == null) {
6190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            spanLimit = runLimit;
6200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int target = after ? offset + 1 : offset;
6220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int limit = mStart + runLimit;
6230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            while (true) {
6240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit,
6250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        MetricAffectingSpan.class) - mStart;
6260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (spanLimit >= target) {
6270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    break;
628e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
6290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                spanStart = spanLimit;
6300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            }
6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart,
6330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    mStart + spanLimit, MetricAffectingSpan.class);
6340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (spans.length > 0) {
6360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ReplacementSpan replacement = null;
6370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = 0; j < spans.length; j++) {
6380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    MetricAffectingSpan span = spans[j];
6390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    if (span instanceof ReplacementSpan) {
6400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        replacement = (ReplacementSpan)span;
6410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    } else {
6420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateMeasureState(wp);
6430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    }
6440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                }
6450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
6460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                if (replacement != null) {
6470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // If we have a replacement span, we're moving either to
6480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    // the start or end of this span.
6490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    return after ? spanLimit : spanStart;
650e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
651e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
652e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
653e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
6550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
6560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (mCharsValid) {
6570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
6580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    flags, offset, cursorOpt);
6590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        } else {
6600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            return wp.getTextRunCursor(mText, mStart + spanStart,
661345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne                    mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart;
662e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
663e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
664e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
665e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
666e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering text.  The text must
667e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * not include a tab or emoji.
668e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
669e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the working paint
670e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the text
6710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the text
672e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
673e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if rendering is not needed
674e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the run closest to the leading margin
675e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
676e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
677e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
678e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
679e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the run is needed
680e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
681e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
682e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
6830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private float handleText(TextPaint wp, int start, int end,
6840c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl,
6850c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            Canvas c, float x, int top, int y, int bottom,
686e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            FontMetricsInt fmi, boolean needWidth) {
687e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
688e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ret = 0;
689e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
6900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int runLen = end - start;
6910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int contextLen = contextEnd - contextStart;
692e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (needWidth || (c != null && (wp.bgColor != 0 || runIsRtl))) {
6930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
694e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mCharsValid) {
6950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mChars, start, runLen,
6960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        contextStart, contextLen, flags, null, 0);
697e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
6980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                int delta = mStart;
6990c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                ret = wp.getTextRunAdvances(mText, delta + start,
7000c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        delta + end, delta + contextStart, delta + contextEnd,
7010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        flags, null, 0);
702e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
703e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
704e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
705e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (fmi != null) {
706e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            wp.getFontMetricsInt(fmi);
707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
708e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
709e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (c != null) {
710e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (runIsRtl) {
711e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                x -= ret;
712e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
713e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
714e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (wp.bgColor != 0) {
715e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                int color = wp.getColor();
716e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                Paint.Style s = wp.getStyle();
717e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setColor(wp.bgColor);
718e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setStyle(Paint.Style.FILL);
719e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
720e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                c.drawRect(x, top, x + ret, bottom, wp);
721e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
722e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setStyle(s);
723e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                wp.setColor(color);
724e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
725e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
7270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y + wp.baselineShift);
728e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
729e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
730e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
731e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
732e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
733e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
734e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for measuring and rendering a replacement.
735e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
736e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param replacement the replacement
737e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the work paint
738e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
739e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the start of the run
740e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
741e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
742e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null if not rendering
743e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the edge of the replacement closest to the leading margin
744e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
745e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
746e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
747e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
748e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width of the replacement is needed
749e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
750e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
751e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
752e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private float handleReplacement(ReplacementSpan replacement, TextPaint wp,
753e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int runIndex, int start, int limit, boolean runIsRtl, Canvas c,
754e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float x, int top, int y, int bottom, FontMetricsInt fmi,
7550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            boolean needWidth) {
756e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
757e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ret = 0;
758e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textStart = mStart + start;
7600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        int textLimit = mStart + limit;
761e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (needWidth || (c != null && runIsRtl)) {
7630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            ret = replacement.getSize(wp, mText, textStart, textLimit, fmi);
7640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        }
765e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
7660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        if (c != null) {
7670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (runIsRtl) {
7680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x -= ret;
769e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
7700c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            replacement.draw(c, mText, textStart, textLimit,
7710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, top, y, bottom, wp);
772e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
773e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
774e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return runIsRtl ? -ret : ret;
775e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
776e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
777e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
778e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Utility function for handling a unidirectional run.  The run must not
779e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * contain tabs or emoji but can contain styles.
780e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
781e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIndex the run index
782e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param start the line-relative start of the run
7830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param measureLimit the offset to measure to, between start and limit inclusive
784e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param limit the limit of the run
785e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
786e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas, can be null
787e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the end of the run closest to the leading margin
788e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param top the top of the line
789e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline
790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param bottom the bottom of the line
791e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param fmi receives metrics information, can be null
792e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param needWidth true if the width is required
793e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the signed width of the run based on the run direction; only
794e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * valid if needWidth is true
795e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
7960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private float handleRun(int runIndex, int start, int measureLimit,
797e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
7980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int bottom, FontMetricsInt fmi, boolean needWidth) {
799e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // Shaping needs to take into account context up to metric boundaries,
801e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        // but rendering needs to take into account character style boundaries.
8020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // So we iterate through metric runs to get metric bounds,
8030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // then within each metric run iterate through character style runs
8040c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        // for the run bounds.
805e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        float ox = x;
8060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt        for (int i = start, inext; i < measureLimit; i = inext) {
807e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            TextPaint wp = mWorkPaint;
808e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            wp.set(mPaint);
809e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int mlimit;
811e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (mSpanned == null) {
812e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                inext = limit;
8130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                mlimit = measureLimit;
814e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            } else {
815e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                inext = mSpanned.nextSpanTransition(mStart + i, mStart + limit,
816e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        MetricAffectingSpan.class) - mStart;
817e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                mlimit = inext < measureLimit ? inext : measureLimit;
819e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + i,
8200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        mStart + mlimit, MetricAffectingSpan.class);
821e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
822e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                if (spans.length > 0) {
823e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    ReplacementSpan replacement = null;
824e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    for (int j = 0; j < spans.length; j++) {
825e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        MetricAffectingSpan span = spans[j];
826e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (span instanceof ReplacementSpan) {
827e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                            replacement = (ReplacementSpan)span;
828e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        } else {
8290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            // We might have a replacement that uses the draw
8300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            // state, otherwise measure state would suffice.
8310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            span.updateDrawState(wp);
832e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        }
833e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
834e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
835e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    if (replacement != null) {
836e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        x += handleReplacement(replacement, wp, runIndex, i,
8370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                                mlimit, runIsRtl, c, x, top, y, bottom, fmi,
8380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                                needWidth || mlimit < measureLimit);
839e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        continue;
840e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
841e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
842e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
843e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            if (mSpanned == null || c == null) {
8450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                x += handleText(wp, i, mlimit, i, inext, runIsRtl, c, x, top,
8460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        y, bottom, fmi, needWidth || mlimit < measureLimit);
8470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            } else {
8480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                for (int j = i, jnext; j < mlimit; j = jnext) {
8490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    jnext = mSpanned.nextSpanTransition(mStart + j,
8500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            mStart + mlimit, CharacterStyle.class) - mStart;
851e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    CharacterStyle[] spans = mSpanned.getSpans(mStart + j,
8530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            mStart + jnext, CharacterStyle.class);
854e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
8550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    wp.set(mPaint);
8560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    for (int k = 0; k < spans.length; k++) {
8570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        CharacterStyle span = spans[k];
8580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                        span.updateDrawState(wp);
859e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                    }
8600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt
8610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
8620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                            top, y, bottom, fmi, needWidth || jnext < measureLimit);
863e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                }
864e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            }
865e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
866e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
867e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return x - ox;
868e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
869e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
870e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
871e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Render a text run with the set-up paint.
872e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
873e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param c the canvas
874e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param wp the paint used to render the text
8750c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param start the start of the run
8760c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param end the end of the run
8770c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextStart the start of context for the run
8780c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt     * @param contextEnd the end of the context for the run
879e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param runIsRtl true if the run is right-to-left
880e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param x the x position of the left edge of the run
881e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param y the baseline of the run
882e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
8830c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt    private void drawTextRun(Canvas c, TextPaint wp, int start, int end,
8840c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextStart, int contextEnd, boolean runIsRtl, float x, int y) {
885e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
886f47d7405bbcb25d7cdf89ebb059f41520fe9ab87Doug Felt        int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR;
887e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mCharsValid) {
8880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int count = end - start;
8890c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int contextCount = contextEnd - contextStart;
8900c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mChars, start, count, contextStart, contextCount,
8910c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    x, y, flags, wp);
892e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        } else {
8930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            int delta = mStart;
8940c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt            c.drawTextRun(mText, delta + start, delta + end,
8950c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt                    delta + contextStart, delta + contextEnd, x, y, flags, wp);
896e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
897e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
898e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
899e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
900e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the ascent of the text at start.  This is used for scaling
901e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * emoji.
902e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
903e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param pos the line-relative position
904e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the ascent of the text at start
905e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
906e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float ascent(int pos) {
907e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (mSpanned == null) {
908e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
909e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
910e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
911e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        pos += mStart;
912e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1,
913e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                MetricAffectingSpan.class);
914e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        if (spans.length == 0) {
915e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            return mPaint.ascent();
916e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
917e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
918e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        TextPaint wp = mWorkPaint;
919e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        wp.set(mPaint);
920e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        for (MetricAffectingSpan span : spans) {
921e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            span.updateMeasureState(wp);
922e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
923e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        return wp.ascent();
924e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
925e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
926e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    /**
927e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * Returns the next tab position.
928e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     *
929e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @param h the (unsigned) offset from the leading margin
930e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * @return the (unsigned) tab position after this offset
931e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     */
932e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    float nextTab(float h) {
933c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (mTabs != null) {
934c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt            return mTabs.nextTab(h);
935e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        }
936c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        return TabStops.nextDefaultStop(h, TAB_INCREMENT);
937e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    }
938e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
939e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private static final int TAB_INCREMENT = 20;
940e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt}
941