MeasuredParagraph.java revision 9d3bd08ebab564ed9231c8ee112e8085cda74ce8
19d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka/*
29d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * Copyright (C) 2010 The Android Open Source Project
39d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
49d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * Licensed under the Apache License, Version 2.0 (the "License");
59d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * you may not use this file except in compliance with the License.
69d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * You may obtain a copy of the License at
79d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
89d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *      http://www.apache.org/licenses/LICENSE-2.0
99d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * Unless required by applicable law or agreed to in writing, software
119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * distributed under the License is distributed on an "AS IS" BASIS,
129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * See the License for the specific language governing permissions and
149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * limitations under the License.
159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka */
169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakapackage android.text;
189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.annotation.FloatRange;
209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.annotation.IntRange;
219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.annotation.NonNull;
229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.annotation.Nullable;
239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.graphics.Paint;
249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.AutoGrowArray.ByteArray;
259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.AutoGrowArray.FloatArray;
269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.AutoGrowArray.IntArray;
279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.Layout.Directions;
289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.style.MetricAffectingSpan;
299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.style.ReplacementSpan;
309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.util.Pools.SynchronizedPool;
319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport dalvik.annotation.optimization.CriticalNative;
339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport libcore.util.NativeAllocationRegistry;
359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport java.util.Arrays;
379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka/**
399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * MeasuredParagraph provides text information for rendering purpose.
409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * The first motivation of this class is identify the text directions and retrieving individual
429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * character widths. However retrieving character widths is slower than identifying text directions.
439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * Thus, this class provides several builder methods for specific purposes.
449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * - buildForBidi:
469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   Compute only text directions.
479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * - buildForMeasurement:
489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   Compute text direction and all character widths.
499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * - buildForStaticLayout:
509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   This is bit special. StaticLayout also needs to know text direction and character widths for
519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   line breaking, but all things are done in native code. Similarly, text measurement is done
529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   in native code. So instead of storing result to Java array, this keeps the result in native
539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   code since there is no good reason to move the results to Java layer.
549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * In addition to the character widths, some additional information is computed for each purposes,
569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * e.g. whole text length for measurement or font metrics for static layout.
579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * MeasuredParagraph is NOT a thread safe object.
599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * @hide
609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka */
619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakapublic class MeasuredParagraph {
629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            MeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private MeasuredParagraph() {}  // Use build static functions instead.
689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static final SynchronizedPool<MeasuredParagraph> sPool = new SynchronizedPool<>(1);
709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static @NonNull MeasuredParagraph obtain() { // Use build static functions instead.
729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = sPool.acquire();
739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt != null ? mt : new MeasuredParagraph();
749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Recycle the MeasuredParagraph.
789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Do not call any methods after you call this method.
809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public void recycle() {
829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        release();
839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        sPool.release(this);
849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The casted original text.
879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    //
889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // This may be null if the passed text is not a Spanned.
899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable Spanned mSpanned;
909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The start offset of the target range in the original text (mSpanned);
929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @IntRange(from = 0) int mTextStart;
939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The length of the target range in the original text.
959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @IntRange(from = 0) int mTextLength;
969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The copied character buffer for measuring text.
989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    //
999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The length of this array is mTextLength.
1009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable char[] mCopiedBuffer;
1019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The whole paragraph direction.
1039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Layout.Direction int mParaDir;
1049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // True if the text is LTR direction and doesn't contain any bidi characters.
1069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private boolean mLtrWithoutBidi;
1079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The bidi level for individual characters.
1099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    //
1109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // This is empty if mLtrWithoutBidi is true.
1119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @NonNull ByteArray mLevels = new ByteArray();
1129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The whole width of the text.
1149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // See getWholeWidth comments.
1159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @FloatRange(from = 0.0f) float mWholeWidth;
1169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // Individual characters' widths.
1189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // See getWidths comments.
1199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable FloatArray mWidths = new FloatArray();
1209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The span end positions.
1229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // See getSpanEndCache comments.
1239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable IntArray mSpanEndCache = new IntArray(4);
1249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The font metrics.
1269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // See getFontMetrics comments.
1279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
1289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The native MeasuredParagraph.
1309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // See getNativePtr comments.
1319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
1329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private /* Maybe Zero */ long mNativePtr = 0;
1339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable Runnable mNativeObjectCleaner;
1349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // Associate the native object to this Java object.
1369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void bindNativeObject(/* Non Zero*/ long nativePtr) {
1379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mNativePtr = nativePtr;
1389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
1399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // Decouple the native object from this Java object and release the native object.
1429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void unbindNativeObject() {
1439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mNativePtr != 0) {
1449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mNativeObjectCleaner.run();
1459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mNativePtr = 0;
1469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
1479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // Following two objects are for avoiding object allocation.
1509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @NonNull TextPaint mCachedPaint = new TextPaint();
1519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable Paint.FontMetricsInt mCachedFm;
1529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
1549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Releases internal buffers.
1559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
1569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public void release() {
1579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        reset();
1589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mLevels.clearWithReleasingLargeArray();
1599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mWidths.clearWithReleasingLargeArray();
1609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mFontMetrics.clearWithReleasingLargeArray();
1619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mSpanEndCache.clearWithReleasingLargeArray();
1629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
1659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Resets the internal state for starting new text.
1669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
1679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void reset() {
1689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mSpanned = null;
1699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mCopiedBuffer = null;
1709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mWholeWidth = 0;
1719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mLevels.clear();
1729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mWidths.clear();
1739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mFontMetrics.clear();
1749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mSpanEndCache.clear();
1759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        unbindNativeObject();
1769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
1799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the characters to be measured.
1809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
1819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is always available.
1829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
1839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull char[] getChars() {
1849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mCopiedBuffer;
1859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
1889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the paragraph direction.
1899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
1909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is always available.
1919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
1929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @Layout.Direction int getParagraphDir() {
1939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mParaDir;
1949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
1979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the directions.
1989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
1999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is always available.
2009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public Directions getDirections(@IntRange(from = 0) int start,  // inclusive
2029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                    @IntRange(from = 0) int end) {  // exclusive
2039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mLtrWithoutBidi) {
2049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return Layout.DIRS_ALL_LEFT_TO_RIGHT;
2059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
2069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final int length = end - start;
2089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start,
2099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                length);
2109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the whole text width.
2149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is available only if the MeasureText is computed with computeForMeasurement.
2169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns 0 in other cases.
2179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @FloatRange(from = 0.0f) float getWholeWidth() {
2199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mWholeWidth;
2209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the individual character's width.
2249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is available only if the MeasureText is computed with computeForMeasurement.
2269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns empty array in other cases.
2279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull FloatArray getWidths() {
2299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mWidths;
2309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the MetricsAffectingSpan end indices.
2349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If the input text is not a spanned string, this has one value that is the length of the text.
2369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is available only if the MeasureText is computed with computeForStaticLayout.
2389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns empty array in other cases.
2399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull IntArray getSpanEndCache() {
2419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mSpanEndCache;
2429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the int array which holds FontMetrics.
2469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This array holds the repeat of top, bottom, ascent, descent of font metrics value.
2489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is available only if the MeasureText is computed with computeForStaticLayout.
2509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns empty array in other cases.
2519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull IntArray getFontMetrics() {
2539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mFontMetrics;
2549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the native ptr of the MeasuredParagraph.
2589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is available only if the MeasureText is computed with computeForStaticLayout.
2609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns 0 in other cases.
2619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public /* Maybe Zero */ long getNativePtr() {
2639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mNativePtr;
2649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Generates new MeasuredParagraph for Bidi computation.
2689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
2709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * result to recycle and returns recycle.
2719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
2739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
2749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
2759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
2769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param recycle pass existing MeasuredParagraph if you want to recycle it.
2779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @return measured text
2799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text,
2819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @IntRange(from = 0) int start,
2829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @IntRange(from = 0) int end,
2839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @NonNull TextDirectionHeuristic textDir,
2849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @Nullable MeasuredParagraph recycle) {
2859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
2869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.resetAndAnalyzeBidi(text, start, end, textDir);
2879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt;
2889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Generates new MeasuredParagraph for measuring texts.
2929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
2949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * result to recycle and returns recycle.
2959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paint the paint to be used for rendering the text.
2979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
2989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
2999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
3009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
3019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param recycle pass existing MeasuredParagraph if you want to recycle it.
3029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @return measured text
3049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
3059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint,
3069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @NonNull CharSequence text,
3079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @IntRange(from = 0) int start,
3089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @IntRange(from = 0) int end,
3099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @NonNull TextDirectionHeuristic textDir,
3109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @Nullable MeasuredParagraph recycle) {
3119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
3129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.resetAndAnalyzeBidi(text, start, end, textDir);
3139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.mWidths.resize(mt.mTextLength);
3159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mt.mTextLength == 0) {
3169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return mt;
3179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
3189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mt.mSpanned == null) {
3209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // No style change by MetricsAffectingSpan. Just measure all text.
3219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mt.applyMetricsAffectingSpan(
3229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    paint, null /* spans */, start, end, 0 /* native static layout ptr */);
3239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
3249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
3259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            int spanEnd;
3269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
3279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
3289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
3299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        MetricAffectingSpan.class);
3309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
3319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.applyMetricsAffectingSpan(
3329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
3339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
3349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
3359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt;
3369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
3379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
3399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Generates new MeasuredParagraph for StaticLayout.
3409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
3429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * result to recycle and returns recycle.
3439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paint the paint to be used for rendering the text.
3459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
3469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
3479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
3489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
3499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param recycle pass existing MeasuredParagraph if you want to recycle it.
3509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @return measured text
3529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
3539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public static @NonNull MeasuredParagraph buildForStaticLayout(
3549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull TextPaint paint,
3559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull CharSequence text,
3569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int start,
3579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int end,
3589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull TextDirectionHeuristic textDir,
3599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @Nullable MeasuredParagraph recycle) {
3609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
3619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.resetAndAnalyzeBidi(text, start, end, textDir);
3629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mt.mTextLength == 0) {
3639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Need to build empty native measured text for StaticLayout.
3649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // TODO: Stop creating empty measured text for empty lines.
3659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            long nativeBuilderPtr = nInitBuilder();
3669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            try {
3679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.bindNativeObject(
3689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer));
3699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } finally {
3709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                nFreeBuilder(nativeBuilderPtr);
3719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
3729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return mt;
3739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
3749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        long nativeBuilderPtr = nInitBuilder();
3769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        try {
3779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (mt.mSpanned == null) {
3789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // No style change by MetricsAffectingSpan. Just measure all text.
3799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
3809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.mSpanEndCache.append(end);
3819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
3829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // There may be a MetricsAffectingSpan. Split into span transitions and apply
3839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // styles.
3849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                int spanEnd;
3859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
3869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
3879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                             MetricAffectingSpan.class);
3889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
3899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                            MetricAffectingSpan.class);
3909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
3919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                       MetricAffectingSpan.class);
3929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
3939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                 nativeBuilderPtr);
3949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    mt.mSpanEndCache.append(spanEnd);
3959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                }
3969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
3979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer));
3989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } finally {
3999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            nFreeBuilder(nativeBuilderPtr);
4009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt;
4039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
4049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
4069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Reset internal state and analyzes text for bidirectional runs.
4079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
4089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
4099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
4109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
4119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
4129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
4139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void resetAndAnalyzeBidi(@NonNull CharSequence text,
4149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int start,  // inclusive
4159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int end,  // exclusive
4169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @NonNull TextDirectionHeuristic textDir) {
4179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        reset();
4189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mSpanned = text instanceof Spanned ? (Spanned) text : null;
4199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mTextStart = start;
4209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mTextLength = end - start;
4219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) {
4239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mCopiedBuffer = new char[mTextLength];
4249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        TextUtils.getChars(text, start, end, mCopiedBuffer, 0);
4269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // Replace characters associated with ReplacementSpan to U+FFFC.
4289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mSpanned != null) {
4299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class);
4309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int i = 0; i < spans.length; i++) {
4329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                int startInPara = mSpanned.getSpanStart(spans[i]) - start;
4339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                int endInPara = mSpanned.getSpanEnd(spans[i]) - start;
4349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // The span interval may be larger and must be restricted to [start, end)
4359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (startInPara < 0) startInPara = 0;
4369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (endInPara > mTextLength) endInPara = mTextLength;
4379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER);
4389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
4399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if ((textDir == TextDirectionHeuristics.LTR
4429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
4439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                || textDir == TextDirectionHeuristics.ANYRTL_LTR)
4449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                && TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
4459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLevels.clear();
4469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mParaDir = Layout.DIR_LEFT_TO_RIGHT;
4479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLtrWithoutBidi = true;
4489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
4499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            final int bidiRequest;
4509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (textDir == TextDirectionHeuristics.LTR) {
4519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_LTR;
4529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else if (textDir == TextDirectionHeuristics.RTL) {
4539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_RTL;
4549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
4559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
4569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
4579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
4589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
4599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
4609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
4619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
4629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLevels.resize(mTextLength);
4639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray());
4649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLtrWithoutBidi = false;
4659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
4679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void applyReplacementRun(@NonNull ReplacementSpan replacement,
4699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int start,  // inclusive, in copied buffer
4709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int end,  // exclusive, in copied buffer
4719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     /* Maybe Zero */ long nativeBuilderPtr) {
4729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // Use original text. Shouldn't matter.
4739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
4749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        //       backward compatibility? or Should we initialize them for getFontMetricsInt?
4759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final float width = replacement.getSize(
4769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
4779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (nativeBuilderPtr == 0) {
4789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Assigns all width to the first character. This is the same behavior as minikin.
4799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mWidths.set(start, width);
4809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (end > start + 1) {
4819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f);
4829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
4839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mWholeWidth += width;
4849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
4859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
4869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                               width);
4879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
4899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void applyStyleRun(@IntRange(from = 0) int start,  // inclusive, in copied buffer
4919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                               @IntRange(from = 0) int end,  // exclusive, in copied buffer
4929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                               /* Maybe Zero */ long nativeBuilderPtr) {
4939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (nativeBuilderPtr != 0) {
4949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mCachedPaint.getFontMetricsInt(mCachedFm);
4959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mLtrWithoutBidi) {
4989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // If the whole text is LTR direction, just apply whole region.
4999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (nativeBuilderPtr == 0) {
5009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mWholeWidth += mCachedPaint.getTextRunAdvances(
5019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
5029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        mWidths.getRawArray(), start);
5039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
5049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
5059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        false /* isRtl */);
5069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
5089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // If there is multiple bidi levels, split into individual bidi level and apply style.
5099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            byte level = mLevels.get(start);
5109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Note that the empty text or empty range won't reach this method.
5119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Safe to search from start + 1.
5129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
5139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (levelEnd == end || mLevels.get(levelEnd) != level) {  // transition point
5149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    final boolean isRtl = (level & 0x1) != 0;
5159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    if (nativeBuilderPtr == 0) {
5169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        final int levelLength = levelEnd - levelStart;
5179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        mWholeWidth += mCachedPaint.getTextRunAdvances(
5189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
5199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                isRtl, mWidths.getRawArray(), levelStart);
5209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    } else {
5219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
5229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                levelEnd, isRtl);
5239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    }
5249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    if (levelEnd == end) {
5259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        break;
5269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    }
5279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    levelStart = levelEnd;
5289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    level = mLevels.get(levelEnd);
5299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                }
5309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
5339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void applyMetricsAffectingSpan(
5359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull TextPaint paint,
5369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @Nullable MetricAffectingSpan[] spans,
5379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int start,  // inclusive, in original text buffer
5389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int end,  // exclusive, in original text buffer
5399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            /* Maybe Zero */ long nativeBuilderPtr) {
5409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mCachedPaint.set(paint);
5419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // XXX paint should not have a baseline shift, but...
5429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mCachedPaint.baselineShift = 0;
5439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final boolean needFontMetrics = nativeBuilderPtr != 0;
5459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (needFontMetrics && mCachedFm == null) {
5479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mCachedFm = new Paint.FontMetricsInt();
5489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        ReplacementSpan replacement = null;
5519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (spans != null) {
5529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int i = 0; i < spans.length; i++) {
5539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                MetricAffectingSpan span = spans[i];
5549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (span instanceof ReplacementSpan) {
5559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    // The last ReplacementSpan is effective for backward compatibility reasons.
5569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    replacement = (ReplacementSpan) span;
5579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                } else {
5589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    // TODO: No need to call updateMeasureState for ReplacementSpan as well?
5599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    span.updateMeasureState(mCachedPaint);
5609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                }
5619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final int startInCopiedBuffer = start - mTextStart;
5659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final int endInCopiedBuffer = end - mTextStart;
5669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (replacement != null) {
5689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
5699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                nativeBuilderPtr);
5709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
5719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
5729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (needFontMetrics) {
5759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (mCachedPaint.baselineShift < 0) {
5769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.ascent += mCachedPaint.baselineShift;
5779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.top += mCachedPaint.baselineShift;
5789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
5799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.descent += mCachedPaint.baselineShift;
5809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.bottom += mCachedPaint.baselineShift;
5819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.top);
5849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.bottom);
5859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.ascent);
5869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.descent);
5879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
5899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
5919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the maximum index that the accumulated width not exceeds the width.
5929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
5939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If forward=false is passed, returns the minimum index from the end instead.
5949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
5959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This only works if the MeasuredParagraph is computed with computeForMeasurement.
5969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Undefined behavior in other case.
5979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
5989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
5999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        float[] w = mWidths.getRawArray();
6009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (forwards) {
6019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            int i = 0;
6029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i < limit) {
6039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                width -= w[i];
6049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (width < 0.0f) break;
6059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                i++;
6069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--;
6089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return i;
6099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
6109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            int i = limit - 1;
6119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i >= 0) {
6129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                width -= w[i];
6139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (width < 0.0f) break;
6149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                i--;
6159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) {
6179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                i++;
6189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return limit - i - 1;
6209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
6229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
6249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the length of the substring.
6259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
6269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This only works if the MeasuredParagraph is computed with computeForMeasurement.
6279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Undefined behavior in other case.
6289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
6299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    @FloatRange(from = 0.0f) float measure(int start, int limit) {
6309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        float width = 0;
6319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        float[] w = mWidths.getRawArray();
6329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        for (int i = start; i < limit; ++i) {
6339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            width += w[i];
6349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return width;
6369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
6379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native /* Non Zero */ long nInitBuilder();
6399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
6419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Apply style to make native measured text.
6429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
6439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
6449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paintPtr The native paint pointer to be applied.
6459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start The start offset in the copied buffer.
6469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end The end offset in the copied buffer.
6479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param isRtl True if the text is RTL.
6489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
6499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
6509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            /* Non Zero */ long paintPtr,
6519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            @IntRange(from = 0) int start,
6529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            @IntRange(from = 0) int end,
6539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            boolean isRtl);
6549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
6569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Apply ReplacementRun to make native measured text.
6579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
6589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
6599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paintPtr The native paint pointer to be applied.
6609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start The start offset in the copied buffer.
6619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end The end offset in the copied buffer.
6629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param width The width of the replacement.
6639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
6649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
6659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  /* Non Zero */ long paintPtr,
6669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  @IntRange(from = 0) int start,
6679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  @IntRange(from = 0) int end,
6689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  @FloatRange(from = 0) float width);
6699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
6719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                 @NonNull char[] text);
6729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
6749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    @CriticalNative
6769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native /* Non Zero */ long nGetReleaseFunc();
6779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka}
678