TextLine.java revision c1f44830809f0a8526855f13822702ea756214fa
1e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/* 2e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Copyright (C) 2010 The Android Open Source Project 3e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 4e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Licensed under the Apache License, Version 2.0 (the "License"); 5e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * you may not use this file except in compliance with the License. 6e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * You may obtain a copy of the License at 7e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 8e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * http://www.apache.org/licenses/LICENSE-2.0 9e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 10e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Unless required by applicable law or agreed to in writing, software 11e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * distributed under the License is distributed on an "AS IS" BASIS, 12e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * See the License for the specific language governing permissions and 14e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * limitations under the License. 15e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 16e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 17e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltpackage android.text; 18e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 19e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Bitmap; 20e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Canvas; 21e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint; 22e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.graphics.Paint.FontMetricsInt; 23cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunneimport android.graphics.RectF; 24e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.Layout.Directions; 25c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.Layout.TabStops; 26e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.CharacterStyle; 27e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.MetricAffectingSpan; 28e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.text.style.ReplacementSpan; 29e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltimport android.util.Log; 30e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 31dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunneimport com.android.internal.util.ArrayUtils; 32dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 33945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunneimport java.lang.reflect.Array; 34945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 35e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt/** 36e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Represents a line of styled text, for measuring in visual order and 37e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * for rendering. 38e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 39e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Get a new instance using obtain(), and when finished with it, return it 40e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * to the pool using recycle(). 41e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 42e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>Call set to prepare the instance for use, then either draw, measure, 43e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * metrics, or caretToLeftRightOf. 44e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 45e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @hide 46e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 47e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Feltclass TextLine { 48bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private static final boolean DEBUG = false; 49bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy 50e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private TextPaint mPaint; 51e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private CharSequence mText; 52e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private int mStart; 53e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private int mLen; 54e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private int mDir; 55e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private Directions mDirections; 56e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private boolean mHasTabs; 57c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt private TabStops mTabs; 58e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private char[] mChars; 59e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private boolean mCharsValid; 60e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private Spanned mSpanned; 61345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne private final TextPaint mWorkPaint = new TextPaint(); 62c1f44830809f0a8526855f13822702ea756214faGilles Debunne private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet = 63c1f44830809f0a8526855f13822702ea756214faGilles Debunne new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class); 64c1f44830809f0a8526855f13822702ea756214faGilles Debunne private final SpanSet<CharacterStyle> mCharacterStyleSpanSet = 65c1f44830809f0a8526855f13822702ea756214faGilles Debunne new SpanSet<CharacterStyle>(CharacterStyle.class); 66c1f44830809f0a8526855f13822702ea756214faGilles Debunne private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet = 67c1f44830809f0a8526855f13822702ea756214faGilles Debunne new SpanSet<ReplacementSpan>(ReplacementSpan.class); 68e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 69bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private static final TextLine[] sCached = new TextLine[3]; 70e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 71e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 72e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns a new TextLine from the shared pool. 73e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 74e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return an uninitialized TextLine 75e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 76e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt static TextLine obtain() { 77e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextLine tl; 78bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy synchronized (sCached) { 79bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy for (int i = sCached.length; --i >= 0;) { 80bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy if (sCached[i] != null) { 81bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy tl = sCached[i]; 82bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy sCached[i] = null; 83e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return tl; 84e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 85e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 86e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 87e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl = new TextLine(); 88bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy if (DEBUG) { 89bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy Log.v("TLINE", "new: " + tl); 90bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy } 91e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return tl; 92e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 93e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 94e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 95e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Puts a TextLine back into the shared pool. Do not use this TextLine once 96e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * it has been returned. 97e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param tl the textLine 98e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return null, as a convenience from clearing references to the provided 99e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * TextLine 100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 101e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt static TextLine recycle(TextLine tl) { 102e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.mText = null; 103e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.mPaint = null; 104e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt tl.mDirections = null; 105bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy synchronized(sCached) { 106bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy for (int i = 0; i < sCached.length; ++i) { 107bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy if (sCached[i] == null) { 108bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy sCached[i] = tl; 109f902d7bc49797ec277b4576c921dfffa15d741ddGilles Debunne break; 110e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 111e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 112e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 113e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return null; 114e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 115e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 116e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 117e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Initializes a TextLine and prepares it for use. 118e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 119e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param paint the base paint for the line 120e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param text the text, can be Styled 121e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the line relative to the text 122e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the line relative to the text 123e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param dir the paragraph direction of this line 124e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param directions the directions information of this line 125e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param hasTabs true if the line might contain tabs or emoji 126c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt * @param tabStops the tabStops. Can be null. 127e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 128e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt void set(TextPaint paint, CharSequence text, int start, int limit, int dir, 129c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt Directions directions, boolean hasTabs, TabStops tabStops) { 130e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mPaint = paint; 131e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mText = text; 132e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mStart = start; 133e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mLen = limit - start; 134e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mDir = dir; 135e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mDirections = directions; 1368059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio if (mDirections == null) { 1378059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio throw new IllegalArgumentException("Directions cannot be null"); 1388059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio } 139e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mHasTabs = hasTabs; 140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mSpanned = null; 141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 142e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean hasReplacement = false; 143e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (text instanceof Spanned) { 144e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mSpanned = (Spanned) text; 145c1f44830809f0a8526855f13822702ea756214faGilles Debunne mReplacementSpanSpanSet.init(mSpanned, start, limit); 146c1f44830809f0a8526855f13822702ea756214faGilles Debunne hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; 147e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 148e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1491e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; 150e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 151e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 152e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mChars == null || mChars.length < mLen) { 153e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt mChars = new char[ArrayUtils.idealCharArraySize(mLen)]; 154e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 155e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextUtils.getChars(text, start, limit, mChars, 0); 1560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (hasReplacement) { 1570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Handle these all at once so we don't have to do it as we go. 1580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Replace the first character of each replacement run with the 1590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // object-replacement character and the remainder with zero width 1600c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // non-break space aka BOM. Cursor movement code skips these 1610c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // zero-width characters. 1620c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt char[] chars = mChars; 1630c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int i = start, inext; i < limit; i = inext) { 164c1f44830809f0a8526855f13822702ea756214faGilles Debunne inext = mReplacementSpanSpanSet.getNextTransition(i, limit); 165c1f44830809f0a8526855f13822702ea756214faGilles Debunne if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) { 1661e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne // transition into a span 1670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt chars[i - start] = '\ufffc'; 1680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int j = i - start + 1, e = inext - start; j < e; ++j) { 1690c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip 1700c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1720c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 1730c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 174e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 175c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt mTabs = tabStops; 176e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 177e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 178e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 179e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Renders the TextLine. 180e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 181e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas to render on 182e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the leading margin position 183e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 184e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 185e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 186e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 187e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt void draw(Canvas c, float x, int top, int y, int bottom) { 188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (!mHasTabs) { 189e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { 190bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy drawRun(c, 0, mLen, false, x, top, y, bottom, false); 191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return; 192e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 193e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { 194bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy drawRun(c, 0, mLen, true, x, top, y, bottom, false); 195e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return; 196e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 197e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 198e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 199e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float h = 0; 200e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 201e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt RectF emojiRect = null; 202e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 203e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lastRunIndex = runs.length - 2; 204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int i = 0; i < runs.length; i += 2) { 205e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runStart = runs[i]; 206e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK); 207e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > mLen) { 208e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = mLen; 209e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 210e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0; 211e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 212e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int segstart = runStart; 213e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { 214e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int codept = 0; 215e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Bitmap bm = null; 216e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 217e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mHasTabs && j < runLimit) { 218e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = mChars[j]; 219e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) { 220e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = Character.codePointAt(mChars, j); 221e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) { 222e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept); 223e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (codept > 0xffff) { 224e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt ++j; 225e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 226e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 227e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 228e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 229e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (j == runLimit || codept == '\t' || bm != null) { 231bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom, 232e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt i != lastRunIndex || j != mLen); 233e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 234e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept == '\t') { 235e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h = mDir * nextTab(h * mDir); 236e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (bm != null) { 237e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float bmAscent = ascent(j); 238e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float bitmapHeight = bm.getHeight(); 239e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float scale = -bmAscent / bitmapHeight; 240e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float width = bm.getWidth() * scale; 241e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 242e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (emojiRect == null) { 243e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt emojiRect = new RectF(); 244e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt emojiRect.set(x + h, y + bmAscent, 246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt x + h + width, y); 247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt c.drawBitmap(bm, null, emojiRect, mPaint); 248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += width; 249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt j++; 250e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 251e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt segstart = j + 1; 252e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 253e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 254e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 255e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 256e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 257e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 258e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns metrics information for the entire line. 259e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 260e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives font metrics information, can be null 261e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the line 262e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 263e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float metrics(FontMetricsInt fmi) { 264e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return measure(mLen, false, fmi); 265e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 266e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 267e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 268e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns information about a position on the line. 269e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 270e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the line-relative character offset, between 0 and the 271e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * line length, inclusive 272e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param trailing true to measure the trailing edge of the character 273e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * before offset, false to measure the leading edge of the character 274e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * at offset. 275e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information about the requested 276e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * character, can be null. 277e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed offset from the leading margin to the requested 278e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * character edge. 279e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 280e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float measure(int offset, boolean trailing, FontMetricsInt fmi) { 281e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int target = trailing ? offset - 1 : offset; 282e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (target < 0) { 283e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return 0; 284e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 285e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 286e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float h = 0; 287e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 288e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (!mHasTabs) { 289e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { 290bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return measureRun(0, offset, mLen, false, fmi); 291e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 292e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { 293bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return measureRun(0, offset, mLen, true, fmi); 294e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 295e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 296e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 297e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt char[] chars = mChars; 298e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 299e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int i = 0; i < runs.length; i += 2) { 300e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runStart = runs[i]; 301e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK); 302e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > mLen) { 303e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = mLen; 304e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 305e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0; 306e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 307e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int segstart = runStart; 308e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { 309e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int codept = 0; 310e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Bitmap bm = null; 311e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 312e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mHasTabs && j < runLimit) { 313e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = chars[j]; 314e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) { 315e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt codept = Character.codePointAt(chars, j); 316e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) { 317e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept); 318e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (codept > 0xffff) { 319e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt ++j; 320e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 321e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 322e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 323e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 324e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 325e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (j == runLimit || codept == '\t' || bm != null) { 326e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean inSegment = target >= segstart && target < j; 327e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 328e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; 329e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (inSegment && advance) { 330bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return h += measureRun(segstart, offset, j, runIsRtl, fmi); 331e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 332e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 333bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy float w = measureRun(segstart, j, j, runIsRtl, fmi); 334e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += advance ? w : -w; 335e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 336e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (inSegment) { 337bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return h += measureRun(segstart, offset, j, runIsRtl, null); 338e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 339e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 340e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (codept == '\t') { 341e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (offset == j) { 342e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 343e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 344e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h = mDir * nextTab(h * mDir); 345e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (target == j) { 346e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 347e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 348e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 349e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 350e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (bm != null) { 351e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float bmAscent = ascent(j); 352e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float wid = bm.getWidth() * -bmAscent / bm.getHeight(); 353e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt h += mDir * wid; 354e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt j++; 355e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 356e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 357e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt segstart = j + 1; 358e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 359e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 360e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 361e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 362e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return h; 363e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 364e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 365e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 366e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Draws a unidirectional (but possibly multi-styled) run of text. 367e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 368bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 369e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas to draw on 370e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start 371e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the line-relative limit 372e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 373e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the position of the run that is closest to the leading margin 374e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 375e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 376e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 377e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width value is required. 378e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run, based on the paragraph direction. 379e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Only valid if needWidth is true. 380e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 381bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private float drawRun(Canvas c, int start, 382e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int limit, boolean runIsRtl, float x, int top, int y, int bottom, 383e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean needWidth) { 384e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 385e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { 386bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy float w = -measureRun(start, limit, limit, runIsRtl, null); 387bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy handleRun(start, limit, limit, runIsRtl, c, x + w, top, 3880c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt y, bottom, null, false); 389e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return w; 390e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 391e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 392bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return handleRun(start, limit, limit, runIsRtl, c, x, top, 3930c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt y, bottom, null, needWidth); 394e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 395e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 396e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 397e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Measures a unidirectional (but possibly multi-styled) run of text. 398e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 399bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 400e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start of the run 401e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the offset to measure to, between start and limit inclusive 402e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the line-relative limit of the run 403e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 404e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information about the requested 405e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * run, can be null. 406e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width from the start of the run to the leading edge 407e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * of the character at offset, based on the run (not paragraph) direction 408e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 409bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private float measureRun(int start, int offset, int limit, boolean runIsRtl, 410bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy FontMetricsInt fmi) { 411bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true); 412e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 413e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 414e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 415e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Walk the cursor through this line, skipping conjuncts and 416e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * zero-width characters. 417e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 418e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * <p>This function cannot properly walk the cursor off the ends of the line 419e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * since it does not know about any shaping on the previous/following line 420e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * that might affect the cursor position. Callers must either avoid these 421e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * situations or handle the result specially. 422e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 423e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param cursor the starting position of the cursor, between 0 and the 424e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * length of the line, inclusive 425e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param toLeft true if the caret is moving to the left. 426e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the new offset. If it is less than 0 or greater than the length 427e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * of the line, the previous/following line should be examined to get the 428e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * actual offset. 429e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 430e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int getOffsetToLeftRightOf(int cursor, boolean toLeft) { 431e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 1) The caret marks the leading edge of a character. The character 432e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // logically before it might be on a different level, and the active caret 433e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // position is on the character at the lower level. If that character 434e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // was the previous character, the caret is on its trailing edge. 435e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 2) Take this character/edge and move it in the indicated direction. 436e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // This gives you a new character and a new edge. 437e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 3) This position is between two visually adjacent characters. One of 438e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // these might be at a lower level. The active position is on the 439e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // character at the lower level. 440e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // 4) If the active position is on the trailing edge of the character, 441e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the new caret position is the following logical character, else it 442e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // is the character. 443e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 444e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineStart = 0; 445e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int lineEnd = mLen; 446e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean paraIsRtl = mDir == -1; 447e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int[] runs = mDirections.mDirections; 448e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 449e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1; 450e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean trailing = false; 451e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 452e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor == lineStart) { 453e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = -2; 454e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else if (cursor == lineEnd) { 455e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = runs.length; 456e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 457e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // First, get information about the run containing the character with 458e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the active caret. 459e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (runIndex = 0; runIndex < runs.length; runIndex += 2) { 460e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runStart = lineStart + runs[runIndex]; 461e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor >= runStart) { 462e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK); 463e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runLimit > lineEnd) { 464e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = lineEnd; 465e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 466e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor < runLimit) { 467e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) & 468e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Layout.RUN_LEVEL_MASK; 469e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor == runStart) { 470e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The caret is on a run boundary, see if we should 471e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // use the position on the trailing edge of the previous 472e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // logical character instead. 473e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit; 474e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int pos = cursor - 1; 475e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) { 476e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunStart = lineStart + runs[prevRunIndex]; 477e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (pos >= prevRunStart) { 478e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLimit = prevRunStart + 479e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK); 480e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (prevRunLimit > lineEnd) { 481e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLimit = lineEnd; 482e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 483e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (pos < prevRunLimit) { 484e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) 485e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt & Layout.RUN_LEVEL_MASK; 486e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (prevRunLevel < runLevel) { 487e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Start from logically previous character. 488e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = prevRunIndex; 489e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = prevRunLevel; 490e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runStart = prevRunStart; 491e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLimit = prevRunLimit; 492e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt trailing = true; 493e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 494e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 495e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 496e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 497e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 498e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 499e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 500e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 501e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 502e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 503e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 504e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // caret might be == lineEnd. This is generally a space or paragraph 505e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // separator and has an associated run, but might be the end of 506e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // text, in which case it doesn't. If that happens, we ran off the 507e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // end of the run list, and runIndex == runs.length. In this case, 508e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we are at a run boundary so we skip the below test. 509e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runIndex != runs.length) { 510e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean runIsRtl = (runLevel & 0x1) != 0; 511e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == runIsRtl; 512e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (cursor != (advance ? runLimit : runStart) || advance != trailing) { 513e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Moving within or into the run, so we can move logically. 5140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit, 5150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt runIsRtl, cursor, advance); 516e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // If the new position is internal to the run, we're at the strong 517e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // position already so we're finished. 518e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret != (advance ? runLimit : runStart)) { 519e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return newCaret; 520e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 521e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 522e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 523e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 524e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 525e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // If newCaret is -1, we're starting at a run boundary and crossing 526e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // into another run. Otherwise we've arrived at a run boundary, and 527e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // need to figure out which character to attach to. Note we might 528e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // need to run this twice, if we cross a run boundary and end up at 529e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // another run boundary. 530e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt while (true) { 531e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean advance = toLeft == paraIsRtl; 532e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunIndex = runIndex + (advance ? 2 : -2); 533e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunIndex >= 0 && otherRunIndex < runs.length) { 534e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunStart = lineStart + runs[otherRunIndex]; 535e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunLimit = otherRunStart + 536e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK); 537e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunLimit > lineEnd) { 538e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt otherRunLimit = lineEnd; 539e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 540e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) & 541e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt Layout.RUN_LEVEL_MASK; 542e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt boolean otherRunIsRtl = (otherRunLevel & 1) != 0; 543e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 544e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt advance = toLeft == otherRunIsRtl; 545e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == -1) { 5460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart, 5470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt otherRunLimit, otherRunIsRtl, 548e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt advance ? otherRunStart : otherRunLimit, advance); 549e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == (advance ? otherRunLimit : otherRunStart)) { 550e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Crossed and ended up at a new boundary, 551e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // repeat a second and final time. 552e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runIndex = otherRunIndex; 553e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt runLevel = otherRunLevel; 554e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt continue; 555e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 556e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 557e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 558e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 559e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The new caret is at a boundary. 560e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (otherRunLevel < runLevel) { 561e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // The strong character is in the other run. 562e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt newCaret = advance ? otherRunStart : otherRunLimit; 563e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 564e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 565e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 566e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 567e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret == -1) { 568e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // We're walking off the end of the line. The paragraph 569e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // level is always equal to or lower than any internal level, so 570e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the boundaries get the strong caret. 5710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt newCaret = advance ? mLen + 1 : -1; 572e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 573e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 574e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 575e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Else we've arrived at the end of the line. That's a strong position. 576e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // We might have arrived here by crossing over a run with no internal 577e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // breaks and dropping out of the above loop before advancing one final 578e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // time, so reset the caret. 579e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Note, we use '<=' below to handle a situation where the only run 580e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // on the line is a counter-directional run. If we're not advancing, 581e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // we can end up at the 'lineEnd' position but the caret we want is at 582e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // the lineStart. 583e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (newCaret <= lineEnd) { 584e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt newCaret = advance ? lineEnd : lineStart; 585e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 586e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt break; 587e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 588e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 589e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return newCaret; 590e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 591e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 592e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 593e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the next valid offset within this directional run, skipping 594e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * conjuncts and zero-width characters. This should not be called to walk 595cc3ec6cdb2b892eb29513e72d8b205acbe997b25Gilles Debunne * off the end of the line, since the returned values might not be valid 5960c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * on neighboring lines. If the returned offset is less than zero or 5970c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * greater than the line length, the offset should be recomputed on the 5980c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * preceding or following line, respectively. 599e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 600e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIndex the run index 6010c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runStart the start of the run 6020c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runLimit the limit of the run 6030c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param runIsRtl true if the run is right-to-left 604e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param offset the offset 605e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param after true if the new offset should logically follow the provided 606e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * offset 607e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the new offset 608e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 6090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit, 6100c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean runIsRtl, int offset, boolean after) { 611e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 6120c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (runIndex < 0 || offset == (after ? mLen : 0)) { 6130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // Walking off end of line. Since we don't know 6140c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // what cursor positions are available on other lines, we can't 6150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // return accurate values. These are a guess. 616e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (after) { 6170c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart; 6180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart; 6200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6210c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt TextPaint wp = mWorkPaint; 6230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt wp.set(mPaint); 6240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6250c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanStart = runStart; 6260c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int spanLimit; 6270c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (mSpanned == null) { 6280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanLimit = runLimit; 6290c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int target = after ? offset + 1 : offset; 6310c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int limit = mStart + runLimit; 6320c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt while (true) { 6330c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit, 6340c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan.class) - mStart; 6350c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (spanLimit >= target) { 6360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt break; 637e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 6380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt spanStart = spanLimit; 6390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart, 6420c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt mStart + spanLimit, MetricAffectingSpan.class); 6431e3ac18e7ad03e02819f3e1a89d6a80a2bb7645fGilles Debunne spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class); 6440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (spans.length > 0) { 6460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ReplacementSpan replacement = null; 6470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int j = 0; j < spans.length; j++) { 6480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt MetricAffectingSpan span = spans[j]; 6490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (span instanceof ReplacementSpan) { 6500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt replacement = (ReplacementSpan)span; 6510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt span.updateMeasureState(wp); 6530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6540c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 6550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 6560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (replacement != null) { 6570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // If we have a replacement span, we're moving either to 6580c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // the start or end of this span. 6590c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return after ? spanLimit : spanStart; 660e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 661e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 662e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 663e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 6640c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; 6650c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE; 6660c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (mCharsValid) { 6670c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart, 6680c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt flags, offset, cursorOpt); 6690c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 6700c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt return wp.getTextRunCursor(mText, mStart + spanStart, 671345cb03315a0813ec57e44f97fc3fa4af6b3c309Gilles Debunne mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart; 672e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 673e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 674e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 675e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 6760bb000931bb841e75903d655552d1626ae158707Gilles Debunne * @param wp 6770bb000931bb841e75903d655552d1626ae158707Gilles Debunne */ 6780bb000931bb841e75903d655552d1626ae158707Gilles Debunne private static void expandMetricsFromPaint(FontMetricsInt fmi, TextPaint wp) { 6790bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousTop = fmi.top; 6800bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousAscent = fmi.ascent; 6810bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousDescent = fmi.descent; 6820bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousBottom = fmi.bottom; 6830bb000931bb841e75903d655552d1626ae158707Gilles Debunne final int previousLeading = fmi.leading; 6840bb000931bb841e75903d655552d1626ae158707Gilles Debunne 6850bb000931bb841e75903d655552d1626ae158707Gilles Debunne wp.getFontMetricsInt(fmi); 6860bb000931bb841e75903d655552d1626ae158707Gilles Debunne 6878a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom, 6888a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousLeading); 6898a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio } 6908a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 6918a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio static void updateMetrics(FontMetricsInt fmi, int previousTop, int previousAscent, 6928a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousDescent, int previousBottom, int previousLeading) { 6930bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.top = Math.min(fmi.top, previousTop); 6940bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.ascent = Math.min(fmi.ascent, previousAscent); 6950bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.descent = Math.max(fmi.descent, previousDescent); 6960bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.bottom = Math.max(fmi.bottom, previousBottom); 6970bb000931bb841e75903d655552d1626ae158707Gilles Debunne fmi.leading = Math.max(fmi.leading, previousLeading); 6980bb000931bb841e75903d655552d1626ae158707Gilles Debunne } 6990bb000931bb841e75903d655552d1626ae158707Gilles Debunne 7000bb000931bb841e75903d655552d1626ae158707Gilles Debunne /** 701e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for measuring and rendering text. The text must 702e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * not include a tab or emoji. 703e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 704e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the working paint 705e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the text 7060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param end the end of the text 707e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 708e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null if rendering is not needed 709e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the edge of the run closest to the leading margin 710e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 711e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 712e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 713e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 714e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width of the run is needed 715e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 716e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 717e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 7180c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private float handleText(TextPaint wp, int start, int end, 7190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextStart, int contextEnd, boolean runIsRtl, 7200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt Canvas c, float x, int top, int y, int bottom, 721e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt FontMetricsInt fmi, boolean needWidth) { 722e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 723850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio // Get metrics first (even for empty strings or "0" width runs) 724850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio if (fmi != null) { 725850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio expandMetricsFromPaint(fmi, wp); 726850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio } 727e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 7280c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int runLen = end - start; 729850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio // No need to do anything if the run width is "0" 730850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio if (runLen == 0) { 731850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio return 0f; 732850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio } 733850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio 734850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio float ret = 0; 735850dffa01ba9111799f24800ae8550eca457d757Fabrice Di Meglio 7360c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextLen = contextEnd - contextStart; 737c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) { 7380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; 739e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 7400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ret = wp.getTextRunAdvances(mChars, start, runLen, 7410c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt contextStart, contextLen, flags, null, 0); 742e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 7430c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int delta = mStart; 7440c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ret = wp.getTextRunAdvances(mText, delta + start, 7450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt delta + end, delta + contextStart, delta + contextEnd, 7460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt flags, null, 0); 747e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 748e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 749e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 750e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (c != null) { 751e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (runIsRtl) { 752e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt x -= ret; 753e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 754e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 755e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (wp.bgColor != 0) { 756dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne int previousColor = wp.getColor(); 757dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne Paint.Style previousStyle = wp.getStyle(); 758dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 759e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.setColor(wp.bgColor); 760e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.setStyle(Paint.Style.FILL); 761e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt c.drawRect(x, top, x + ret, bottom, wp); 762e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 763dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setStyle(previousStyle); 764dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setColor(previousColor); 765dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne } 766dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 767c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne if (wp.underlineColor != 0) { 768dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h 769e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize(); 770dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 771dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne int previousColor = wp.getColor(); 772dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne Paint.Style previousStyle = wp.getStyle(); 773e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin boolean previousAntiAlias = wp.isAntiAlias(); 774dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 775dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setStyle(Paint.Style.FILL); 776e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin wp.setAntiAlias(true); 777e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin 778c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne wp.setColor(wp.underlineColor); 779c9fd978da60f76c0576150c55629a034e1fa19fbGilles Debunne c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThickness, wp); 780dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne 781dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setStyle(previousStyle); 782dd8f5ed79c7baed35b3f04e4778aff7867653255Gilles Debunne wp.setColor(previousColor); 783e6d368218918f911b1954296dab25bf84147b4c6Luca Zanolin wp.setAntiAlias(previousAntiAlias); 784e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 785e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 7860c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl, 7870c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x, y + wp.baselineShift); 788e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 789e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 790e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return runIsRtl ? -ret : ret; 791e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 792e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 793e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 794e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for measuring and rendering a replacement. 795e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 796bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 797e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param replacement the replacement 798e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the work paint 799e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the start of the run 800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the run 801e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 802e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null if not rendering 803e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the edge of the replacement closest to the leading margin 804e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 805e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 806e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 807e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 808e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width of the replacement is needed 809e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 810e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 811e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 812e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private float handleReplacement(ReplacementSpan replacement, TextPaint wp, 813bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy int start, int limit, boolean runIsRtl, Canvas c, 814e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float x, int top, int y, int bottom, FontMetricsInt fmi, 8150c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt boolean needWidth) { 816e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 817e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float ret = 0; 818e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8190c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int textStart = mStart + start; 8200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int textLimit = mStart + limit; 821e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (needWidth || (c != null && runIsRtl)) { 8238a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousTop = 0; 8248a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousAscent = 0; 8258a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousDescent = 0; 8268a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousBottom = 0; 8278a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio int previousLeading = 0; 8288a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8298a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio boolean needUpdateMetrics = (fmi != null); 8308a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8318a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio if (needUpdateMetrics) { 8328a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousTop = fmi.top; 8338a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousAscent = fmi.ascent; 8348a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousDescent = fmi.descent; 8358a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousBottom = fmi.bottom; 8368a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousLeading = fmi.leading; 8378a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio } 8388a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt ret = replacement.getSize(wp, mText, textStart, textLimit, fmi); 8408a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio 8418a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio if (needUpdateMetrics) { 8428a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom, 8438a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio previousLeading); 8448a5137a5aeba39cbc2c57c83ef79241b446d0cb7Fabrice Di Meglio } 8450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } 846e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 8470c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (c != null) { 8480c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt if (runIsRtl) { 8490c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x -= ret; 850e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 8510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt replacement.draw(c, mText, textStart, textLimit, 8520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x, top, y, bottom, wp); 853e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 854e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 855e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return runIsRtl ? -ret : ret; 856e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 857e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 858945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne private static class SpanSet<E> { 859c1f44830809f0a8526855f13822702ea756214faGilles Debunne int numberOfSpans; 860c1f44830809f0a8526855f13822702ea756214faGilles Debunne E[] spans; 861c1f44830809f0a8526855f13822702ea756214faGilles Debunne int[] spanStarts; 862c1f44830809f0a8526855f13822702ea756214faGilles Debunne int[] spanEnds; 863c1f44830809f0a8526855f13822702ea756214faGilles Debunne int[] spanFlags; 864c1f44830809f0a8526855f13822702ea756214faGilles Debunne final Class<? extends E> classType; 865c1f44830809f0a8526855f13822702ea756214faGilles Debunne 866c1f44830809f0a8526855f13822702ea756214faGilles Debunne SpanSet(Class<? extends E> type) { 867c1f44830809f0a8526855f13822702ea756214faGilles Debunne classType = type; 868c1f44830809f0a8526855f13822702ea756214faGilles Debunne numberOfSpans = 0; 869c1f44830809f0a8526855f13822702ea756214faGilles Debunne } 870945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 871945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne @SuppressWarnings("unchecked") 872c1f44830809f0a8526855f13822702ea756214faGilles Debunne public void init(Spanned spanned, int start, int limit) { 873c1f44830809f0a8526855f13822702ea756214faGilles Debunne final E[] allSpans = spanned.getSpans(start, limit, classType); 874945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final int length = allSpans.length; 875c1f44830809f0a8526855f13822702ea756214faGilles Debunne 876c1f44830809f0a8526855f13822702ea756214faGilles Debunne if (length > 0 && (spans == null || spans.length < length)) { 877c1f44830809f0a8526855f13822702ea756214faGilles Debunne // These arrays may end up being too large because of empty spans 878c1f44830809f0a8526855f13822702ea756214faGilles Debunne spans = (E[]) Array.newInstance(classType, length); 879c1f44830809f0a8526855f13822702ea756214faGilles Debunne spanStarts = new int[length]; 880c1f44830809f0a8526855f13822702ea756214faGilles Debunne spanEnds = new int[length]; 881c1f44830809f0a8526855f13822702ea756214faGilles Debunne spanFlags = new int[length]; 882c1f44830809f0a8526855f13822702ea756214faGilles Debunne } 883945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 884945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne int count = 0; 885945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne for (int i = 0; i < length; i++) { 886945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final E span = allSpans[i]; 887945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 888945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final int spanStart = spanned.getSpanStart(span); 889945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final int spanEnd = spanned.getSpanEnd(span); 890945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (spanStart == spanEnd) continue; 891945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 892945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final int spanFlag = spanned.getSpanFlags(span); 893945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 894c1f44830809f0a8526855f13822702ea756214faGilles Debunne spans[i] = span; 895c1f44830809f0a8526855f13822702ea756214faGilles Debunne spanStarts[i] = spanStart; 896c1f44830809f0a8526855f13822702ea756214faGilles Debunne spanEnds[i] = spanEnd; 897c1f44830809f0a8526855f13822702ea756214faGilles Debunne spanFlags[i] = spanFlag; 898945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 899945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne count++; 900945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 901945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne numberOfSpans = count; 902945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 903945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 9048a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne public boolean hasSpansIntersecting(int start, int end) { 9058a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne for (int i = 0; i < numberOfSpans; i++) { 9068a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne // equal test is valid since both intervals are not empty by construction 9078a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne if (spanStarts[i] >= end || spanEnds[i] <= start) continue; 9088a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne return true; 9098a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne } 9108a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne return false; 9118a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne } 9128a439ac7a34d6b83782a672f3d6aa90fa262409cGilles Debunne 913945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne int getNextTransition(int start, int limit) { 914945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne for (int i = 0; i < numberOfSpans; i++) { 915945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final int spanStart = spanStarts[i]; 916945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final int spanEnd = spanEnds[i]; 917945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (spanStart > start && spanStart < limit) limit = spanStart; 918945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (spanEnd > start && spanEnd < limit) limit = spanEnd; 919945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 920945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne return limit; 921945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 922945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 923945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 924e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 925e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Utility function for handling a unidirectional run. The run must not 926e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * contain tabs or emoji but can contain styles. 927e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 928bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy * 929e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param start the line-relative start of the run 9300c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param measureLimit the offset to measure to, between start and limit inclusive 931e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param limit the limit of the run 932e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 933e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas, can be null 934e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the end of the run closest to the leading margin 935e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param top the top of the line 936e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline 937e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param bottom the bottom of the line 938e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param fmi receives metrics information, can be null 939e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param needWidth true if the width is required 940e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the signed width of the run based on the run direction; only 941e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * valid if needWidth is true 942e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 943bc7cdb6783d059249133b1c0baf52c305c6b4a33Romain Guy private float handleRun(int start, int measureLimit, 944e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt int limit, boolean runIsRtl, Canvas c, float x, int top, int y, 9450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int bottom, FontMetricsInt fmi, boolean needWidth) { 946e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 947f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne // Case of an empty line, make sure we update fmi according to mPaint 948f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne if (start == measureLimit) { 949f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne TextPaint wp = mWorkPaint; 950f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne wp.set(mPaint); 95115c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio if (fmi != null) { 95215c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio expandMetricsFromPaint(fmi, wp); 95315c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio } 95415c097a1c23105cdc0dd66dd5605ff35467d7118Fabrice Di Meglio return 0f; 955f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne } 956f483e514d4ed3b93cc5ba22beb9c85efcda75535Gilles Debunne 957945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (mSpanned == null) { 958945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne TextPaint wp = mWorkPaint; 959945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne wp.set(mPaint); 960945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final int mlimit = measureLimit; 961945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top, 962945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne y, bottom, fmi, needWidth || mlimit < measureLimit); 963945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 964945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 965c1f44830809f0a8526855f13822702ea756214faGilles Debunne mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); 966c1f44830809f0a8526855f13822702ea756214faGilles Debunne mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); 967945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 968e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // Shaping needs to take into account context up to metric boundaries, 969e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt // but rendering needs to take into account character style boundaries. 9700c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // So we iterate through metric runs to get metric bounds, 9710c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // then within each metric run iterate through character style runs 9720c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt // for the run bounds. 973945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne final float originalX = x; 9740c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int i = start, inext; i < measureLimit; i = inext) { 975e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextPaint wp = mWorkPaint; 976e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.set(mPaint); 977e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 978c1f44830809f0a8526855f13822702ea756214faGilles Debunne inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) - 979c1f44830809f0a8526855f13822702ea756214faGilles Debunne mStart; 980945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne int mlimit = Math.min(inext, measureLimit); 981945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 982945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne ReplacementSpan replacement = null; 983945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 984c1f44830809f0a8526855f13822702ea756214faGilles Debunne for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) { 985945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT 986945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // empty by construction. This special case in getSpans() explains the >= & <= tests 987c1f44830809f0a8526855f13822702ea756214faGilles Debunne if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) || 988c1f44830809f0a8526855f13822702ea756214faGilles Debunne (mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue; 989c1f44830809f0a8526855f13822702ea756214faGilles Debunne MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j]; 990945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (span instanceof ReplacementSpan) { 991945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne replacement = (ReplacementSpan)span; 992945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } else { 993945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // We might have a replacement that uses the draw 994945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // state, otherwise measure state would suffice. 995945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne span.updateDrawState(wp); 996e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 997e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 998e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 999945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (replacement != null) { 1000945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y, 1001945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne bottom, fmi, needWidth || mlimit < measureLimit); 1002945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne continue; 1003945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne } 1004945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 1005945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne if (c == null) { 10060c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x += handleText(wp, i, mlimit, i, inext, runIsRtl, c, x, top, 10070c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt y, bottom, fmi, needWidth || mlimit < measureLimit); 10080c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt } else { 10090c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt for (int j = i, jnext; j < mlimit; j = jnext) { 1010c1f44830809f0a8526855f13822702ea756214faGilles Debunne jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) - 1011945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne mStart; 1012e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 10130c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt wp.set(mPaint); 1014c1f44830809f0a8526855f13822702ea756214faGilles Debunne for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) { 1015945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne // Intentionally using >= and <= as explained above 1016c1f44830809f0a8526855f13822702ea756214faGilles Debunne if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) || 1017c1f44830809f0a8526855f13822702ea756214faGilles Debunne (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue; 1018945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne 1019c1f44830809f0a8526855f13822702ea756214faGilles Debunne CharacterStyle span = mCharacterStyleSpanSet.spans[k]; 10200c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt span.updateDrawState(wp); 1021e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 10220c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt 10230c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x, 10240c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt top, y, bottom, fmi, needWidth || jnext < measureLimit); 1025e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1026e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1027e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1028e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1029945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne return x - originalX; 1030e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1031e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1032e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 1033e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Render a text run with the set-up paint. 1034e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 1035e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param c the canvas 1036e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param wp the paint used to render the text 10370c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param start the start of the run 10380c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param end the end of the run 10390c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param contextStart the start of context for the run 10400c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt * @param contextEnd the end of the context for the run 1041e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param runIsRtl true if the run is right-to-left 1042e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param x the x position of the left edge of the run 1043e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param y the baseline of the run 1044e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 10450c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt private void drawTextRun(Canvas c, TextPaint wp, int start, int end, 10460c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextStart, int contextEnd, boolean runIsRtl, float x, int y) { 1047e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1048f47d7405bbcb25d7cdf89ebb059f41520fe9ab87Doug Felt int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR; 1049e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mCharsValid) { 10500c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int count = end - start; 10510c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int contextCount = contextEnd - contextStart; 10520c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt c.drawTextRun(mChars, start, count, contextStart, contextCount, 10530c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt x, y, flags, wp); 1054e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } else { 10550c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt int delta = mStart; 10560c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt c.drawTextRun(mText, delta + start, delta + end, 10570c702b88c5d0d4380930b920f5be6e66dd95a0d8Doug Felt delta + contextStart, delta + contextEnd, x, y, flags, wp); 1058e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1059e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1060e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1061e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 1062e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the ascent of the text at start. This is used for scaling 1063e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * emoji. 1064e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 1065e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param pos the line-relative position 1066e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the ascent of the text at start 1067e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 1068e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float ascent(int pos) { 1069e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (mSpanned == null) { 1070e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return mPaint.ascent(); 1071e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1072e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1073e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt pos += mStart; 1074945ee9b1661e60e0074d4f16f61fc147c728c6bfGilles Debunne MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1, MetricAffectingSpan.class); 1075e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt if (spans.length == 0) { 1076e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return mPaint.ascent(); 1077e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1078e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1079e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt TextPaint wp = mWorkPaint; 1080e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt wp.set(mPaint); 1081e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt for (MetricAffectingSpan span : spans) { 1082e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt span.updateMeasureState(wp); 1083e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1084e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt return wp.ascent(); 1085e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1086e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1087e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt /** 1088e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * Returns the next tab position. 1089e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * 1090e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @param h the (unsigned) offset from the leading margin 1091e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt * @return the (unsigned) tab position after this offset 1092e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt */ 1093e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt float nextTab(float h) { 1094c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt if (mTabs != null) { 1095c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return mTabs.nextTab(h); 1096e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1097c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt return TabStops.nextDefaultStop(h, TAB_INCREMENT); 1098e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt } 1099e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt 1100e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt private static final int TAB_INCREMENT = 20; 1101e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt} 1102