StaticLayout.java revision 4c02e831728daf9374b52e2fe3fdbf7ce982bfc4
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.graphics.Bitmap;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Paint;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.LeadingMarginSpan;
226611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunneimport android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.LineHeightSpan;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.MetricAffectingSpan;
25c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Feltimport android.text.style.TabStopSpan;
268059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglioimport android.util.Log;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
28cb379120456d8065d742021fc5c66748fc8a11a8Doug Feltimport com.android.internal.util.ArrayUtils;
29776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinskiimport com.android.internal.util.GrowingArrayUtils;
30cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
31c8f9e6218681640d5f384c12edf06619be56a583Anish Athalyeimport java.util.Arrays;
32c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * StaticLayout is a Layout for text that will not be edited after it
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * is laid out.  Use {@link DynamicLayout} for text that may change.
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>This is used by widgets to control text layout. You should not need
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * to use this class directly unless you are implementing your own widget
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * or custom display object, or would be tempted to call
394e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int,
404e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * float, float, android.graphics.Paint)
414e0c5e55e171532760d5f51e0165563827129d4eDoug Felt * Canvas.drawText()} directly.</p>
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
43121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Megliopublic class StaticLayout extends Layout {
44121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio
458059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    static final String TAG = "StaticLayout";
468059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, TextPaint paint,
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int width,
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Alignment align, float spacingmult, float spacingadd,
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad) {
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(source, 0, source.length(), paint, width, align,
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             spacingmult, spacingadd, includepad);
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
55cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
56cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
57cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
58cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, TextPaint paint,
59cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            int width, Alignment align, TextDirectionHeuristic textDir,
60cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
61cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad) {
62cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, 0, source.length(), paint, width, align, textDir,
63cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                spacingmult, spacingadd, includepad);
64cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    }
65cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, int bufstart, int bufend,
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        TextPaint paint, int outerwidth,
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Alignment align,
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        float spacingmult, float spacingadd,
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad) {
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(source, bufstart, bufend, paint, outerwidth, align,
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             spacingmult, spacingadd, includepad, null, 0);
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
75cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
76cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
77cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
78cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, int bufstart, int bufend,
79cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextPaint paint, int outerwidth,
80cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            Alignment align, TextDirectionHeuristic textDir,
81cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
82cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad) {
83cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, bufstart, bufend, paint, outerwidth, align, textDir,
848059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                spacingmult, spacingadd, includepad, null, 0, Integer.MAX_VALUE);
85cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt}
86cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
87cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    public StaticLayout(CharSequence source, int bufstart, int bufend,
88cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextPaint paint, int outerwidth,
89cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            Alignment align,
90cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            float spacingmult, float spacingadd,
91cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            boolean includepad,
92cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
93cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt        this(source, bufstart, bufend, paint, outerwidth, align,
94cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                TextDirectionHeuristics.FIRSTSTRONG_LTR,
958059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE);
96cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    }
97cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
98cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt    /**
99cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     * @hide
100cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt     */
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StaticLayout(CharSequence source, int bufstart, int bufend,
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        TextPaint paint, int outerwidth,
103cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt                        Alignment align, TextDirectionHeuristic textDir,
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        float spacingmult, float spacingadd,
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        boolean includepad,
1068059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super((ellipsize == null)
1084e0c5e55e171532760d5f51e0165563827129d4eDoug Felt                ? source
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                : (source instanceof Spanned)
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? new SpannedEllipsizer(source)
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : new Ellipsizer(source),
112cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt              paint, outerwidth, align, textDir, spacingmult, spacingadd);
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /*
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This is annoying, but we can't refer to the layout until
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * superclass construction is finished, and the superclass
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * constructor wants the reference to the display text.
1184e0c5e55e171532760d5f51e0165563827129d4eDoug Felt         *
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This will break if the superclass constructor ever actually
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * cares about the content instead of just holding the reference.
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (ellipsize != null) {
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Ellipsizer e = (Ellipsizer) getText();
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mLayout = this;
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mWidth = ellipsizedWidth;
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.mMethod = ellipsize;
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mEllipsizedWidth = ellipsizedWidth;
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mColumns = COLUMNS_ELLIPSIZE;
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mColumns = COLUMNS_NORMAL;
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mEllipsizedWidth = outerwidth;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
136776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
137776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLines = new int[mLineDirections.length];
1388059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        mMaximumVisibleLineCount = maxLines;
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
140e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mMeasured = MeasuredText.obtain();
141e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt
142d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne        generate(source, bufstart, bufend, paint, outerwidth, textDir, spacingmult,
143d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                 spacingadd, includepad, includepad, ellipsizedWidth,
144d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                 ellipsize);
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
146e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mMeasured = MeasuredText.recycle(mMeasured);
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mFontMetricsInt = null;
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1508059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    /* package */ StaticLayout(CharSequence text) {
1518059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        super(text, null, 0, null, 0, 0);
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mColumns = COLUMNS_ELLIPSIZE;
154776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
155776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski        mLines = new int[mLineDirections.length];
15681541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne        // FIXME This is never recycled
157e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        mMeasured = MeasuredText.obtain();
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
160121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    /* package */ void generate(CharSequence source, int bufStart, int bufEnd,
161121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        TextPaint paint, int outerWidth,
162d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                        TextDirectionHeuristic textDir, float spacingmult,
163d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                        float spacingadd, boolean includepad,
164d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                        boolean trackpad, float ellipsizedWidth,
165d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                        TextUtils.TruncateAt ellipsize) {
166c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        LineBreaks lineBreaks = new LineBreaks();
167c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // store span end locations
168c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        int[] spanEndCache = new int[4];
169c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // store fontMetrics per span range
170c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
171c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        int[] fmCache = new int[4 * 4];
17288b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye        final String localeLanguageTag = paint.getTextLocale().toLanguageTag();
17388b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLineCount = 0;
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int v = 0;
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Paint.FontMetricsInt fm = mFontMetricsInt;
180121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        int[] chooseHtv = null;
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
182e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        MeasuredText measured = mMeasured;
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Spanned spanned = null;
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof Spanned)
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            spanned = (Spanned) source;
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
188e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt        int paraEnd;
189121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
190121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
191e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            if (paraEnd < 0)
192121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                paraEnd = bufEnd;
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
194e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                paraEnd++;
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
196c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int firstWidthLineCount = 1;
197121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            int firstWidth = outerWidth;
198121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            int restWidth = outerWidth;
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
200121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            LineHeightSpan[] chooseHt = null;
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (spanned != null) {
20374d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0Eric Fischer                LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
204e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        LeadingMarginSpan.class);
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < sp.length; i++) {
2067b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    LeadingMarginSpan lms = sp[i];
207121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    firstWidth -= sp[i].getLeadingMargin(true);
208121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    restWidth -= sp[i].getLeadingMargin(false);
209cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt
210c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    // LeadingMarginSpan2 is odd.  The count affects all
211ab08c6d38ab2e575f809ca8ce4c7f095e49d258cAnish Athalye                    // leading margin spans, not just this particular one
212c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                    if (lms instanceof LeadingMarginSpan2) {
213c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                        LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
214c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        firstWidthLineCount = Math.max(firstWidthLineCount,
215c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                                lms2.getLeadingMarginLineCount());
2167b5676e4d40a09ccdbc8b6f691a3d8be23e480d3Mark Wagner                    }
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
219121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
221121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (chooseHt.length != 0) {
222121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    if (chooseHtv == null ||
223121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        chooseHtv.length < chooseHt.length) {
224776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                        chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
227121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    for (int i = 0; i < chooseHt.length; i++) {
228121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        int o = spanned.getSpanStart(chooseHt[i]);
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
230e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt                        if (o < paraStart) {
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // starts in this layout, before the
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // current paragraph
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
234121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                            chooseHtv[i] = getLineTop(getLineForOffset(o));
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else {
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            // starts in this paragraph
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
238121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                            chooseHtv[i] = v;
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
244cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt            measured.setPara(source, paraStart, paraEnd, textDir);
245e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            char[] chs = measured.mChars;
246e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            float[] widths = measured.mWidths;
247e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            byte[] chdirs = measured.mLevels;
248e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            int dir = measured.mDir;
249e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt            boolean easy = measured.mEasy;
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
251c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // measurement has to be done before performing line breaking
252c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // but we don't want to recompute fontmetrics or span ranges the
253c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // second time, so we cache those and then use those stored values
254c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmCacheCount = 0;
255c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int spanEndCacheCount = 0;
256cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne            for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
257c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fmCacheCount * 4 >= fmCache.length) {
258c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int[] grow = new int[fmCacheCount * 4 * 2];
259c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
260c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmCache = grow;
261c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
262c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
263c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (spanEndCacheCount >= spanEndCache.length) {
264c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int[] grow = new int[spanEndCacheCount * 2];
265c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
266c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    spanEndCache = grow;
267c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
26823241887515ed77687c23e29a4a3ffff671666bdDoug Felt
269cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                if (spanned == null) {
270cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spanEnd = paraEnd;
27123241887515ed77687c23e29a4a3ffff671666bdDoug Felt                    int spanLen = spanEnd - spanStart;
272cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    measured.addStyleRun(paint, spanLen, fm);
273cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                } else {
274cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
275cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                            MetricAffectingSpan.class);
276cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    int spanLen = spanEnd - spanStart;
277cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    MetricAffectingSpan[] spans =
27823241887515ed77687c23e29a4a3ffff671666bdDoug Felt                            spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
279cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
280cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne                    measured.addStyleRun(paint, spans, spanLen, fm);
28123241887515ed77687c23e29a4a3ffff671666bdDoug Felt                }
28223241887515ed77687c23e29a4a3ffff671666bdDoug Felt
283c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // the order of storage here (top, bottom, ascent, descent) has to match the code below
284c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // where these values are retrieved
285c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 0] = fm.top;
286c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 1] = fm.bottom;
287c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 2] = fm.ascent;
288c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCache[fmCacheCount * 4 + 3] = fm.descent;
289c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCacheCount++;
290c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
291c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEndCache[spanEndCacheCount] = spanEnd;
292c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEndCacheCount++;
293c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            }
294c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
295c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // tab stop locations
296c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int[] variableTabStops = null;
297c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            if (spanned != null) {
298c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
299c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        paraEnd, TabStopSpan.class);
300c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (spans.length > 0) {
301c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int[] stops = new int[spans.length];
302c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    for (int i = 0; i < spans.length; i++) {
303c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        stops[i] = spans[i].getTabStop();
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
305c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    Arrays.sort(stops, 0, stops.length);
306c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    variableTabStops = stops;
307c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
308c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            }
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
310c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int breakCount = nComputeLineBreaks(localeLanguageTag, chs, widths, paraEnd - paraStart, firstWidth,
311969e7b949633baebd3f6090a60d622ee475a5b22Anish Athalye                    firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, false, lineBreaks,
312c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
31381541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne
314c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int[] breaks = lineBreaks.breaks;
315c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            float[] lineWidths = lineBreaks.widths;
316c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            boolean[] flags = lineBreaks.flags;
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
318c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            // here is the offset of the starting character of the line we are currently measuring
319c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int here = paraStart;
320d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne
321c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
322c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int fmCacheIndex = 0;
323c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int spanEndCacheIndex = 0;
324c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int breakIndex = 0;
325c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
326c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // retrieve end of span
327c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                spanEnd = spanEndCache[spanEndCacheIndex++];
328c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
329c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // retrieve cached metrics, order matches above
330c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.top = fmCache[fmCacheIndex * 4 + 0];
331c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.bottom = fmCache[fmCacheIndex * 4 + 1];
332c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.ascent = fmCache[fmCacheIndex * 4 + 2];
333c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fm.descent = fmCache[fmCacheIndex * 4 + 3];
334c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                fmCacheIndex++;
335c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
336c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.top < fmTop) {
337c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmTop = fm.top;
338c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
339c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.ascent < fmAscent) {
340c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmAscent = fm.ascent;
341c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
342c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.descent > fmDescent) {
343c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmDescent = fm.descent;
344c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
345c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                if (fm.bottom > fmBottom) {
346c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    fmBottom = fm.bottom;
347c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
348cd943a7a013952af9b7286fd506fd63bf0993ac1Gilles Debunne
349c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                // skip breaks ending before current span range
350c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
351c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    breakIndex++;
352c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                }
35381541491946bfc4f2e26c171b4ebff4249dca51cGilles Debunne
354c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
355c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    int endPos = paraStart + breaks[breakIndex];
356c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
3574c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien                    boolean moreChars = (endPos < paraEnd); // XXX is this the right way to calculate this?
3584c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien
359c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    v = out(source, here, endPos,
360c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            fmAscent, fmDescent, fmTop, fmBottom,
361c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex],
362c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
363c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                            chs, widths, paraStart, ellipsize, ellipsizedWidth,
3644c02e831728daf9374b52e2fe3fdbf7ce982bfc4Raph Levien                            lineWidths[breakIndex], paint, moreChars);
365c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
366c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    if (endPos < spanEnd) {
367c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        // preserve metrics for current span
368c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmTop = fm.top;
369c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmBottom = fm.bottom;
370c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmAscent = fm.ascent;
371c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmDescent = fm.descent;
372c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    } else {
373c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        fmTop = fmBottom = fmAscent = fmDescent = 0;
3748059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
376c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    here = endPos;
377c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    breakIndex++;
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
379c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    if (mLineCount >= mMaximumVisibleLineCount) {
380c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                        return;
381c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye                    }
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
385121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (paraEnd == bufEnd)
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3898059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) &&
390ad0b051b133baf92f199c96a8ac1e81b3393190cFabrice Di Meglio                mLineCount < mMaximumVisibleLineCount) {
391121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            // Log.e("text", "output last " + bufEnd);
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
39346514a75e33e87d3461a502a5a0821c46b2783ebAnish Athalye            measured.setPara(source, bufEnd, bufEnd, textDir);
394e631889e1ae7edc6a2fae495ba504f85820b6a4bFabrice Di Meglio
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            paint.getFontMetricsInt(fm);
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            v = out(source,
398121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    bufEnd, bufEnd, fm.ascent, fm.descent,
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fm.top, fm.bottom,
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    v,
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    spacingmult, spacingadd, null,
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    null, fm, false,
403e631889e1ae7edc6a2fae495ba504f85820b6a4bFabrice Di Meglio                    needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
404d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    includepad, trackpad, null,
405d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    null, bufStart, ellipsize,
406d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                    ellipsizedWidth, 0, paint, false);
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int out(CharSequence text, int start, int end,
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                      int above, int below, int top, int bottom, int v,
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                      float spacingmult, float spacingadd,
413121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                      LineHeightSpan[] chooseHt, int[] chooseHtv,
414c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt                      Paint.FontMetricsInt fm, boolean hasTabOrEmoji,
415d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean needMultiply, byte[] chdirs, int dir,
416d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean easy, int bufEnd, boolean includePad,
417d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      boolean trackPad, char[] chs,
418d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      float[] widths, int widthStart, TextUtils.TruncateAt ellipsize,
419d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      float ellipsisWidth, float textWidth,
420d300e75eff0d5e54390400cbd3f80dc4cea8b617Gilles Debunne                      TextPaint paint, boolean moreChars) {
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int j = mLineCount;
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int off = j * mColumns;
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int want = off + mColumns + TOP;
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] lines = mLines;
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (want >= lines.length) {
427776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            Directions[] grow2 = ArrayUtils.newUnpaddedArray(
428776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski                    Directions.class, GrowingArrayUtils.growSize(want));
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(mLineDirections, 0, grow2, 0,
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                             mLineDirections.length);
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLineDirections = grow2;
432776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski
433776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            int[] grow = new int[grow2.length];
434776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            System.arraycopy(lines, 0, grow, 0, lines.length);
435776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            mLines = grow;
436776abc24cdd18610232a50b997cce3cffa74609bAdam Lesinski            lines = grow;
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
439121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        if (chooseHt != null) {
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.ascent = above;
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.descent = below;
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.top = top;
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fm.bottom = bottom;
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
445121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            for (int i = 0; i < chooseHt.length; i++) {
446121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (chooseHt[i] instanceof LineHeightSpan.WithDensity) {
447121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    ((LineHeightSpan.WithDensity) chooseHt[i]).
448121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                        chooseHeight(text, start, end, chooseHtv[i], v, fm, paint);
449a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer
450a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                } else {
451121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                    chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm);
452a9f1dd021f8f6ee777bc4d27913bd40c42e753afEric Fischer                }
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            above = fm.ascent;
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            below = fm.descent;
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            top = fm.top;
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            bottom = fm.bottom;
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
461d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean firstLine = (j == 0);
462d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
463d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd);
464d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
465d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (firstLine) {
466121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (trackPad) {
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mTopPadding = top - above;
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
470121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (includePad) {
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                above = top;
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
474d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
475d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        int extra;
476d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien
477d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (lastLine) {
478121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (trackPad) {
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mBottomPadding = bottom - below;
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
482121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio            if (includePad) {
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                below = bottom;
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
488d97b097a5b7a906e8d30c6d3b0f41c55650ce7a9Raph Levien        if (needMultiply && !lastLine) {
4891065758a0f8966a8597a61492112f7859a7050a4Doug Felt            double ex = (below - above) * (spacingmult - 1) + spacingadd;
4901065758a0f8966a8597a61492112f7859a7050a4Doug Felt            if (ex >= 0) {
491121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                extra = (int)(ex + EXTRA_ROUNDING);
4921065758a0f8966a8597a61492112f7859a7050a4Doug Felt            } else {
493121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                extra = -(int)(-ex + EXTRA_ROUNDING);
4941065758a0f8966a8597a61492112f7859a7050a4Doug Felt            }
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            extra = 0;
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + START] = start;
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + TOP] = v;
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + DESCENT] = below + extra;
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        v += (below - above) + extra;
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + mColumns + START] = end;
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        lines[off + mColumns + TOP] = v;
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
507c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt        if (hasTabOrEmoji)
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            lines[off + TAB] |= TAB_MASK;
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5109f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        lines[off + DIR] |= dir << DIR_SHIFT;
5119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
5129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // easy means all chars < the first RTL, so no emoji, no nothing
5134e0c5e55e171532760d5f51e0165563827129d4eDoug Felt        // XXX a run with no text or all spaces is easy but might be an empty
5149f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // RTL paragraph.  Make sure easy is false if this is the case.
5159f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (easy) {
5169f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            mLineDirections[j] = linedirs;
5179f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        } else {
518f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne            mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
519f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne                    start - widthStart, end - start);
5200a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        }
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
522aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio        if (ellipsize != null) {
523aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            // If there is only one line, then do any type of ellipsis except when it is MARQUEE
524aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            // if there are multiple lines, just allow END ellipsis on the last line
5258059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
526aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio
52734a126e51aaf22e32c7af808ec6b5a0c41ae3311Fabrice Di Meglio            boolean doEllipsis =
52834a126e51aaf22e32c7af808ec6b5a0c41ae3311Fabrice Di Meglio                        (((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
529aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                                ellipsize != TextUtils.TruncateAt.MARQUEE) ||
530aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
531aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                                ellipsize == TextUtils.TruncateAt.END);
532aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            if (doEllipsis) {
533aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                calculateEllipsis(start, end, widths, widthStart,
534aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        ellipsisWidth, ellipsize, j,
535aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                        textWidth, paint, forceEllipsis);
536aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            }
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLineCount++;
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return v;
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
543121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private void calculateEllipsis(int lineStart, int lineEnd,
544121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                                   float[] widths, int widthStart,
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                   float avail, TextUtils.TruncateAt where,
5468059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                                   int line, float textWidth, TextPaint paint,
5478059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                                   boolean forceEllipsis) {
5488059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        if (textWidth <= avail && !forceEllipsis) {
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Everything fits!
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLines[mColumns * line + ELLIPSIS_START] = 0;
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLines[mColumns * line + ELLIPSIS_COUNT] = 0;
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
555cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio        float ellipsisWidth = paint.measureText(
5568d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio                (where == TextUtils.TruncateAt.END_SMALL) ?
5578d44fff7e62f77c3b3072a96712cc1389e63ca64Fabrice Di Meglio                        ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL, 0, 1);
5588059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        int ellipsisStart = 0;
5598059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        int ellipsisCount = 0;
560121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio        int len = lineEnd - lineStart;
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5628059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        // We only support start ellipsis on a single line
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (where == TextUtils.TruncateAt.START) {
5648059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            if (mMaximumVisibleLineCount == 1) {
5658059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float sum = 0;
5668059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                int i;
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5688059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                for (i = len; i >= 0; i--) {
5698059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[i - 1 + lineStart - widthStart];
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5718059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + sum + ellipsisWidth > avail) {
5728059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
5738059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
5748059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
5758059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    sum += w;
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5788059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisStart = 0;
5798059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = i;
5808059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            } else {
5818059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                if (Log.isLoggable(TAG, Log.WARN)) {
5828059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    Log.w(TAG, "Start Ellipsis only supported with one line");
5838059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                }
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
585cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio        } else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE ||
586cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio                where == TextUtils.TruncateAt.END_SMALL) {
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float sum = 0;
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i;
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (i = 0; i < len; i++) {
591121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                float w = widths[i + lineStart - widthStart];
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
593121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio                if (w + sum + ellipsisWidth > avail) {
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sum += w;
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ellipsisStart = i;
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ellipsisCount = len - i;
602aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio            if (forceEllipsis && ellipsisCount == 0 && len > 0) {
603aef455fd5b4c667267deb050bc7997e737b7507eFabrice Di Meglio                ellipsisStart = len - 1;
6048059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = 1;
6058059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            }
6068059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio        } else {
6078059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            // where = TextUtils.TruncateAt.MIDDLE We only support middle ellipsis on a single line
6088059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            if (mMaximumVisibleLineCount == 1) {
6098059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float lsum = 0, rsum = 0;
6108059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                int left = 0, right = len;
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6128059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float ravail = (avail - ellipsisWidth) / 2;
6130e3c5e827235911d33312e431975533f046421e7Raph Levien                for (right = len; right > 0; right--) {
6148059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[right - 1 + lineStart - widthStart];
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6168059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + rsum > ravail) {
6178059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
6188059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
6198059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio
6208059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    rsum += w;
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6238059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                float lavail = avail - ellipsisWidth - rsum;
6248059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                for (left = 0; left < right; left++) {
6258059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    float w = widths[left + lineStart - widthStart];
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6278059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    if (w + lsum > lavail) {
6288059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                        break;
6298059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    }
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6318059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    lsum += w;
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6348059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisStart = left;
6358059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                ellipsisCount = right - left;
6368059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio            } else {
6378059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                if (Log.isLoggable(TAG, Log.WARN)) {
6388059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                    Log.w(TAG, "Middle Ellipsis only supported with one line");
6398059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio                }
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart;
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
647e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    // Override the base class so we can directly access our members,
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // rather than relying on member functions.
6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The logic mirrors that of Layout.getLineForVertical
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // FIXME: It may be faster to do a linear search for layouts without many lines.
6516611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineForVertical(int vertical) {
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int high = mLineCount;
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int low = -1;
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int guess;
6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int[] lines = mLines;
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (high - low > 1) {
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            guess = (high + low) >> 1;
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (lines[mColumns * guess + TOP] > vertical){
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                high = guess;
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                low = guess;
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (low < 0) {
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return low;
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6726611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineCount() {
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLineCount;
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6776611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineTop(int line) {
6790a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        int top = mLines[mColumns * line + TOP];
6800a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount &&
6810a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne                line != mLineCount) {
6820a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne            top += getBottomPadding();
6830a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        }
6840a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        return top;
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6876611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineDescent(int line) {
6890a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        int descent = mLines[mColumns * line + DESCENT];
690f3fa0cdbaea109b114f7facbb5d42de3fc12bbc8Gilles Debunne        if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount - 1 && // -1 intended
6910a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne                line != mLineCount) {
6920a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne            descent += getBottomPadding();
6930a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        }
6940a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne        return descent;
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6976611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getLineStart(int line) {
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + START] & START_MASK;
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7026611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getParagraphDirection(int line) {
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + DIR] >> DIR_SHIFT;
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7076611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean getLineContainsTab(int line) {
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return (mLines[mColumns * line + TAB] & TAB_MASK) != 0;
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7126611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final Directions getLineDirections(int line) {
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLineDirections[line];
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7176611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getTopPadding() {
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mTopPadding;
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7226611147383118cd91cc29b31bff9aaf4c853f39dGilles Debunne    @Override
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getBottomPadding() {
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mBottomPadding;
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsisCount(int line) {
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mColumns < COLUMNS_ELLIPSIZE) {
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + ELLIPSIS_COUNT];
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsisStart(int line) {
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mColumns < COLUMNS_ELLIPSIZE) {
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mLines[mColumns * line + ELLIPSIS_START];
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getEllipsizedWidth() {
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mEllipsizedWidth;
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
750e5ea4403ce58982522554b7ff23f41e6551923c1Romain Guy    void prepare() {
751e5ea4403ce58982522554b7ff23f41e6551923c1Romain Guy        mMeasured = MeasuredText.obtain();
752e5ea4403ce58982522554b7ff23f41e6551923c1Romain Guy    }
753e5ea4403ce58982522554b7ff23f41e6551923c1Romain Guy
754e5ea4403ce58982522554b7ff23f41e6551923c1Romain Guy    void finish() {
755e5ea4403ce58982522554b7ff23f41e6551923c1Romain Guy        mMeasured = MeasuredText.recycle(mMeasured);
756e5ea4403ce58982522554b7ff23f41e6551923c1Romain Guy    }
7570a4db3c5270440eeb7e4e44a7029926e239ec3bdGilles Debunne
758c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // populates LineBreaks and returns the number of breaks found
759c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    //
760c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // the arrays inside the LineBreaks objects are passed in as well
761c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // to reduce the number of JNI calls in the common case where the
762c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // arrays do not have to be resized
763c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    private static native int nComputeLineBreaks(String locale, char[] text, float[] widths,
764c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int length, float firstWidth, int firstWidthLineCount, float restWidth,
765969e7b949633baebd3f6090a60d622ee475a5b22Anish Athalye            int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle,
766c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye            int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength);
76788b5b0be887fc5dc3b0b879b4179dde200d2e4d6Anish Athalye
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mLineCount;
7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mTopPadding, mBottomPadding;
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mColumns;
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mEllipsizedWidth;
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int COLUMNS_NORMAL = 3;
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int COLUMNS_ELLIPSIZE = 5;
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int START = 0;
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DIR = START;
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAB = START;
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TOP = 1;
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DESCENT = 2;
7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ELLIPSIS_START = 3;
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ELLIPSIS_COUNT = 4;
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int[] mLines;
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Directions[] mLineDirections;
7858059e0903e36cbb5cf8b5c5d5d653acc9bbc8402Fabrice Di Meglio    private int mMaximumVisibleLineCount = Integer.MAX_VALUE;
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int START_MASK = 0x1FFFFFFF;
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DIR_SHIFT  = 30;
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TAB_MASK   = 0x20000000;
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
791c982f60e982c1d2df9f115ed9a5c3ef3643d0892Doug Felt    private static final int TAB_INCREMENT = 20; // same as Layout, but that's private
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
793121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private static final char CHAR_NEW_LINE = '\n';
794121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio
795121c82c8130c2658f73fb19f3a62eb88c8679968Fabrice Di Meglio    private static final double EXTRA_ROUNDING = 0.5;
796cb332649e44db86ff8b4e7f006db4bbfd82fed55Fabrice Di Meglio
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /*
798e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt     * This is reused across calls to generate()
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
800e8e45f2c05cb3b6d23f30c8f96d8e0b3699cea7aDoug Felt    private MeasuredText mMeasured;
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
802c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
803c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // This is used to return three arrays from a single JNI call when
804c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    // performing line breaking
8057053919a3a55ad1b58e6db701afda18850037732Deepanshu Gupta    /*package*/ static class LineBreaks {
806c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        private static final int INITIAL_SIZE = 16;
807c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        public int[] breaks = new int[INITIAL_SIZE];
808c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        public float[] widths = new float[INITIAL_SIZE];
809c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        public boolean[] flags = new boolean[INITIAL_SIZE]; // hasTabOrEmoji
810c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye        // breaks, widths, and flags should all have the same length
811c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye    }
812c8f9e6218681640d5f384c12edf06619be56a583Anish Athalye
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
814