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;
24a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonakaimport android.graphics.Rect;
259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.AutoGrowArray.ByteArray;
269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.AutoGrowArray.FloatArray;
279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.AutoGrowArray.IntArray;
289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.Layout.Directions;
299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.style.MetricAffectingSpan;
309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.text.style.ReplacementSpan;
319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport android.util.Pools.SynchronizedPool;
329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport dalvik.annotation.optimization.CriticalNative;
349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport libcore.util.NativeAllocationRegistry;
369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakaimport java.util.Arrays;
389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka/**
409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * MeasuredParagraph provides text information for rendering purpose.
419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * The first motivation of this class is identify the text directions and retrieving individual
439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * character widths. However retrieving character widths is slower than identifying text directions.
449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * Thus, this class provides several builder methods for specific purposes.
459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * - buildForBidi:
479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   Compute only text directions.
489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * - buildForMeasurement:
499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   Compute text direction and all character widths.
509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * - buildForStaticLayout:
519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   This is bit special. StaticLayout also needs to know text direction and character widths for
529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   line breaking, but all things are done in native code. Similarly, text measurement is done
539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   in native code. So instead of storing result to Java array, this keeps the result in native
549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *   code since there is no good reason to move the results to Java layer.
559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * In addition to the character widths, some additional information is computed for each purposes,
579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * e.g. whole text length for measurement or font metrics for static layout.
589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka *
599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * MeasuredParagraph is NOT a thread safe object.
609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka * @hide
619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka */
629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonakapublic class MeasuredParagraph {
639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            MeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private MeasuredParagraph() {}  // Use build static functions instead.
699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static final SynchronizedPool<MeasuredParagraph> sPool = new SynchronizedPool<>(1);
719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static @NonNull MeasuredParagraph obtain() { // Use build static functions instead.
739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = sPool.acquire();
749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt != null ? mt : new MeasuredParagraph();
759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Recycle the MeasuredParagraph.
799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Do not call any methods after you call this method.
819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public void recycle() {
839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        release();
849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        sPool.release(this);
859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The casted original text.
889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    //
899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // This may be null if the passed text is not a Spanned.
909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable Spanned mSpanned;
919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The start offset of the target range in the original text (mSpanned);
939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @IntRange(from = 0) int mTextStart;
949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The length of the target range in the original text.
969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @IntRange(from = 0) int mTextLength;
979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The copied character buffer for measuring text.
999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    //
1009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The length of this array is mTextLength.
1019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable char[] mCopiedBuffer;
1029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The whole paragraph direction.
1049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Layout.Direction int mParaDir;
1059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // True if the text is LTR direction and doesn't contain any bidi characters.
1079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private boolean mLtrWithoutBidi;
1089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The bidi level for individual characters.
1109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    //
1119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // This is empty if mLtrWithoutBidi is true.
1129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @NonNull ByteArray mLevels = new ByteArray();
1139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The whole width of the text.
1159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // See getWholeWidth comments.
1169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @FloatRange(from = 0.0f) float mWholeWidth;
1179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // Individual characters' widths.
1199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // See getWidths comments.
1209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable FloatArray mWidths = new FloatArray();
1219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The span end positions.
1239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // See getSpanEndCache comments.
1249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable IntArray mSpanEndCache = new IntArray(4);
1259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The font metrics.
1279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // See getFontMetrics comments.
1289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
1299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // The native MeasuredParagraph.
1319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // See getNativePtr comments.
1329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
1339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private /* Maybe Zero */ long mNativePtr = 0;
1349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable Runnable mNativeObjectCleaner;
1359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // Associate the native object to this Java object.
1379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void bindNativeObject(/* Non Zero*/ long nativePtr) {
1389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mNativePtr = nativePtr;
1399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
1409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // Decouple the native object from this Java object and release the native object.
1439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void unbindNativeObject() {
1449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mNativePtr != 0) {
1459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mNativeObjectCleaner.run();
1469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mNativePtr = 0;
1479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
1489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    // Following two objects are for avoiding object allocation.
1519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @NonNull TextPaint mCachedPaint = new TextPaint();
1529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private @Nullable Paint.FontMetricsInt mCachedFm;
1539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
1559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Releases internal buffers.
1569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
1579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public void release() {
1589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        reset();
1599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mLevels.clearWithReleasingLargeArray();
1609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mWidths.clearWithReleasingLargeArray();
1619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mFontMetrics.clearWithReleasingLargeArray();
1629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mSpanEndCache.clearWithReleasingLargeArray();
1639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
1669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Resets the internal state for starting new text.
1679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
1689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void reset() {
1699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mSpanned = null;
1709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mCopiedBuffer = null;
1719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mWholeWidth = 0;
1729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mLevels.clear();
1739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mWidths.clear();
1749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mFontMetrics.clear();
1759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mSpanEndCache.clear();
1769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        unbindNativeObject();
1779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
180783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * Returns the length of the paragraph.
181783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     *
182783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is always available.
183783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     */
184783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    public int getTextLength() {
185783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka        return mTextLength;
186783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    }
187783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka
188783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    /**
1899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the characters to be measured.
1909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
1919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is always available.
1929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
1939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull char[] getChars() {
1949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mCopiedBuffer;
1959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
1969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
1979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
1989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the paragraph direction.
1999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is always available.
2019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @Layout.Direction int getParagraphDir() {
2039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mParaDir;
2049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the directions.
2089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This is always available.
2109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public Directions getDirections(@IntRange(from = 0) int start,  // inclusive
2129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                    @IntRange(from = 0) int end) {  // exclusive
2139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mLtrWithoutBidi) {
2149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return Layout.DIRS_ALL_LEFT_TO_RIGHT;
2159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
2169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final int length = end - start;
2189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start,
2199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                length);
2209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the whole text width.
2249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
225783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForMeasurement.
2269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns 0 in other cases.
2279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @FloatRange(from = 0.0f) float getWholeWidth() {
2299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mWholeWidth;
2309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the individual character's width.
2349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
235783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForMeasurement.
2369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns empty array in other cases.
2379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull FloatArray getWidths() {
2399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mWidths;
2409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the MetricsAffectingSpan end indices.
2449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If the input text is not a spanned string, this has one value that is the length of the text.
2469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
247783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
2489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns empty array in other cases.
2499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull IntArray getSpanEndCache() {
2519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mSpanEndCache;
2529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the int array which holds FontMetrics.
2569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
2579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * This array holds the repeat of top, bottom, ascent, descent of font metrics value.
2589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
259783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
2609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns empty array in other cases.
2619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public @NonNull IntArray getFontMetrics() {
2639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mFontMetrics;
2649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
2679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the native ptr of the MeasuredParagraph.
2689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
269783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
2709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns 0 in other cases.
2719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
2729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public /* Maybe Zero */ long getNativePtr() {
2739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mNativePtr;
2749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
2759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
2769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
277783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * Returns the width of the given range.
278783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     *
279783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This is not available if the MeasuredParagraph is computed with buildForBidi.
280783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * Returns 0 if the MeasuredParagraph is computed with buildForBidi.
281783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     *
282783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
283783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
284783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     */
285783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    public float getWidth(int start, int end) {
286783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka        if (mNativePtr == 0) {
287783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            // We have result in Java.
288783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            final float[] widths = mWidths.getRawArray();
289783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            float r = 0.0f;
290783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            for (int i = start; i < end; ++i) {
291783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                r += widths[i];
292783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            }
293783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            return r;
294783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka        } else {
295783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            // We have result in native.
296783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            return nGetWidth(mNativePtr, start, end);
297783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka        }
298783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    }
299783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka
300783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    /**
301a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka     * Retrieves the bounding rectangle that encloses all of the characters, with an implied origin
302a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka     * at (0, 0).
303a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka     *
304a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
305a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka     */
306fb0abe1feb2cadcee2a3edb2028e1da04bb8a8d5Seigo Nonaka    public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
307fb0abe1feb2cadcee2a3edb2028e1da04bb8a8d5Seigo Nonaka            @NonNull Rect bounds) {
308fb0abe1feb2cadcee2a3edb2028e1da04bb8a8d5Seigo Nonaka        nGetBounds(mNativePtr, mCopiedBuffer, start, end, bounds);
309a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka    }
310a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka
311a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka    /**
3129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Generates new MeasuredParagraph for Bidi computation.
3139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
3159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * result to recycle and returns recycle.
3169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
3189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
3199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
3209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
3219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param recycle pass existing MeasuredParagraph if you want to recycle it.
3229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @return measured text
3249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
3259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text,
3269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @IntRange(from = 0) int start,
3279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @IntRange(from = 0) int end,
3289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @NonNull TextDirectionHeuristic textDir,
3299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                     @Nullable MeasuredParagraph recycle) {
3309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
3319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.resetAndAnalyzeBidi(text, start, end, textDir);
3329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt;
3339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
3349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
3369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Generates new MeasuredParagraph for measuring texts.
3379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
3399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * result to recycle and returns recycle.
3409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paint the paint to be used for rendering the text.
3429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
3439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
3449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
3459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
3469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param recycle pass existing MeasuredParagraph if you want to recycle it.
3479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @return measured text
3499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
3509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint,
3519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @NonNull CharSequence text,
3529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @IntRange(from = 0) int start,
3539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @IntRange(from = 0) int end,
3549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @NonNull TextDirectionHeuristic textDir,
3559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                            @Nullable MeasuredParagraph recycle) {
3569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
3579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.resetAndAnalyzeBidi(text, start, end, textDir);
3589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.mWidths.resize(mt.mTextLength);
3609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mt.mTextLength == 0) {
3619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return mt;
3629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
3639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mt.mSpanned == null) {
3659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // No style change by MetricsAffectingSpan. Just measure all text.
3669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mt.applyMetricsAffectingSpan(
3679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    paint, null /* spans */, start, end, 0 /* native static layout ptr */);
3689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
3699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
3709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            int spanEnd;
3719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
3729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
3739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
3749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        MetricAffectingSpan.class);
3759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
3769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.applyMetricsAffectingSpan(
3779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
3789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
3799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
3809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt;
3819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
3829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
3839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
3849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Generates new MeasuredParagraph for StaticLayout.
3859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
3879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * result to recycle and returns recycle.
3889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paint the paint to be used for rendering the text.
3909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
3919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
3929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
3939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
3949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param recycle pass existing MeasuredParagraph if you want to recycle it.
3959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
3969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @return measured text
3979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
3989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    public static @NonNull MeasuredParagraph buildForStaticLayout(
3999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull TextPaint paint,
4009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull CharSequence text,
4019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int start,
4029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int end,
4039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull TextDirectionHeuristic textDir,
40487b1547c929190c77b6b2d779f1d992691f04d17Seigo Nonaka            boolean computeHyphenation,
405783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka            boolean computeLayout,
4069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @Nullable MeasuredParagraph recycle) {
4079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
4089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mt.resetAndAnalyzeBidi(text, start, end, textDir);
4099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mt.mTextLength == 0) {
4109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Need to build empty native measured text for StaticLayout.
4119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // TODO: Stop creating empty measured text for empty lines.
4129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            long nativeBuilderPtr = nInitBuilder();
4139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            try {
4149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.bindNativeObject(
41587b1547c929190c77b6b2d779f1d992691f04d17Seigo Nonaka                        nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
416783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                              computeHyphenation, computeLayout));
4179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } finally {
4189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                nFreeBuilder(nativeBuilderPtr);
4199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
4209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return mt;
4219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        long nativeBuilderPtr = nInitBuilder();
4249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        try {
4259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (mt.mSpanned == null) {
4269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // No style change by MetricsAffectingSpan. Just measure all text.
4279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
4289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mt.mSpanEndCache.append(end);
4299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
4309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // There may be a MetricsAffectingSpan. Split into span transitions and apply
4319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // styles.
4329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                int spanEnd;
4339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
4349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
4359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                             MetricAffectingSpan.class);
4369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
4379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                            MetricAffectingSpan.class);
4389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
4399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                       MetricAffectingSpan.class);
4409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
4419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                 nativeBuilderPtr);
4429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    mt.mSpanEndCache.append(spanEnd);
4439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                }
4449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
44587b1547c929190c77b6b2d779f1d992691f04d17Seigo Nonaka            mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
446783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                      computeHyphenation, computeLayout));
4479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } finally {
4489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            nFreeBuilder(nativeBuilderPtr);
4499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return mt;
4529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
4539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
4559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Reset internal state and analyzes text for bidirectional runs.
4569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
4579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param text the character sequence to be measured
4589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start the inclusive start offset of the target region in the text
4599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end the exclusive end offset of the target region in the text
4609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param textDir the text direction
4619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
4629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void resetAndAnalyzeBidi(@NonNull CharSequence text,
4639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int start,  // inclusive
4649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int end,  // exclusive
4659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @NonNull TextDirectionHeuristic textDir) {
4669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        reset();
4679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mSpanned = text instanceof Spanned ? (Spanned) text : null;
4689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mTextStart = start;
4699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mTextLength = end - start;
4709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) {
4729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mCopiedBuffer = new char[mTextLength];
4739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        TextUtils.getChars(text, start, end, mCopiedBuffer, 0);
4759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // Replace characters associated with ReplacementSpan to U+FFFC.
4779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mSpanned != null) {
4789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class);
4799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int i = 0; i < spans.length; i++) {
4819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                int startInPara = mSpanned.getSpanStart(spans[i]) - start;
4829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                int endInPara = mSpanned.getSpanEnd(spans[i]) - start;
4839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                // The span interval may be larger and must be restricted to [start, end)
4849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (startInPara < 0) startInPara = 0;
4859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (endInPara > mTextLength) endInPara = mTextLength;
4869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER);
4879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
4889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
4899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
4909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if ((textDir == TextDirectionHeuristics.LTR
4919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
4929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                || textDir == TextDirectionHeuristics.ANYRTL_LTR)
4939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                && TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
4949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLevels.clear();
4959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mParaDir = Layout.DIR_LEFT_TO_RIGHT;
4969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLtrWithoutBidi = true;
4979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
4989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            final int bidiRequest;
4999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (textDir == TextDirectionHeuristics.LTR) {
5009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_LTR;
5019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else if (textDir == TextDirectionHeuristics.RTL) {
5029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_RTL;
5039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
5049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
5059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
5069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
5079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
5089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
5099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
5109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLevels.resize(mTextLength);
5129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray());
5139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mLtrWithoutBidi = false;
5149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
5169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void applyReplacementRun(@NonNull ReplacementSpan replacement,
5189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int start,  // inclusive, in copied buffer
5199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     @IntRange(from = 0) int end,  // exclusive, in copied buffer
5209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                     /* Maybe Zero */ long nativeBuilderPtr) {
5219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // Use original text. Shouldn't matter.
5229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
5239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        //       backward compatibility? or Should we initialize them for getFontMetricsInt?
5249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final float width = replacement.getSize(
5259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
5269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (nativeBuilderPtr == 0) {
5279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Assigns all width to the first character. This is the same behavior as minikin.
5289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mWidths.set(start, width);
5299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (end > start + 1) {
5309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f);
5319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mWholeWidth += width;
5339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
5349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
5359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                               width);
5369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
5389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void applyStyleRun(@IntRange(from = 0) int start,  // inclusive, in copied buffer
5409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                               @IntRange(from = 0) int end,  // exclusive, in copied buffer
5419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                               /* Maybe Zero */ long nativeBuilderPtr) {
5429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (mLtrWithoutBidi) {
5449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // If the whole text is LTR direction, just apply whole region.
5459d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (nativeBuilderPtr == 0) {
5469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mWholeWidth += mCachedPaint.getTextRunAdvances(
5479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
5489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        mWidths.getRawArray(), start);
5499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
5509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
5519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        false /* isRtl */);
5529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
5549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // If there is multiple bidi levels, split into individual bidi level and apply style.
5559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            byte level = mLevels.get(start);
5569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Note that the empty text or empty range won't reach this method.
5579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            // Safe to search from start + 1.
5589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
5599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (levelEnd == end || mLevels.get(levelEnd) != level) {  // transition point
5609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    final boolean isRtl = (level & 0x1) != 0;
5619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    if (nativeBuilderPtr == 0) {
5629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        final int levelLength = levelEnd - levelStart;
5639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        mWholeWidth += mCachedPaint.getTextRunAdvances(
5649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
5659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                isRtl, mWidths.getRawArray(), levelStart);
5669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    } else {
5679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
5689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                levelEnd, isRtl);
5699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    }
5709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    if (levelEnd == end) {
5719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                        break;
5729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    }
5739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    levelStart = levelEnd;
5749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    level = mLevels.get(levelEnd);
5759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                }
5769d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
5779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
5799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private void applyMetricsAffectingSpan(
5819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @NonNull TextPaint paint,
5829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @Nullable MetricAffectingSpan[] spans,
5839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int start,  // inclusive, in original text buffer
5849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            @IntRange(from = 0) int end,  // exclusive, in original text buffer
5859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            /* Maybe Zero */ long nativeBuilderPtr) {
5869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mCachedPaint.set(paint);
5879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        // XXX paint should not have a baseline shift, but...
5889d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        mCachedPaint.baselineShift = 0;
5899d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5909d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final boolean needFontMetrics = nativeBuilderPtr != 0;
5919d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5929d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (needFontMetrics && mCachedFm == null) {
5939d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mCachedFm = new Paint.FontMetricsInt();
5949d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
5959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
5969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        ReplacementSpan replacement = null;
5979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (spans != null) {
5989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            for (int i = 0; i < spans.length; i++) {
5999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                MetricAffectingSpan span = spans[i];
6009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (span instanceof ReplacementSpan) {
6019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    // The last ReplacementSpan is effective for backward compatibility reasons.
6029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    replacement = (ReplacementSpan) span;
6039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                } else {
6049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    // TODO: No need to call updateMeasureState for ReplacementSpan as well?
6059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                    span.updateMeasureState(mCachedPaint);
6069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                }
6079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final int startInCopiedBuffer = start - mTextStart;
6119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        final int endInCopiedBuffer = end - mTextStart;
6129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6130903665f97435d27a893d45927e4a019008cadd6Seigo Nonaka        if (nativeBuilderPtr != 0) {
6140903665f97435d27a893d45927e4a019008cadd6Seigo Nonaka            mCachedPaint.getFontMetricsInt(mCachedFm);
6150903665f97435d27a893d45927e4a019008cadd6Seigo Nonaka        }
6160903665f97435d27a893d45927e4a019008cadd6Seigo Nonaka
6179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (replacement != null) {
6189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
6199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                nativeBuilderPtr);
6209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
6219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
6229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (needFontMetrics) {
6259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            if (mCachedPaint.baselineShift < 0) {
6269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.ascent += mCachedPaint.baselineShift;
6279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.top += mCachedPaint.baselineShift;
6289d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            } else {
6299d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.descent += mCachedPaint.baselineShift;
6309d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                mCachedFm.bottom += mCachedPaint.baselineShift;
6319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.top);
6349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.bottom);
6359d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.ascent);
6369d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            mFontMetrics.append(mCachedFm.descent);
6379d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6389d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
6399d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
6419d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the maximum index that the accumulated width not exceeds the width.
6429d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
6439d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * If forward=false is passed, returns the minimum index from the end instead.
6449d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
645783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This only works if the MeasuredParagraph is computed with buildForMeasurement.
6469d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Undefined behavior in other case.
6479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
6489d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
6499d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        float[] w = mWidths.getRawArray();
6509d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        if (forwards) {
6519d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            int i = 0;
6529d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i < limit) {
6539d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                width -= w[i];
6549d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (width < 0.0f) break;
6559d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                i++;
6569d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6579d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--;
6589d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return i;
6599d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        } else {
6609d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            int i = limit - 1;
6619d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i >= 0) {
6629d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                width -= w[i];
6639d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                if (width < 0.0f) break;
6649d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                i--;
6659d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6669d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) {
6679d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                i++;
6689d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            }
6699d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            return limit - i - 1;
6709d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6719d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
6729d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6739d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
6749d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Returns the length of the substring.
6759d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
676783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka     * This only works if the MeasuredParagraph is computed with buildForMeasurement.
6779d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Undefined behavior in other case.
6789d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
6799d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    @FloatRange(from = 0.0f) float measure(int start, int limit) {
6809d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        float width = 0;
6819d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        float[] w = mWidths.getRawArray();
6829d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        for (int i = start; i < limit; ++i) {
6839d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka            width += w[i];
6849d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        }
6859d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka        return width;
6869d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    }
6879d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
68849ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka    /**
68949ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka     * This only works if the MeasuredParagraph is computed with buildForStaticLayout.
69049ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka     */
691c3328d648e827c8a65f46ed3a8b0ec96076b5ebeSeigo Nonaka    public @IntRange(from = 0) int getMemoryUsage() {
69249ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka        return nGetMemoryUsage(mNativePtr);
69349ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka    }
69449ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka
6959d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native /* Non Zero */ long nInitBuilder();
6969d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
6979d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
6989d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Apply style to make native measured text.
6999d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
7009d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
7019d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paintPtr The native paint pointer to be applied.
7029d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start The start offset in the copied buffer.
7039d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end The end offset in the copied buffer.
7049d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param isRtl True if the text is RTL.
7059d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
7069d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
7079d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            /* Non Zero */ long paintPtr,
7089d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            @IntRange(from = 0) int start,
7099d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            @IntRange(from = 0) int end,
7109d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                            boolean isRtl);
7119d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
7129d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    /**
7139d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * Apply ReplacementRun to make native measured text.
7149d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     *
7159d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
7169d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param paintPtr The native paint pointer to be applied.
7179d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param start The start offset in the copied buffer.
7189d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param end The end offset in the copied buffer.
7199d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     * @param width The width of the replacement.
7209d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka     */
7219d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
7229d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  /* Non Zero */ long paintPtr,
7239d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  @IntRange(from = 0) int start,
7249d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  @IntRange(from = 0) int end,
7259d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka                                                  @FloatRange(from = 0) float width);
7269d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
7279d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
72887b1547c929190c77b6b2d779f1d992691f04d17Seigo Nonaka                                                 @NonNull char[] text,
729783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                                                 boolean computeHyphenation,
730783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                                                 boolean computeLayout);
7319d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
7329d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
7339d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka
7349d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    @CriticalNative
735783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    private static native float nGetWidth(/* Non Zero */ long nativePtr,
736783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                                         @IntRange(from = 0) int start,
737783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka                                         @IntRange(from = 0) int end);
738783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka
739783f961d2fa6f916009844dafeaa08ffaf96a4d3Seigo Nonaka    @CriticalNative
7409d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka    private static native /* Non Zero */ long nGetReleaseFunc();
74149ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka
74249ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka    @CriticalNative
74349ca0244c9fa01dd667755cde6eb2342b8e2e05dSeigo Nonaka    private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
744a553477ddf55d170a66410ed325ae5e5d3005965Seigo Nonaka
745fb0abe1feb2cadcee2a3edb2028e1da04bb8a8d5Seigo Nonaka    private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
746fb0abe1feb2cadcee2a3edb2028e1da04bb8a8d5Seigo Nonaka            Rect rect);
7479d3bd08ebab564ed9231c8ee112e8085cda74ce8Seigo Nonaka}
748