MeasuredParagraph.java revision c3328d648e827c8a65f46ed3a8b0ec96076b5ebe
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    /**
179783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * Returns the length of the paragraph.
180783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     *
181783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is always available.
182783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     */
183783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    public int getTextLength() {
184783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka        return mTextLength;
185783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    }
186783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka
187783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    /**
1889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the characters to be measured.
1899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
1909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is always available.
1919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
1929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull char[] getChars() {
1939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mCopiedBuffer;
1949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
1979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the paragraph direction.
1989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
1999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is always available.
2009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @Layout.Direction int getParagraphDir() {
2029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mParaDir;
2039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the directions.
2079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is always available.
2099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public Directions getDirections(@IntRange(from = 0) int start,  // inclusive
2119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                    @IntRange(from = 0) int end) {  // exclusive
2129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mLtrWithoutBidi) {
2139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return Layout.DIRS_ALL_LEFT_TO_RIGHT;
2149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
2159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final int length = end - start;
2179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start,
2189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                length);
2199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the whole text width.
2239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
224783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForMeasurement.
2259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns 0 in other cases.
2269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @FloatRange(from = 0.0f) float getWholeWidth() {
2289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mWholeWidth;
2299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the individual character's width.
2339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
234783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForMeasurement.
2359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns empty array in other cases.
2369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull FloatArray getWidths() {
2389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mWidths;
2399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the MetricsAffectingSpan end indices.
2439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If the input text is not a spanned string, this has one value that is the length of the text.
2459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
246783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
2479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns empty array in other cases.
2489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull IntArray getSpanEndCache() {
2509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mSpanEndCache;
2519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the int array which holds FontMetrics.
2559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This array holds the repeat of top, bottom, ascent, descent of font metrics value.
2579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
258783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
2599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns empty array in other cases.
2609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull IntArray getFontMetrics() {
2629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mFontMetrics;
2639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the native ptr of the MeasuredParagraph.
2679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
268783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
2699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns 0 in other cases.
2709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public /* Maybe Zero */ long getNativePtr() {
2729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mNativePtr;
2739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
276783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * Returns the width of the given range.
277783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     *
278783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is not available if the MeasuredParagraph is computed with buildForBidi.
279783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * Returns 0 if the MeasuredParagraph is computed with buildForBidi.
280783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     *
281783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
282783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
283783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     */
284783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    public float getWidth(int start, int end) {
285783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka        if (mNativePtr == 0) {
286783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            // We have result in Java.
287783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            final float[] widths = mWidths.getRawArray();
288783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            float r = 0.0f;
289783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            for (int i = start; i < end; ++i) {
290783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                r += widths[i];
291783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            }
292783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            return r;
293783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka        } else {
294783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            // We have result in native.
295783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            return nGetWidth(mNativePtr, start, end);
296783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka        }
297783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    }
298783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka
299783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    /**
3009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Generates new MeasuredParagraph for Bidi computation.
3019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
3039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * result to recycle and returns recycle.
3049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
3069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
3079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
3089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
3099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param recycle pass existing MeasuredParagraph if you want to recycle it.
3109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @return measured text
3129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
3139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text,
3149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @IntRange(from = 0) int start,
3159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @IntRange(from = 0) int end,
3169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @NonNull TextDirectionHeuristic textDir,
3179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @Nullable MeasuredParagraph recycle) {
3189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
3199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.resetAndAnalyzeBidi(text, start, end, textDir);
3209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt;
3219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
3229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
3249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Generates new MeasuredParagraph for measuring texts.
3259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
3279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * result to recycle and returns recycle.
3289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paint the paint to be used for rendering the text.
3309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
3319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
3329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
3339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
3349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param recycle pass existing MeasuredParagraph if you want to recycle it.
3359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @return measured text
3379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
3389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint,
3399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @NonNull CharSequence text,
3409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @IntRange(from = 0) int start,
3419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @IntRange(from = 0) int end,
3429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @NonNull TextDirectionHeuristic textDir,
3439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @Nullable MeasuredParagraph recycle) {
3449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
3459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.resetAndAnalyzeBidi(text, start, end, textDir);
3469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.mWidths.resize(mt.mTextLength);
3489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mt.mTextLength == 0) {
3499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return mt;
3509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
3519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mt.mSpanned == null) {
3539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // No style change by MetricsAffectingSpan. Just measure all text.
3549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mt.applyMetricsAffectingSpan(
3559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    paint, null /* spans */, start, end, 0 /* native static layout ptr */);
3569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
3579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
3589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            int spanEnd;
3599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
3609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
3619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
3629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        MetricAffectingSpan.class);
3639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
3649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.applyMetricsAffectingSpan(
3659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
3669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
3679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
3689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt;
3699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
3709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
3729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Generates new MeasuredParagraph for StaticLayout.
3739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
3759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * result to recycle and returns recycle.
3769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paint the paint to be used for rendering the text.
3789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
3799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
3809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
3819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
3829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param recycle pass existing MeasuredParagraph if you want to recycle it.
3839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @return measured text
3859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
3869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public static @NonNull MeasuredParagraph buildForStaticLayout(
3879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull TextPaint paint,
3889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull CharSequence text,
3899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int start,
3909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int end,
3919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull TextDirectionHeuristic textDir,
39287b1547c929190c77b6b2d779f1d992691f04d17Seigo Nonaka            boolean computeHyphenation,
393783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            boolean computeLayout,
3949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @Nullable MeasuredParagraph recycle) {
3959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
3969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.resetAndAnalyzeBidi(text, start, end, textDir);
3979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mt.mTextLength == 0) {
3989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Need to build empty native measured text for StaticLayout.
3999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // TODO: Stop creating empty measured text for empty lines.
4009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            long nativeBuilderPtr = nInitBuilder();
4019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            try {
4029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.bindNativeObject(
40387b1547c929190c77b6b2d779f1d992691f04d17Seigo Nonaka                        nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
404783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                              computeHyphenation, computeLayout));
4059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } finally {
4069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                nFreeBuilder(nativeBuilderPtr);
4079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
4089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return mt;
4099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        long nativeBuilderPtr = nInitBuilder();
4129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        try {
4139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (mt.mSpanned == null) {
4149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // No style change by MetricsAffectingSpan. Just measure all text.
4159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
4169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.mSpanEndCache.append(end);
4179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
4189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // There may be a MetricsAffectingSpan. Split into span transitions and apply
4199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // styles.
4209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                int spanEnd;
4219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
4229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
4239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                             MetricAffectingSpan.class);
4249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
4259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                            MetricAffectingSpan.class);
4269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
4279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                       MetricAffectingSpan.class);
4289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
4299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                 nativeBuilderPtr);
4309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    mt.mSpanEndCache.append(spanEnd);
4319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                }
4329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
43387b1547c929190c77b6b2d779f1d992691f04d17Seigo Nonaka            mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
434783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                      computeHyphenation, computeLayout));
4359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } finally {
4369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            nFreeBuilder(nativeBuilderPtr);
4379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt;
4409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
4419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
4439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Reset internal state and analyzes text for bidirectional runs.
4449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
4459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
4469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
4479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
4489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
4499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
4509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void resetAndAnalyzeBidi(@NonNull CharSequence text,
4519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int start,  // inclusive
4529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int end,  // exclusive
4539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @NonNull TextDirectionHeuristic textDir) {
4549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        reset();
4559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mSpanned = text instanceof Spanned ? (Spanned) text : null;
4569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mTextStart = start;
4579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mTextLength = end - start;
4589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) {
4609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mCopiedBuffer = new char[mTextLength];
4619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        TextUtils.getChars(text, start, end, mCopiedBuffer, 0);
4639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // Replace characters associated with ReplacementSpan to U+FFFC.
4659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mSpanned != null) {
4669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class);
4679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int i = 0; i < spans.length; i++) {
4699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                int startInPara = mSpanned.getSpanStart(spans[i]) - start;
4709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                int endInPara = mSpanned.getSpanEnd(spans[i]) - start;
4719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // The span interval may be larger and must be restricted to [start, end)
4729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (startInPara < 0) startInPara = 0;
4739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (endInPara > mTextLength) endInPara = mTextLength;
4749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER);
4759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
4769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if ((textDir == TextDirectionHeuristics.LTR
4799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
4809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                || textDir == TextDirectionHeuristics.ANYRTL_LTR)
4819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                && TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
4829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLevels.clear();
4839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mParaDir = Layout.DIR_LEFT_TO_RIGHT;
4849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLtrWithoutBidi = true;
4859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
4869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            final int bidiRequest;
4879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (textDir == TextDirectionHeuristics.LTR) {
4889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_LTR;
4899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else if (textDir == TextDirectionHeuristics.RTL) {
4909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_RTL;
4919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
4929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
4939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
4949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
4959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
4969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
4979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
4989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
4999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLevels.resize(mTextLength);
5009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray());
5019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLtrWithoutBidi = false;
5029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
5049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void applyReplacementRun(@NonNull ReplacementSpan replacement,
5069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int start,  // inclusive, in copied buffer
5079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int end,  // exclusive, in copied buffer
5089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     /* Maybe Zero */ long nativeBuilderPtr) {
5099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // Use original text. Shouldn't matter.
5109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
5119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        //       backward compatibility? or Should we initialize them for getFontMetricsInt?
5129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final float width = replacement.getSize(
5139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
5149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (nativeBuilderPtr == 0) {
5159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Assigns all width to the first character. This is the same behavior as minikin.
5169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mWidths.set(start, width);
5179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (end > start + 1) {
5189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f);
5199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mWholeWidth += width;
5219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
5229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
5239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                               width);
5249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
5269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void applyStyleRun(@IntRange(from = 0) int start,  // inclusive, in copied buffer
5289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                               @IntRange(from = 0) int end,  // exclusive, in copied buffer
5299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                               /* Maybe Zero */ long nativeBuilderPtr) {
5309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (nativeBuilderPtr != 0) {
5319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mCachedPaint.getFontMetricsInt(mCachedFm);
5329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mLtrWithoutBidi) {
5359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // If the whole text is LTR direction, just apply whole region.
5369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (nativeBuilderPtr == 0) {
5379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mWholeWidth += mCachedPaint.getTextRunAdvances(
5389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
5399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        mWidths.getRawArray(), start);
5409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
5419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
5429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        false /* isRtl */);
5439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
5459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // If there is multiple bidi levels, split into individual bidi level and apply style.
5469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            byte level = mLevels.get(start);
5479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Note that the empty text or empty range won't reach this method.
5489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Safe to search from start + 1.
5499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
5509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (levelEnd == end || mLevels.get(levelEnd) != level) {  // transition point
5519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    final boolean isRtl = (level & 0x1) != 0;
5529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    if (nativeBuilderPtr == 0) {
5539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        final int levelLength = levelEnd - levelStart;
5549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        mWholeWidth += mCachedPaint.getTextRunAdvances(
5559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
5569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                isRtl, mWidths.getRawArray(), levelStart);
5579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    } else {
5589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
5599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                levelEnd, isRtl);
5609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    }
5619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    if (levelEnd == end) {
5629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        break;
5639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    }
5649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    levelStart = levelEnd;
5659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    level = mLevels.get(levelEnd);
5669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                }
5679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
5709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void applyMetricsAffectingSpan(
5729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull TextPaint paint,
5739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @Nullable MetricAffectingSpan[] spans,
5749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int start,  // inclusive, in original text buffer
5759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int end,  // exclusive, in original text buffer
5769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            /* Maybe Zero */ long nativeBuilderPtr) {
5779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mCachedPaint.set(paint);
5789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // XXX paint should not have a baseline shift, but...
5799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mCachedPaint.baselineShift = 0;
5809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final boolean needFontMetrics = nativeBuilderPtr != 0;
5829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (needFontMetrics && mCachedFm == null) {
5849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mCachedFm = new Paint.FontMetricsInt();
5859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        ReplacementSpan replacement = null;
5889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (spans != null) {
5899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int i = 0; i < spans.length; i++) {
5909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                MetricAffectingSpan span = spans[i];
5919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (span instanceof ReplacementSpan) {
5929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    // The last ReplacementSpan is effective for backward compatibility reasons.
5939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    replacement = (ReplacementSpan) span;
5949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                } else {
5959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    // TODO: No need to call updateMeasureState for ReplacementSpan as well?
5969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    span.updateMeasureState(mCachedPaint);
5979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                }
5989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final int startInCopiedBuffer = start - mTextStart;
6029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final int endInCopiedBuffer = end - mTextStart;
6039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (replacement != null) {
6059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
6069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                nativeBuilderPtr);
6079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
6089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
6099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (needFontMetrics) {
6129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (mCachedPaint.baselineShift < 0) {
6139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.ascent += mCachedPaint.baselineShift;
6149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.top += mCachedPaint.baselineShift;
6159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
6169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.descent += mCachedPaint.baselineShift;
6179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.bottom += mCachedPaint.baselineShift;
6189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.top);
6219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.bottom);
6229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.ascent);
6239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.descent);
6249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
6269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
6289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the maximum index that the accumulated width not exceeds the width.
6299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
6309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If forward=false is passed, returns the minimum index from the end instead.
6319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
632783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This only works if the MeasuredParagraph is computed with buildForMeasurement.
6339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Undefined behavior in other case.
6349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
6359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
6369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        float[] w = mWidths.getRawArray();
6379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (forwards) {
6389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            int i = 0;
6399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i < limit) {
6409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                width -= w[i];
6419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (width < 0.0f) break;
6429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                i++;
6439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--;
6459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return i;
6469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
6479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            int i = limit - 1;
6489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i >= 0) {
6499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                width -= w[i];
6509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (width < 0.0f) break;
6519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                i--;
6529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) {
6549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                i++;
6559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return limit - i - 1;
6579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
6599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
6619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the length of the substring.
6629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
663783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This only works if the MeasuredParagraph is computed with buildForMeasurement.
6649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Undefined behavior in other case.
6659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
6669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    @FloatRange(from = 0.0f) float measure(int start, int limit) {
6679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        float width = 0;
6689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        float[] w = mWidths.getRawArray();
6699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        for (int i = start; i < limit; ++i) {
6709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            width += w[i];
6719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return width;
6739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
6749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
67549ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka    /**
67649ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka     * This only works if the MeasuredParagraph is computed with buildForStaticLayout.
67749ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka     */
678c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka    public @IntRange(from = 0) int getMemoryUsage() {
67949ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka        return nGetMemoryUsage(mNativePtr);
68049ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka    }
68149ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka
6829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native /* Non Zero */ long nInitBuilder();
6839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
6859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Apply style to make native measured text.
6869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
6879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
6889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paintPtr The native paint pointer to be applied.
6899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start The start offset in the copied buffer.
6909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end The end offset in the copied buffer.
6919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param isRtl True if the text is RTL.
6929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
6939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
6949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            /* Non Zero */ long paintPtr,
6959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            @IntRange(from = 0) int start,
6969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            @IntRange(from = 0) int end,
6979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            boolean isRtl);
6989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
7009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Apply ReplacementRun to make native measured text.
7019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
7029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
7039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paintPtr The native paint pointer to be applied.
7049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start The start offset in the copied buffer.
7059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end The end offset in the copied buffer.
7069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param width The width of the replacement.
7079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
7089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
7099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  /* Non Zero */ long paintPtr,
7109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  @IntRange(from = 0) int start,
7119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  @IntRange(from = 0) int end,
7129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  @FloatRange(from = 0) float width);
7139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
7149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
71587b1547c929190c77b6b2d779f1d992691f04d17Seigo Nonaka                                                 @NonNull char[] text,
716783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                                                 boolean computeHyphenation,
717783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                                                 boolean computeLayout);
7189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
7199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
7209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
7219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    @CriticalNative
722783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    private static native float nGetWidth(/* Non Zero */ long nativePtr,
723783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                                         @IntRange(from = 0) int start,
724783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                                         @IntRange(from = 0) int end);
725783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka
726783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    @CriticalNative
7279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native /* Non Zero */ long nGetReleaseFunc();
72849ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka
72949ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka    @CriticalNative
73049ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka    private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
7319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka}
732