Compute full text layout in MeasuredText and use it for drawing

The full layout is required for drawing text on UI thread.
To save this work, store the full layout result in MeasuredText and
compose the final layout from stored full layout if possible.

Currently justification/hyphenation is not supported but works normally
as before. Nothing changes on existing non measured text.

StaticLayout creation time for no style text (w/o patch -> w/ patch, N=30)
  MeasuredText Balanced Hyphenation  :    721,297 ->    720,657: (-0.1%)
  MeasuredText Balanced NoHyphenation:    550,588 ->    546,069: (-0.8%)
  MeasuredText Greedy Hyphenation    :    503,582 ->    498,009: (-1.1%)
  MeasuredText Greedy NoHyphenation  :    502,344 ->    498,507: (-0.8%)
  RandomText Balanced Hyphenation    : 19,351,802 -> 19,176,024: (-0.9%)
  RandomText Balanced NoHyphenation  :  8,033,830 ->  7,973,336: (-0.8%)
  RandomText Greedy Hyphenation      :  7,957,335 ->  7,927,316: (-0.4%)
  RandomText Greedy NoHyphenation    :  7,988,884 ->  7,929,717: (-0.7%)

StaticLayout.draw time for no style text (w/o patch -> w/ patch, N=30)
  MeasuredText NoStyled              :    644,453 ->    660,684: (+2.5%)
  MeasuredText NoStyled WithoutCache :  9,251,919 ->    648,992: (-93.0%)
  MeasuredText Styled                :  3,092,353 ->    870,702: (-71.8%)
  MeasuredText Styled WithoutCache   : 12,544,014 ->  1,114,557: (-91.1%)
  RandomText NoStyled                :    582,167 ->    572,092: (-1.7%)
  RandomText NoStyled WithoutCache   :  9,167,670 ->  9,056,447: (-1.2%)
  RandomText Styled                  :  3,064,490 ->  3,029,028: (-1.2%)
  RandomText Styled WithoutCache     : 12,314,863 -> 12,283,026: (-0.3%)

Test: minikin_test
Test: bit CtsTextTestCases:*
Test: bit CtsWidgetTestCases:android.widget.cts.TextViewTest
Test: bit FrameworksCoreTests:android.text.MeasuredParagraphTest
Bug: 63897135

Change-Id: I7e6ec5c953d7d0f767aba4f61f94e62b6f3a3a2b
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index c7d4a4a..45fbf6f 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -176,6 +176,15 @@
     }
 
     /**
+     * Returns the length of the paragraph.
+     *
+     * This is always available.
+     */
+    public int getTextLength() {
+        return mTextLength;
+    }
+
+    /**
      * Returns the characters to be measured.
      *
      * This is always available.
@@ -212,7 +221,7 @@
     /**
      * Returns the whole text width.
      *
-     * This is available only if the MeasureText is computed with computeForMeasurement.
+     * This is available only if the MeasuredParagraph is computed with buildForMeasurement.
      * Returns 0 in other cases.
      */
     public @FloatRange(from = 0.0f) float getWholeWidth() {
@@ -222,7 +231,7 @@
     /**
      * Returns the individual character's width.
      *
-     * This is available only if the MeasureText is computed with computeForMeasurement.
+     * This is available only if the MeasuredParagraph is computed with buildForMeasurement.
      * Returns empty array in other cases.
      */
     public @NonNull FloatArray getWidths() {
@@ -234,7 +243,7 @@
      *
      * If the input text is not a spanned string, this has one value that is the length of the text.
      *
-     * This is available only if the MeasureText is computed with computeForStaticLayout.
+     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
      * Returns empty array in other cases.
      */
     public @NonNull IntArray getSpanEndCache() {
@@ -246,7 +255,7 @@
      *
      * This array holds the repeat of top, bottom, ascent, descent of font metrics value.
      *
-     * This is available only if the MeasureText is computed with computeForStaticLayout.
+     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
      * Returns empty array in other cases.
      */
     public @NonNull IntArray getFontMetrics() {
@@ -256,7 +265,7 @@
     /**
      * Returns the native ptr of the MeasuredParagraph.
      *
-     * This is available only if the MeasureText is computed with computeForStaticLayout.
+     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
      * Returns 0 in other cases.
      */
     public /* Maybe Zero */ long getNativePtr() {
@@ -264,6 +273,30 @@
     }
 
     /**
+     * Returns the width of the given range.
+     *
+     * This is not available if the MeasuredParagraph is computed with buildForBidi.
+     * Returns 0 if the MeasuredParagraph is computed with buildForBidi.
+     *
+     * @param start the inclusive start offset of the target region in the text
+     * @param end the exclusive end offset of the target region in the text
+     */
+    public float getWidth(int start, int end) {
+        if (mNativePtr == 0) {
+            // We have result in Java.
+            final float[] widths = mWidths.getRawArray();
+            float r = 0.0f;
+            for (int i = start; i < end; ++i) {
+                r += widths[i];
+            }
+            return r;
+        } else {
+            // We have result in native.
+            return nGetWidth(mNativePtr, start, end);
+        }
+    }
+
+    /**
      * Generates new MeasuredParagraph for Bidi computation.
      *
      * If recycle is null, this returns new instance. If recycle is not null, this fills computed
@@ -357,6 +390,7 @@
             @IntRange(from = 0) int end,
             @NonNull TextDirectionHeuristic textDir,
             boolean computeHyphenation,
+            boolean computeLayout,
             @Nullable MeasuredParagraph recycle) {
         final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
         mt.resetAndAnalyzeBidi(text, start, end, textDir);
@@ -367,7 +401,7 @@
             try {
                 mt.bindNativeObject(
                         nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
-                              computeHyphenation));
+                              computeHyphenation, computeLayout));
             } finally {
                 nFreeBuilder(nativeBuilderPtr);
             }
@@ -397,7 +431,7 @@
                 }
             }
             mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
-                      computeHyphenation));
+                      computeHyphenation, computeLayout));
         } finally {
             nFreeBuilder(nativeBuilderPtr);
         }
@@ -595,7 +629,7 @@
      *
      * If forward=false is passed, returns the minimum index from the end instead.
      *
-     * This only works if the MeasuredParagraph is computed with computeForMeasurement.
+     * This only works if the MeasuredParagraph is computed with buildForMeasurement.
      * Undefined behavior in other case.
      */
     @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
@@ -626,7 +660,7 @@
     /**
      * Returns the length of the substring.
      *
-     * This only works if the MeasuredParagraph is computed with computeForMeasurement.
+     * This only works if the MeasuredParagraph is computed with buildForMeasurement.
      * Undefined behavior in other case.
      */
     @FloatRange(from = 0.0f) float measure(int start, int limit) {
@@ -672,10 +706,16 @@
 
     private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
                                                  @NonNull char[] text,
-                                                 boolean computeHyphenation);
+                                                 boolean computeHyphenation,
+                                                 boolean computeLayout);
 
     private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
 
     @CriticalNative
+    private static native float nGetWidth(/* Non Zero */ long nativePtr,
+                                         @IntRange(from = 0) int start,
+                                         @IntRange(from = 0) int end);
+
+    @CriticalNative
     private static native /* Non Zero */ long nGetReleaseFunc();
 }