diff options
| author | 2018-01-11 10:02:12 -0800 | |
|---|---|---|
| committer | 2018-01-11 11:33:32 -0800 | |
| commit | 9d3bd08ebab564ed9231c8ee112e8085cda74ce8 (patch) | |
| tree | 1cf3f425d82f10a315227d04315f76bb2ed95069 | |
| parent | 11954f5060c12299041ec1efbb02411833322e1d (diff) | |
Rename PremeasuredText to MeasuredText
There is already MeasuredText, so renamed existing MeasuredText to
MeasuredParagraph, then renamed PremeasuredText to MeasuredText.
Bug: 67504091
Test: bit CtsWidgetTestCases:android.widget.cts.TextViewTest
Test: bit CtsTextTestCases:*
Change-Id: Ie20bea9501b18fabb36f64d388a7851c4643d4c3
| -rw-r--r-- | apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java | 10 | ||||
| -rw-r--r-- | api/current.txt | 28 | ||||
| -rw-r--r-- | core/java/android/text/Layout.java | 4 | ||||
| -rw-r--r-- | core/java/android/text/MeasuredParagraph.java | 677 | ||||
| -rw-r--r-- | core/java/android/text/MeasuredText.java | 738 | ||||
| -rw-r--r-- | core/java/android/text/PremeasuredText.java | 272 | ||||
| -rw-r--r-- | core/java/android/text/StaticLayout.java | 51 | ||||
| -rw-r--r-- | core/java/android/text/TextUtils.java | 16 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 4 | ||||
| -rw-r--r-- | core/jni/Android.bp | 2 | ||||
| -rw-r--r-- | core/jni/AndroidRuntime.cpp | 4 | ||||
| -rw-r--r-- | core/jni/android_text_MeasuredParagraph.cpp (renamed from core/jni/android_text_MeasuredText.cpp) | 22 | ||||
| -rw-r--r-- | core/jni/android_text_StaticLayout.cpp | 2 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/text/MeasuredParagraphTest.java (renamed from core/tests/coretests/src/android/text/MeasuredTextTest.java) | 24 |
14 files changed, 928 insertions, 926 deletions
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java index 5653a039a9ed..93a0fc314b7f 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java @@ -190,7 +190,7 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final PremeasuredText text = PremeasuredText.build( + final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); state.resumeTiming(); @@ -206,7 +206,7 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final PremeasuredText text = PremeasuredText.build( + final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); state.resumeTiming(); @@ -222,7 +222,7 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final PremeasuredText text = PremeasuredText.build( + final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); state.resumeTiming(); @@ -238,7 +238,7 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final PremeasuredText text = PremeasuredText.build( + final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); state.resumeTiming(); @@ -254,7 +254,7 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final PremeasuredText text = PremeasuredText.build( + final MeasuredText text = MeasuredText.build( generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, LTR); state.resumeTiming(); diff --git a/api/current.txt b/api/current.txt index d8c7d029114c..838487e03ab3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -42158,20 +42158,9 @@ package android.text { method public boolean isAllowed(char); } - public abstract interface NoCopySpan { - } - - public static class NoCopySpan.Concrete implements android.text.NoCopySpan { - ctor public NoCopySpan.Concrete(); - } - - public abstract interface ParcelableSpan implements android.os.Parcelable { - method public abstract int getSpanTypeId(); - } - - public class PremeasuredText implements android.text.Spanned { - method public static android.text.PremeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic); - method public static android.text.PremeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int); + public class MeasuredText implements android.text.Spanned { + method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic); + method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int); method public char charAt(int); method public int getEnd(); method public android.text.TextPaint getPaint(); @@ -42190,6 +42179,17 @@ package android.text { method public java.lang.CharSequence subSequence(int, int); } + public abstract interface NoCopySpan { + } + + public static class NoCopySpan.Concrete implements android.text.NoCopySpan { + ctor public NoCopySpan.Concrete(); + } + + public abstract interface ParcelableSpan implements android.os.Parcelable { + method public abstract int getSpanTypeId(); + } + public class Selection { method public static boolean extendDown(android.text.Spannable, android.text.Layout); method public static boolean extendLeft(android.text.Spannable, android.text.Layout); diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index bf4b6ac5194f..aa97b2aba749 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1917,10 +1917,10 @@ public abstract class Layout { private static float measurePara(TextPaint paint, CharSequence text, int start, int end, TextDirectionHeuristic textDir) { - MeasuredText mt = null; + MeasuredParagraph mt = null; TextLine tl = TextLine.obtain(); try { - mt = MeasuredText.buildForBidi(text, start, end, textDir, mt); + mt = MeasuredParagraph.buildForBidi(text, start, end, textDir, mt); final char[] chars = mt.getChars(); final int len = chars.length; final Directions directions = mt.getDirections(0, len); diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java new file mode 100644 index 000000000000..c93e0365d58d --- /dev/null +++ b/core/java/android/text/MeasuredParagraph.java @@ -0,0 +1,677 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Paint; +import android.text.AutoGrowArray.ByteArray; +import android.text.AutoGrowArray.FloatArray; +import android.text.AutoGrowArray.IntArray; +import android.text.Layout.Directions; +import android.text.style.MetricAffectingSpan; +import android.text.style.ReplacementSpan; +import android.util.Pools.SynchronizedPool; + +import dalvik.annotation.optimization.CriticalNative; + +import libcore.util.NativeAllocationRegistry; + +import java.util.Arrays; + +/** + * MeasuredParagraph provides text information for rendering purpose. + * + * The first motivation of this class is identify the text directions and retrieving individual + * character widths. However retrieving character widths is slower than identifying text directions. + * Thus, this class provides several builder methods for specific purposes. + * + * - buildForBidi: + * Compute only text directions. + * - buildForMeasurement: + * Compute text direction and all character widths. + * - buildForStaticLayout: + * This is bit special. StaticLayout also needs to know text direction and character widths for + * line breaking, but all things are done in native code. Similarly, text measurement is done + * in native code. So instead of storing result to Java array, this keeps the result in native + * code since there is no good reason to move the results to Java layer. + * + * In addition to the character widths, some additional information is computed for each purposes, + * e.g. whole text length for measurement or font metrics for static layout. + * + * MeasuredParagraph is NOT a thread safe object. + * @hide + */ +public class MeasuredParagraph { + private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC'; + + private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( + MeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024); + + private MeasuredParagraph() {} // Use build static functions instead. + + private static final SynchronizedPool<MeasuredParagraph> sPool = new SynchronizedPool<>(1); + + private static @NonNull MeasuredParagraph obtain() { // Use build static functions instead. + final MeasuredParagraph mt = sPool.acquire(); + return mt != null ? mt : new MeasuredParagraph(); + } + + /** + * Recycle the MeasuredParagraph. + * + * Do not call any methods after you call this method. + */ + public void recycle() { + release(); + sPool.release(this); + } + + // The casted original text. + // + // This may be null if the passed text is not a Spanned. + private @Nullable Spanned mSpanned; + + // The start offset of the target range in the original text (mSpanned); + private @IntRange(from = 0) int mTextStart; + + // The length of the target range in the original text. + private @IntRange(from = 0) int mTextLength; + + // The copied character buffer for measuring text. + // + // The length of this array is mTextLength. + private @Nullable char[] mCopiedBuffer; + + // The whole paragraph direction. + private @Layout.Direction int mParaDir; + + // True if the text is LTR direction and doesn't contain any bidi characters. + private boolean mLtrWithoutBidi; + + // The bidi level for individual characters. + // + // This is empty if mLtrWithoutBidi is true. + private @NonNull ByteArray mLevels = new ByteArray(); + + // The whole width of the text. + // See getWholeWidth comments. + private @FloatRange(from = 0.0f) float mWholeWidth; + + // Individual characters' widths. + // See getWidths comments. + private @Nullable FloatArray mWidths = new FloatArray(); + + // The span end positions. + // See getSpanEndCache comments. + private @Nullable IntArray mSpanEndCache = new IntArray(4); + + // The font metrics. + // See getFontMetrics comments. + private @Nullable IntArray mFontMetrics = new IntArray(4 * 4); + + // The native MeasuredParagraph. + // See getNativePtr comments. + // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead. + private /* Maybe Zero */ long mNativePtr = 0; + private @Nullable Runnable mNativeObjectCleaner; + + // Associate the native object to this Java object. + private void bindNativeObject(/* Non Zero*/ long nativePtr) { + mNativePtr = nativePtr; + mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr); + } + + // Decouple the native object from this Java object and release the native object. + private void unbindNativeObject() { + if (mNativePtr != 0) { + mNativeObjectCleaner.run(); + mNativePtr = 0; + } + } + + // Following two objects are for avoiding object allocation. + private @NonNull TextPaint mCachedPaint = new TextPaint(); + private @Nullable Paint.FontMetricsInt mCachedFm; + + /** + * Releases internal buffers. + */ + public void release() { + reset(); + mLevels.clearWithReleasingLargeArray(); + mWidths.clearWithReleasingLargeArray(); + mFontMetrics.clearWithReleasingLargeArray(); + mSpanEndCache.clearWithReleasingLargeArray(); + } + + /** + * Resets the internal state for starting new text. + */ + private void reset() { + mSpanned = null; + mCopiedBuffer = null; + mWholeWidth = 0; + mLevels.clear(); + mWidths.clear(); + mFontMetrics.clear(); + mSpanEndCache.clear(); + unbindNativeObject(); + } + + /** + * Returns the characters to be measured. + * + * This is always available. + */ + public @NonNull char[] getChars() { + return mCopiedBuffer; + } + + /** + * Returns the paragraph direction. + * + * This is always available. + */ + public @Layout.Direction int getParagraphDir() { + return mParaDir; + } + + /** + * Returns the directions. + * + * This is always available. + */ + public Directions getDirections(@IntRange(from = 0) int start, // inclusive + @IntRange(from = 0) int end) { // exclusive + if (mLtrWithoutBidi) { + return Layout.DIRS_ALL_LEFT_TO_RIGHT; + } + + final int length = end - start; + return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start, + length); + } + + /** + * Returns the whole text width. + * + * This is available only if the MeasureText is computed with computeForMeasurement. + * Returns 0 in other cases. + */ + public @FloatRange(from = 0.0f) float getWholeWidth() { + return mWholeWidth; + } + + /** + * Returns the individual character's width. + * + * This is available only if the MeasureText is computed with computeForMeasurement. + * Returns empty array in other cases. + */ + public @NonNull FloatArray getWidths() { + return mWidths; + } + + /** + * Returns the MetricsAffectingSpan end indices. + * + * 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. + * Returns empty array in other cases. + */ + public @NonNull IntArray getSpanEndCache() { + return mSpanEndCache; + } + + /** + * Returns the int array which holds FontMetrics. + * + * 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. + * Returns empty array in other cases. + */ + public @NonNull IntArray getFontMetrics() { + return mFontMetrics; + } + + /** + * Returns the native ptr of the MeasuredParagraph. + * + * This is available only if the MeasureText is computed with computeForStaticLayout. + * Returns 0 in other cases. + */ + public /* Maybe Zero */ long getNativePtr() { + return mNativePtr; + } + + /** + * Generates new MeasuredParagraph for Bidi computation. + * + * If recycle is null, this returns new instance. If recycle is not null, this fills computed + * result to recycle and returns recycle. + * + * @param text the character sequence to be measured + * @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 + * @param textDir the text direction + * @param recycle pass existing MeasuredParagraph if you want to recycle it. + * + * @return measured text + */ + public static @NonNull MeasuredParagraph buildForBidi(@NonNull CharSequence text, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @NonNull TextDirectionHeuristic textDir, + @Nullable MeasuredParagraph recycle) { + final MeasuredParagraph mt = recycle == null ? obtain() : recycle; + mt.resetAndAnalyzeBidi(text, start, end, textDir); + return mt; + } + + /** + * Generates new MeasuredParagraph for measuring texts. + * + * If recycle is null, this returns new instance. If recycle is not null, this fills computed + * result to recycle and returns recycle. + * + * @param paint the paint to be used for rendering the text. + * @param text the character sequence to be measured + * @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 + * @param textDir the text direction + * @param recycle pass existing MeasuredParagraph if you want to recycle it. + * + * @return measured text + */ + public static @NonNull MeasuredParagraph buildForMeasurement(@NonNull TextPaint paint, + @NonNull CharSequence text, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @NonNull TextDirectionHeuristic textDir, + @Nullable MeasuredParagraph recycle) { + final MeasuredParagraph mt = recycle == null ? obtain() : recycle; + mt.resetAndAnalyzeBidi(text, start, end, textDir); + + mt.mWidths.resize(mt.mTextLength); + if (mt.mTextLength == 0) { + return mt; + } + + if (mt.mSpanned == null) { + // No style change by MetricsAffectingSpan. Just measure all text. + mt.applyMetricsAffectingSpan( + paint, null /* spans */, start, end, 0 /* native static layout ptr */); + } else { + // There may be a MetricsAffectingSpan. Split into span transitions and apply styles. + int spanEnd; + for (int spanStart = start; spanStart < end; spanStart = spanEnd) { + spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class); + MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, + MetricAffectingSpan.class); + spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); + mt.applyMetricsAffectingSpan( + paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */); + } + } + return mt; + } + + /** + * Generates new MeasuredParagraph for StaticLayout. + * + * If recycle is null, this returns new instance. If recycle is not null, this fills computed + * result to recycle and returns recycle. + * + * @param paint the paint to be used for rendering the text. + * @param text the character sequence to be measured + * @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 + * @param textDir the text direction + * @param recycle pass existing MeasuredParagraph if you want to recycle it. + * + * @return measured text + */ + public static @NonNull MeasuredParagraph buildForStaticLayout( + @NonNull TextPaint paint, + @NonNull CharSequence text, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @NonNull TextDirectionHeuristic textDir, + @Nullable MeasuredParagraph recycle) { + final MeasuredParagraph mt = recycle == null ? obtain() : recycle; + mt.resetAndAnalyzeBidi(text, start, end, textDir); + if (mt.mTextLength == 0) { + // Need to build empty native measured text for StaticLayout. + // TODO: Stop creating empty measured text for empty lines. + long nativeBuilderPtr = nInitBuilder(); + try { + mt.bindNativeObject( + nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer)); + } finally { + nFreeBuilder(nativeBuilderPtr); + } + return mt; + } + + long nativeBuilderPtr = nInitBuilder(); + try { + if (mt.mSpanned == null) { + // No style change by MetricsAffectingSpan. Just measure all text. + mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr); + mt.mSpanEndCache.append(end); + } else { + // There may be a MetricsAffectingSpan. Split into span transitions and apply + // styles. + int spanEnd; + for (int spanStart = start; spanStart < end; spanStart = spanEnd) { + spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, + MetricAffectingSpan.class); + MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, + MetricAffectingSpan.class); + spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, + MetricAffectingSpan.class); + mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd, + nativeBuilderPtr); + mt.mSpanEndCache.append(spanEnd); + } + } + mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer)); + } finally { + nFreeBuilder(nativeBuilderPtr); + } + + return mt; + } + + /** + * Reset internal state and analyzes text for bidirectional runs. + * + * @param text the character sequence to be measured + * @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 + * @param textDir the text direction + */ + private void resetAndAnalyzeBidi(@NonNull CharSequence text, + @IntRange(from = 0) int start, // inclusive + @IntRange(from = 0) int end, // exclusive + @NonNull TextDirectionHeuristic textDir) { + reset(); + mSpanned = text instanceof Spanned ? (Spanned) text : null; + mTextStart = start; + mTextLength = end - start; + + if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) { + mCopiedBuffer = new char[mTextLength]; + } + TextUtils.getChars(text, start, end, mCopiedBuffer, 0); + + // Replace characters associated with ReplacementSpan to U+FFFC. + if (mSpanned != null) { + ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class); + + for (int i = 0; i < spans.length; i++) { + int startInPara = mSpanned.getSpanStart(spans[i]) - start; + int endInPara = mSpanned.getSpanEnd(spans[i]) - start; + // The span interval may be larger and must be restricted to [start, end) + if (startInPara < 0) startInPara = 0; + if (endInPara > mTextLength) endInPara = mTextLength; + Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER); + } + } + + if ((textDir == TextDirectionHeuristics.LTR + || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR + || textDir == TextDirectionHeuristics.ANYRTL_LTR) + && TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) { + mLevels.clear(); + mParaDir = Layout.DIR_LEFT_TO_RIGHT; + mLtrWithoutBidi = true; + } else { + final int bidiRequest; + if (textDir == TextDirectionHeuristics.LTR) { + bidiRequest = Layout.DIR_REQUEST_LTR; + } else if (textDir == TextDirectionHeuristics.RTL) { + bidiRequest = Layout.DIR_REQUEST_RTL; + } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) { + bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR; + } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) { + bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL; + } else { + final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength); + bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR; + } + mLevels.resize(mTextLength); + mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray()); + mLtrWithoutBidi = false; + } + } + + private void applyReplacementRun(@NonNull ReplacementSpan replacement, + @IntRange(from = 0) int start, // inclusive, in copied buffer + @IntRange(from = 0) int end, // exclusive, in copied buffer + /* Maybe Zero */ long nativeBuilderPtr) { + // Use original text. Shouldn't matter. + // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for + // backward compatibility? or Should we initialize them for getFontMetricsInt? + final float width = replacement.getSize( + mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm); + if (nativeBuilderPtr == 0) { + // Assigns all width to the first character. This is the same behavior as minikin. + mWidths.set(start, width); + if (end > start + 1) { + Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f); + } + mWholeWidth += width; + } else { + nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end, + width); + } + } + + private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer + @IntRange(from = 0) int end, // exclusive, in copied buffer + /* Maybe Zero */ long nativeBuilderPtr) { + if (nativeBuilderPtr != 0) { + mCachedPaint.getFontMetricsInt(mCachedFm); + } + + if (mLtrWithoutBidi) { + // If the whole text is LTR direction, just apply whole region. + if (nativeBuilderPtr == 0) { + mWholeWidth += mCachedPaint.getTextRunAdvances( + mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */, + mWidths.getRawArray(), start); + } else { + nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end, + false /* isRtl */); + } + } else { + // If there is multiple bidi levels, split into individual bidi level and apply style. + byte level = mLevels.get(start); + // Note that the empty text or empty range won't reach this method. + // Safe to search from start + 1. + for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) { + if (levelEnd == end || mLevels.get(levelEnd) != level) { // transition point + final boolean isRtl = (level & 0x1) != 0; + if (nativeBuilderPtr == 0) { + final int levelLength = levelEnd - levelStart; + mWholeWidth += mCachedPaint.getTextRunAdvances( + mCopiedBuffer, levelStart, levelLength, levelStart, levelLength, + isRtl, mWidths.getRawArray(), levelStart); + } else { + nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart, + levelEnd, isRtl); + } + if (levelEnd == end) { + break; + } + levelStart = levelEnd; + level = mLevels.get(levelEnd); + } + } + } + } + + private void applyMetricsAffectingSpan( + @NonNull TextPaint paint, + @Nullable MetricAffectingSpan[] spans, + @IntRange(from = 0) int start, // inclusive, in original text buffer + @IntRange(from = 0) int end, // exclusive, in original text buffer + /* Maybe Zero */ long nativeBuilderPtr) { + mCachedPaint.set(paint); + // XXX paint should not have a baseline shift, but... + mCachedPaint.baselineShift = 0; + + final boolean needFontMetrics = nativeBuilderPtr != 0; + + if (needFontMetrics && mCachedFm == null) { + mCachedFm = new Paint.FontMetricsInt(); + } + + ReplacementSpan replacement = null; + if (spans != null) { + for (int i = 0; i < spans.length; i++) { + MetricAffectingSpan span = spans[i]; + if (span instanceof ReplacementSpan) { + // The last ReplacementSpan is effective for backward compatibility reasons. + replacement = (ReplacementSpan) span; + } else { + // TODO: No need to call updateMeasureState for ReplacementSpan as well? + span.updateMeasureState(mCachedPaint); + } + } + } + + final int startInCopiedBuffer = start - mTextStart; + final int endInCopiedBuffer = end - mTextStart; + + if (replacement != null) { + applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, + nativeBuilderPtr); + } else { + applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr); + } + + if (needFontMetrics) { + if (mCachedPaint.baselineShift < 0) { + mCachedFm.ascent += mCachedPaint.baselineShift; + mCachedFm.top += mCachedPaint.baselineShift; + } else { + mCachedFm.descent += mCachedPaint.baselineShift; + mCachedFm.bottom += mCachedPaint.baselineShift; + } + + mFontMetrics.append(mCachedFm.top); + mFontMetrics.append(mCachedFm.bottom); + mFontMetrics.append(mCachedFm.ascent); + mFontMetrics.append(mCachedFm.descent); + } + } + + /** + * Returns the maximum index that the accumulated width not exceeds the width. + * + * If forward=false is passed, returns the minimum index from the end instead. + * + * This only works if the MeasuredParagraph is computed with computeForMeasurement. + * Undefined behavior in other case. + */ + @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) { + float[] w = mWidths.getRawArray(); + if (forwards) { + int i = 0; + while (i < limit) { + width -= w[i]; + if (width < 0.0f) break; + i++; + } + while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--; + return i; + } else { + int i = limit - 1; + while (i >= 0) { + width -= w[i]; + if (width < 0.0f) break; + i--; + } + while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) { + i++; + } + return limit - i - 1; + } + } + + /** + * Returns the length of the substring. + * + * This only works if the MeasuredParagraph is computed with computeForMeasurement. + * Undefined behavior in other case. + */ + @FloatRange(from = 0.0f) float measure(int start, int limit) { + float width = 0; + float[] w = mWidths.getRawArray(); + for (int i = start; i < limit; ++i) { + width += w[i]; + } + return width; + } + + private static native /* Non Zero */ long nInitBuilder(); + + /** + * Apply style to make native measured text. + * + * @param nativeBuilderPtr The native MeasuredParagraph builder pointer. + * @param paintPtr The native paint pointer to be applied. + * @param start The start offset in the copied buffer. + * @param end The end offset in the copied buffer. + * @param isRtl True if the text is RTL. + */ + private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr, + /* Non Zero */ long paintPtr, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + boolean isRtl); + + /** + * Apply ReplacementRun to make native measured text. + * + * @param nativeBuilderPtr The native MeasuredParagraph builder pointer. + * @param paintPtr The native paint pointer to be applied. + * @param start The start offset in the copied buffer. + * @param end The end offset in the copied buffer. + * @param width The width of the replacement. + */ + private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr, + /* Non Zero */ long paintPtr, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @FloatRange(from = 0) float width); + + private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr, + @NonNull char[] text); + + private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr); + + @CriticalNative + private static native /* Non Zero */ long nGetReleaseFunc(); +} diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index 14d6f9e8a9ef..2c30360b81bc 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,661 +16,255 @@ package android.text; -import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; -import android.annotation.Nullable; -import android.graphics.Paint; -import android.text.AutoGrowArray.ByteArray; -import android.text.AutoGrowArray.FloatArray; -import android.text.AutoGrowArray.IntArray; -import android.text.Layout.Directions; -import android.text.style.MetricAffectingSpan; -import android.text.style.ReplacementSpan; -import android.util.Pools.SynchronizedPool; +import android.util.IntArray; -import dalvik.annotation.optimization.CriticalNative; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.Preconditions; -import libcore.util.NativeAllocationRegistry; - -import java.util.Arrays; +import java.util.ArrayList; /** - * MeasuredText provides text information for rendering purpose. - * - * The first motivation of this class is identify the text directions and retrieving individual - * character widths. However retrieving character widths is slower than identifying text directions. - * Thus, this class provides several builder methods for specific purposes. - * - * - buildForBidi: - * Compute only text directions. - * - buildForMeasurement: - * Compute text direction and all character widths. - * - buildForStaticLayout: - * This is bit special. StaticLayout also needs to know text direction and character widths for - * line breaking, but all things are done in native code. Similarly, text measurement is done - * in native code. So instead of storing result to Java array, this keeps the result in native - * code since there is no good reason to move the results to Java layer. - * - * In addition to the character widths, some additional information is computed for each purposes, - * e.g. whole text length for measurement or font metrics for static layout. - * - * MeasuredText is NOT a thread safe object. - * @hide + * A text which has already been measured. */ -public class MeasuredText { - private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC'; - - private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( - MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024); - - private MeasuredText() {} // Use build static functions instead. - - private static final SynchronizedPool<MeasuredText> sPool = new SynchronizedPool<>(1); - - private static @NonNull MeasuredText obtain() { // Use build static functions instead. - final MeasuredText mt = sPool.acquire(); - return mt != null ? mt : new MeasuredText(); - } - - /** - * Recycle the MeasuredText. - * - * Do not call any methods after you call this method. - */ - public void recycle() { - release(); - sPool.release(this); - } +public class MeasuredText implements Spanned { + private static final char LINE_FEED = '\n'; - // The casted original text. - // - // This may be null if the passed text is not a Spanned. - private @Nullable Spanned mSpanned; - - // The start offset of the target range in the original text (mSpanned); - private @IntRange(from = 0) int mTextStart; + // The original text. + private final @NonNull CharSequence mText; - // The length of the target range in the original text. - private @IntRange(from = 0) int mTextLength; - - // The copied character buffer for measuring text. - // - // The length of this array is mTextLength. - private @Nullable char[] mCopiedBuffer; + // The inclusive start offset of the measuring target. + private final @IntRange(from = 0) int mStart; - // The whole paragraph direction. - private @Layout.Direction int mParaDir; + // The exclusive end offset of the measuring target. + private final @IntRange(from = 0) int mEnd; - // True if the text is LTR direction and doesn't contain any bidi characters. - private boolean mLtrWithoutBidi; + // The TextPaint used for measurement. + private final @NonNull TextPaint mPaint; - // The bidi level for individual characters. - // - // This is empty if mLtrWithoutBidi is true. - private @NonNull ByteArray mLevels = new ByteArray(); - - // The whole width of the text. - // See getWholeWidth comments. - private @FloatRange(from = 0.0f) float mWholeWidth; - - // Individual characters' widths. - // See getWidths comments. - private @Nullable FloatArray mWidths = new FloatArray(); - - // The span end positions. - // See getSpanEndCache comments. - private @Nullable IntArray mSpanEndCache = new IntArray(4); - - // The font metrics. - // See getFontMetrics comments. - private @Nullable IntArray mFontMetrics = new IntArray(4 * 4); - - // The native MeasuredText. - // See getNativePtr comments. - // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead. - private /* Maybe Zero */ long mNativePtr = 0; - private @Nullable Runnable mNativeObjectCleaner; - - // Associate the native object to this Java object. - private void bindNativeObject(/* Non Zero*/ long nativePtr) { - mNativePtr = nativePtr; - mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr); - } + // The requested text direction. + private final @NonNull TextDirectionHeuristic mTextDir; - // Decouple the native object from this Java object and release the native object. - private void unbindNativeObject() { - if (mNativePtr != 0) { - mNativeObjectCleaner.run(); - mNativePtr = 0; - } - } + // The measured paragraph texts. + private final @NonNull MeasuredParagraph[] mMeasuredParagraphs; - // Following two objects are for avoiding object allocation. - private @NonNull TextPaint mCachedPaint = new TextPaint(); - private @Nullable Paint.FontMetricsInt mCachedFm; - - /** - * Releases internal buffers. - */ - public void release() { - reset(); - mLevels.clearWithReleasingLargeArray(); - mWidths.clearWithReleasingLargeArray(); - mFontMetrics.clearWithReleasingLargeArray(); - mSpanEndCache.clearWithReleasingLargeArray(); - } - - /** - * Resets the internal state for starting new text. - */ - private void reset() { - mSpanned = null; - mCopiedBuffer = null; - mWholeWidth = 0; - mLevels.clear(); - mWidths.clear(); - mFontMetrics.clear(); - mSpanEndCache.clear(); - unbindNativeObject(); - } + // The sorted paragraph end offsets. + private final @NonNull int[] mParagraphBreakPoints; /** - * Returns the characters to be measured. + * Build MeasuredText from the text. * - * This is always available. + * @param text The text to be measured. + * @param paint The paint to be used for drawing. + * @param textDir The text direction. + * @return The measured text. */ - public @NonNull char[] getChars() { - return mCopiedBuffer; + public static @NonNull MeasuredText build(@NonNull CharSequence text, + @NonNull TextPaint paint, + @NonNull TextDirectionHeuristic textDir) { + return MeasuredText.build(text, paint, textDir, 0, text.length()); } /** - * Returns the paragraph direction. + * Build MeasuredText from the specific range of the text.. * - * This is always available. + * @param text The text to be measured. + * @param paint The paint to be used for drawing. + * @param textDir The text direction. + * @param start The inclusive start offset of the text. + * @param end The exclusive start offset of the text. + * @return The measured text. */ - public @Layout.Direction int getParagraphDir() { - return mParaDir; - } + public static @NonNull MeasuredText build(@NonNull CharSequence text, + @NonNull TextPaint paint, + @NonNull TextDirectionHeuristic textDir, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end) { + Preconditions.checkNotNull(text); + Preconditions.checkNotNull(paint); + Preconditions.checkNotNull(textDir); + Preconditions.checkArgumentInRange(start, 0, text.length(), "start"); + Preconditions.checkArgumentInRange(end, 0, text.length(), "end"); + + final IntArray paragraphEnds = new IntArray(); + final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>(); + + int paraEnd = 0; + for (int paraStart = start; paraStart < end; paraStart = paraEnd) { + paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end); + if (paraEnd < 0) { + // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end. + paraEnd = end; + } else { + paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. + } - /** - * Returns the directions. - * - * This is always available. - */ - public Directions getDirections(@IntRange(from = 0) int start, // inclusive - @IntRange(from = 0) int end) { // exclusive - if (mLtrWithoutBidi) { - return Layout.DIRS_ALL_LEFT_TO_RIGHT; + paragraphEnds.add(paraEnd); + measuredTexts.add(MeasuredParagraph.buildForStaticLayout( + paint, text, paraStart, paraEnd, textDir, null /* no recycle */)); } - final int length = end - start; - return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start, - length); + return new MeasuredText(text, start, end, paint, textDir, + measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]), + paragraphEnds.toArray()); } - /** - * Returns the whole text width. - * - * This is available only if the MeasureText is computed with computeForMeasurement. - * Returns 0 in other cases. - */ - public @FloatRange(from = 0.0f) float getWholeWidth() { - return mWholeWidth; + // Use MeasuredText.build instead. + private MeasuredText(@NonNull CharSequence text, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @NonNull TextPaint paint, + @NonNull TextDirectionHeuristic textDir, + @NonNull MeasuredParagraph[] measuredTexts, + @NonNull int[] paragraphBreakPoints) { + mText = text; + mStart = start; + mEnd = end; + mPaint = paint; + mMeasuredParagraphs = measuredTexts; + mParagraphBreakPoints = paragraphBreakPoints; + mTextDir = textDir; } /** - * Returns the individual character's width. - * - * This is available only if the MeasureText is computed with computeForMeasurement. - * Returns empty array in other cases. + * Return the underlying text. */ - public @NonNull FloatArray getWidths() { - return mWidths; + public @NonNull CharSequence getText() { + return mText; } /** - * Returns the MetricsAffectingSpan end indices. - * - * 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. - * Returns empty array in other cases. + * Returns the inclusive start offset of measured region. */ - public @NonNull IntArray getSpanEndCache() { - return mSpanEndCache; + public @IntRange(from = 0) int getStart() { + return mStart; } /** - * Returns the int array which holds FontMetrics. - * - * 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. - * Returns empty array in other cases. + * Returns the exclusive end offset of measured region. */ - public @NonNull IntArray getFontMetrics() { - return mFontMetrics; + public @IntRange(from = 0) int getEnd() { + return mEnd; } /** - * Returns the native ptr of the MeasuredText. - * - * This is available only if the MeasureText is computed with computeForStaticLayout. - * Returns 0 in other cases. + * Returns the text direction associated with char sequence. */ - public /* Maybe Zero */ long getNativePtr() { - return mNativePtr; + public @NonNull TextDirectionHeuristic getTextDir() { + return mTextDir; } /** - * Generates new MeasuredText for Bidi computation. - * - * If recycle is null, this returns new instance. If recycle is not null, this fills computed - * result to recycle and returns recycle. - * - * @param text the character sequence to be measured - * @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 - * @param textDir the text direction - * @param recycle pass existing MeasuredText if you want to recycle it. - * - * @return measured text + * Returns the paint used to measure this text. */ - public static @NonNull MeasuredText buildForBidi(@NonNull CharSequence text, - @IntRange(from = 0) int start, - @IntRange(from = 0) int end, - @NonNull TextDirectionHeuristic textDir, - @Nullable MeasuredText recycle) { - final MeasuredText mt = recycle == null ? obtain() : recycle; - mt.resetAndAnalyzeBidi(text, start, end, textDir); - return mt; + public @NonNull TextPaint getPaint() { + return mPaint; } /** - * Generates new MeasuredText for measuring texts. - * - * If recycle is null, this returns new instance. If recycle is not null, this fills computed - * result to recycle and returns recycle. - * - * @param paint the paint to be used for rendering the text. - * @param text the character sequence to be measured - * @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 - * @param textDir the text direction - * @param recycle pass existing MeasuredText if you want to recycle it. - * - * @return measured text + * Returns the length of the paragraph of this text. */ - public static @NonNull MeasuredText buildForMeasurement(@NonNull TextPaint paint, - @NonNull CharSequence text, - @IntRange(from = 0) int start, - @IntRange(from = 0) int end, - @NonNull TextDirectionHeuristic textDir, - @Nullable MeasuredText recycle) { - final MeasuredText mt = recycle == null ? obtain() : recycle; - mt.resetAndAnalyzeBidi(text, start, end, textDir); - - mt.mWidths.resize(mt.mTextLength); - if (mt.mTextLength == 0) { - return mt; - } - - if (mt.mSpanned == null) { - // No style change by MetricsAffectingSpan. Just measure all text. - mt.applyMetricsAffectingSpan( - paint, null /* spans */, start, end, 0 /* native static layout ptr */); - } else { - // There may be a MetricsAffectingSpan. Split into span transitions and apply styles. - int spanEnd; - for (int spanStart = start; spanStart < end; spanStart = spanEnd) { - spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class); - MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, - MetricAffectingSpan.class); - spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class); - mt.applyMetricsAffectingSpan( - paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */); - } - } - return mt; + public @IntRange(from = 0) int getParagraphCount() { + return mParagraphBreakPoints.length; } /** - * Generates new MeasuredText for StaticLayout. - * - * If recycle is null, this returns new instance. If recycle is not null, this fills computed - * result to recycle and returns recycle. - * - * @param paint the paint to be used for rendering the text. - * @param text the character sequence to be measured - * @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 - * @param textDir the text direction - * @param recycle pass existing MeasuredText if you want to recycle it. - * - * @return measured text + * Returns the paragraph start offset of the text. */ - public static @NonNull MeasuredText buildForStaticLayout( - @NonNull TextPaint paint, - @NonNull CharSequence text, - @IntRange(from = 0) int start, - @IntRange(from = 0) int end, - @NonNull TextDirectionHeuristic textDir, - @Nullable MeasuredText recycle) { - final MeasuredText mt = recycle == null ? obtain() : recycle; - mt.resetAndAnalyzeBidi(text, start, end, textDir); - if (mt.mTextLength == 0) { - // Need to build empty native measured text for StaticLayout. - // TODO: Stop creating empty measured text for empty lines. - long nativeBuilderPtr = nInitBuilder(); - try { - mt.bindNativeObject(nBuildNativeMeasuredText(nativeBuilderPtr, mt.mCopiedBuffer)); - } finally { - nFreeBuilder(nativeBuilderPtr); - } - return mt; - } - - long nativeBuilderPtr = nInitBuilder(); - try { - if (mt.mSpanned == null) { - // No style change by MetricsAffectingSpan. Just measure all text. - mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr); - mt.mSpanEndCache.append(end); - } else { - // There may be a MetricsAffectingSpan. Split into span transitions and apply - // styles. - int spanEnd; - for (int spanStart = start; spanStart < end; spanStart = spanEnd) { - spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, - MetricAffectingSpan.class); - MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd, - MetricAffectingSpan.class); - spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, - MetricAffectingSpan.class); - mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd, - nativeBuilderPtr); - mt.mSpanEndCache.append(spanEnd); - } - } - mt.bindNativeObject(nBuildNativeMeasuredText(nativeBuilderPtr, mt.mCopiedBuffer)); - } finally { - nFreeBuilder(nativeBuilderPtr); - } - - return mt; + public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) { + Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); + return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1]; } /** - * Reset internal state and analyzes text for bidirectional runs. - * - * @param text the character sequence to be measured - * @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 - * @param textDir the text direction + * Returns the paragraph end offset of the text. */ - private void resetAndAnalyzeBidi(@NonNull CharSequence text, - @IntRange(from = 0) int start, // inclusive - @IntRange(from = 0) int end, // exclusive - @NonNull TextDirectionHeuristic textDir) { - reset(); - mSpanned = text instanceof Spanned ? (Spanned) text : null; - mTextStart = start; - mTextLength = end - start; - - if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) { - mCopiedBuffer = new char[mTextLength]; - } - TextUtils.getChars(text, start, end, mCopiedBuffer, 0); - - // Replace characters associated with ReplacementSpan to U+FFFC. - if (mSpanned != null) { - ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class); - - for (int i = 0; i < spans.length; i++) { - int startInPara = mSpanned.getSpanStart(spans[i]) - start; - int endInPara = mSpanned.getSpanEnd(spans[i]) - start; - // The span interval may be larger and must be restricted to [start, end) - if (startInPara < 0) startInPara = 0; - if (endInPara > mTextLength) endInPara = mTextLength; - Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER); - } - } - - if ((textDir == TextDirectionHeuristics.LTR || - textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR || - textDir == TextDirectionHeuristics.ANYRTL_LTR) && - TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) { - mLevels.clear(); - mParaDir = Layout.DIR_LEFT_TO_RIGHT; - mLtrWithoutBidi = true; - } else { - final int bidiRequest; - if (textDir == TextDirectionHeuristics.LTR) { - bidiRequest = Layout.DIR_REQUEST_LTR; - } else if (textDir == TextDirectionHeuristics.RTL) { - bidiRequest = Layout.DIR_REQUEST_RTL; - } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) { - bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR; - } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) { - bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL; - } else { - final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength); - bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR; - } - mLevels.resize(mTextLength); - mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray()); - mLtrWithoutBidi = false; - } + public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) { + Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); + return mParagraphBreakPoints[paraIndex]; } - private void applyReplacementRun(@NonNull ReplacementSpan replacement, - @IntRange(from = 0) int start, // inclusive, in copied buffer - @IntRange(from = 0) int end, // exclusive, in copied buffer - /* Maybe Zero */ long nativeBuilderPtr) { - // Use original text. Shouldn't matter. - // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for - // backward compatibility? or Should we initialize them for getFontMetricsInt? - final float width = replacement.getSize( - mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm); - if (nativeBuilderPtr == 0) { - // Assigns all width to the first character. This is the same behavior as minikin. - mWidths.set(start, width); - if (end > start + 1) { - Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f); - } - mWholeWidth += width; - } else { - nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end, - width); - } + /** @hide */ + public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) { + return mMeasuredParagraphs[paraIndex]; } - private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer - @IntRange(from = 0) int end, // exclusive, in copied buffer - /* Maybe Zero */ long nativeBuilderPtr) { - if (nativeBuilderPtr != 0) { - mCachedPaint.getFontMetricsInt(mCachedFm); - } + /////////////////////////////////////////////////////////////////////////////////////////////// + // Spanned overrides + // + // Just proxy for underlying mText if appropriate. - if (mLtrWithoutBidi) { - // If the whole text is LTR direction, just apply whole region. - if (nativeBuilderPtr == 0) { - mWholeWidth += mCachedPaint.getTextRunAdvances( - mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */, - mWidths.getRawArray(), start); - } else { - nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end, - false /* isRtl */); - } + @Override + public <T> T[] getSpans(int start, int end, Class<T> type) { + if (mText instanceof Spanned) { + return ((Spanned) mText).getSpans(start, end, type); } else { - // If there is multiple bidi levels, split into individual bidi level and apply style. - byte level = mLevels.get(start); - // Note that the empty text or empty range won't reach this method. - // Safe to search from start + 1. - for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) { - if (levelEnd == end || mLevels.get(levelEnd) != level) { // transition point - final boolean isRtl = (level & 0x1) != 0; - if (nativeBuilderPtr == 0) { - final int levelLength = levelEnd - levelStart; - mWholeWidth += mCachedPaint.getTextRunAdvances( - mCopiedBuffer, levelStart, levelLength, levelStart, levelLength, - isRtl, mWidths.getRawArray(), levelStart); - } else { - nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart, - levelEnd, isRtl); - } - if (levelEnd == end) { - break; - } - levelStart = levelEnd; - level = mLevels.get(levelEnd); - } - } + return ArrayUtils.emptyArray(type); } } - private void applyMetricsAffectingSpan( - @NonNull TextPaint paint, - @Nullable MetricAffectingSpan[] spans, - @IntRange(from = 0) int start, // inclusive, in original text buffer - @IntRange(from = 0) int end, // exclusive, in original text buffer - /* Maybe Zero */ long nativeBuilderPtr) { - mCachedPaint.set(paint); - // XXX paint should not have a baseline shift, but... - mCachedPaint.baselineShift = 0; - - final boolean needFontMetrics = nativeBuilderPtr != 0; - - if (needFontMetrics && mCachedFm == null) { - mCachedFm = new Paint.FontMetricsInt(); - } - - ReplacementSpan replacement = null; - if (spans != null) { - for (int i = 0; i < spans.length; i++) { - MetricAffectingSpan span = spans[i]; - if (span instanceof ReplacementSpan) { - // The last ReplacementSpan is effective for backward compatibility reasons. - replacement = (ReplacementSpan) span; - } else { - // TODO: No need to call updateMeasureState for ReplacementSpan as well? - span.updateMeasureState(mCachedPaint); - } - } - } - - final int startInCopiedBuffer = start - mTextStart; - final int endInCopiedBuffer = end - mTextStart; - - if (replacement != null) { - applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, - nativeBuilderPtr); + @Override + public int getSpanStart(Object tag) { + if (mText instanceof Spanned) { + return ((Spanned) mText).getSpanStart(tag); } else { - applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr); + return -1; } + } - if (needFontMetrics) { - if (mCachedPaint.baselineShift < 0) { - mCachedFm.ascent += mCachedPaint.baselineShift; - mCachedFm.top += mCachedPaint.baselineShift; - } else { - mCachedFm.descent += mCachedPaint.baselineShift; - mCachedFm.bottom += mCachedPaint.baselineShift; - } - - mFontMetrics.append(mCachedFm.top); - mFontMetrics.append(mCachedFm.bottom); - mFontMetrics.append(mCachedFm.ascent); - mFontMetrics.append(mCachedFm.descent); + @Override + public int getSpanEnd(Object tag) { + if (mText instanceof Spanned) { + return ((Spanned) mText).getSpanEnd(tag); + } else { + return -1; } } - /** - * Returns the maximum index that the accumulated width not exceeds the width. - * - * If forward=false is passed, returns the minimum index from the end instead. - * - * This only works if the MeasuredText is computed with computeForMeasurement. - * Undefined behavior in other case. - */ - @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) { - float[] w = mWidths.getRawArray(); - if (forwards) { - int i = 0; - while (i < limit) { - width -= w[i]; - if (width < 0.0f) break; - i++; - } - while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--; - return i; + @Override + public int getSpanFlags(Object tag) { + if (mText instanceof Spanned) { + return ((Spanned) mText).getSpanFlags(tag); } else { - int i = limit - 1; - while (i >= 0) { - width -= w[i]; - if (width < 0.0f) break; - i--; - } - while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) { - i++; - } - return limit - i - 1; + return 0; } } - /** - * Returns the length of the substring. - * - * This only works if the MeasuredText is computed with computeForMeasurement. - * Undefined behavior in other case. - */ - @FloatRange(from = 0.0f) float measure(int start, int limit) { - float width = 0; - float[] w = mWidths.getRawArray(); - for (int i = start; i < limit; ++i) { - width += w[i]; + @Override + public int nextSpanTransition(int start, int limit, Class type) { + if (mText instanceof Spanned) { + return ((Spanned) mText).nextSpanTransition(start, limit, type); + } else { + return mText.length(); } - return width; } - private static native /* Non Zero */ long nInitBuilder(); - - /** - * Apply style to make native measured text. - * - * @param nativeBuilderPtr The native MeasuredText builder pointer. - * @param paintPtr The native paint pointer to be applied. - * @param start The start offset in the copied buffer. - * @param end The end offset in the copied buffer. - * @param isRtl True if the text is RTL. - */ - private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr, - /* Non Zero */ long paintPtr, - @IntRange(from = 0) int start, - @IntRange(from = 0) int end, - boolean isRtl); + /////////////////////////////////////////////////////////////////////////////////////////////// + // CharSequence overrides. + // + // Just proxy for underlying mText. - /** - * Apply ReplacementRun to make native measured text. - * - * @param nativeBuilderPtr The native MeasuredText builder pointer. - * @param paintPtr The native paint pointer to be applied. - * @param start The start offset in the copied buffer. - * @param end The end offset in the copied buffer. - * @param width The width of the replacement. - */ - private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr, - /* Non Zero */ long paintPtr, - @IntRange(from = 0) int start, - @IntRange(from = 0) int end, - @FloatRange(from = 0) float width); + @Override + public int length() { + return mText.length(); + } - private static native long nBuildNativeMeasuredText(/* Non Zero */ long nativeBuilderPtr, - @NonNull char[] text); + @Override + public char charAt(int index) { + // TODO: Should this be index + mStart ? + return mText.charAt(index); + } - private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr); + @Override + public CharSequence subSequence(int start, int end) { + // TODO: return MeasuredText. + // TODO: Should this be index + mStart, end + mStart ? + return mText.subSequence(start, end); + } - @CriticalNative - private static native /* Non Zero */ long nGetReleaseFunc(); + @Override + public String toString() { + return mText.toString(); + } } diff --git a/core/java/android/text/PremeasuredText.java b/core/java/android/text/PremeasuredText.java deleted file mode 100644 index 465314dd21ac..000000000000 --- a/core/java/android/text/PremeasuredText.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.text; - -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.util.IntArray; - -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.Preconditions; - -import java.util.ArrayList; - -/** - * A text which has already been measured. - * - * TODO: Rename to better name? e.g. MeasuredText, FrozenText etc. - */ -public class PremeasuredText implements Spanned { - private static final char LINE_FEED = '\n'; - - // The original text. - private final @NonNull CharSequence mText; - - // The inclusive start offset of the measuring target. - private final @IntRange(from = 0) int mStart; - - // The exclusive end offset of the measuring target. - private final @IntRange(from = 0) int mEnd; - - // The TextPaint used for measurement. - private final @NonNull TextPaint mPaint; - - // The requested text direction. - private final @NonNull TextDirectionHeuristic mTextDir; - - // The measured paragraph texts. - private final @NonNull MeasuredText[] mMeasuredTexts; - - // The sorted paragraph end offsets. - private final @NonNull int[] mParagraphBreakPoints; - - /** - * Build PremeasuredText from the text. - * - * @param text The text to be measured. - * @param paint The paint to be used for drawing. - * @param textDir The text direction. - * @return The measured text. - */ - public static @NonNull PremeasuredText build(@NonNull CharSequence text, - @NonNull TextPaint paint, - @NonNull TextDirectionHeuristic textDir) { - return PremeasuredText.build(text, paint, textDir, 0, text.length()); - } - - /** - * Build PremeasuredText from the specific range of the text.. - * - * @param text The text to be measured. - * @param paint The paint to be used for drawing. - * @param textDir The text direction. - * @param start The inclusive start offset of the text. - * @param end The exclusive start offset of the text. - * @return The measured text. - */ - public static @NonNull PremeasuredText build(@NonNull CharSequence text, - @NonNull TextPaint paint, - @NonNull TextDirectionHeuristic textDir, - @IntRange(from = 0) int start, - @IntRange(from = 0) int end) { - Preconditions.checkNotNull(text); - Preconditions.checkNotNull(paint); - Preconditions.checkNotNull(textDir); - Preconditions.checkArgumentInRange(start, 0, text.length(), "start"); - Preconditions.checkArgumentInRange(end, 0, text.length(), "end"); - - final IntArray paragraphEnds = new IntArray(); - final ArrayList<MeasuredText> measuredTexts = new ArrayList<>(); - - int paraEnd = 0; - for (int paraStart = start; paraStart < end; paraStart = paraEnd) { - paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end); - if (paraEnd < 0) { - // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end. - paraEnd = end; - } else { - paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. - } - - paragraphEnds.add(paraEnd); - measuredTexts.add(MeasuredText.buildForStaticLayout( - paint, text, paraStart, paraEnd, textDir, null /* no recycle */)); - } - - return new PremeasuredText(text, start, end, paint, textDir, - measuredTexts.toArray(new MeasuredText[measuredTexts.size()]), - paragraphEnds.toArray()); - } - - // Use PremeasuredText.build instead. - private PremeasuredText(@NonNull CharSequence text, - @IntRange(from = 0) int start, - @IntRange(from = 0) int end, - @NonNull TextPaint paint, - @NonNull TextDirectionHeuristic textDir, - @NonNull MeasuredText[] measuredTexts, - @NonNull int[] paragraphBreakPoints) { - mText = text; - mStart = start; - mEnd = end; - mPaint = paint; - mMeasuredTexts = measuredTexts; - mParagraphBreakPoints = paragraphBreakPoints; - mTextDir = textDir; - } - - /** - * Return the underlying text. - */ - public @NonNull CharSequence getText() { - return mText; - } - - /** - * Returns the inclusive start offset of measured region. - */ - public @IntRange(from = 0) int getStart() { - return mStart; - } - - /** - * Returns the exclusive end offset of measured region. - */ - public @IntRange(from = 0) int getEnd() { - return mEnd; - } - - /** - * Returns the text direction associated with char sequence. - */ - public @NonNull TextDirectionHeuristic getTextDir() { - return mTextDir; - } - - /** - * Returns the paint used to measure this text. - */ - public @NonNull TextPaint getPaint() { - return mPaint; - } - - /** - * Returns the length of the paragraph of this text. - */ - public @IntRange(from = 0) int getParagraphCount() { - return mParagraphBreakPoints.length; - } - - /** - * Returns the paragraph start offset of the text. - */ - public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) { - Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); - return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1]; - } - - /** - * Returns the paragraph end offset of the text. - */ - public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) { - Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); - return mParagraphBreakPoints[paraIndex]; - } - - /** @hide */ - public @NonNull MeasuredText getMeasuredText(@IntRange(from = 0) int paraIndex) { - return mMeasuredTexts[paraIndex]; - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Spanned overrides - // - // Just proxy for underlying mText if appropriate. - - @Override - public <T> T[] getSpans(int start, int end, Class<T> type) { - if (mText instanceof Spanned) { - return ((Spanned) mText).getSpans(start, end, type); - } else { - return ArrayUtils.emptyArray(type); - } - } - - @Override - public int getSpanStart(Object tag) { - if (mText instanceof Spanned) { - return ((Spanned) mText).getSpanStart(tag); - } else { - return -1; - } - } - - @Override - public int getSpanEnd(Object tag) { - if (mText instanceof Spanned) { - return ((Spanned) mText).getSpanEnd(tag); - } else { - return -1; - } - } - - @Override - public int getSpanFlags(Object tag) { - if (mText instanceof Spanned) { - return ((Spanned) mText).getSpanFlags(tag); - } else { - return 0; - } - } - - @Override - public int nextSpanTransition(int start, int limit, Class type) { - if (mText instanceof Spanned) { - return ((Spanned) mText).nextSpanTransition(start, limit, type); - } else { - return mText.length(); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // CharSequence overrides. - // - // Just proxy for underlying mText. - - @Override - public int length() { - return mText.length(); - } - - @Override - public char charAt(int index) { - // TODO: Should this be index + mStart ? - return mText.charAt(index); - } - - @Override - public CharSequence subSequence(int start, int end) { - // TODO: return PremeasuredText. - // TODO: Should this be index + mStart, end + mStart ? - return mText.subSequence(start, end); - } - - @Override - public String toString() { - return mText.toString(); - } -} diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index d69b1190140f..36bec863e7ac 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -55,7 +55,8 @@ public class StaticLayout extends Layout { * First, call nInit to setup native line breaker object. Then, for each paragraph, do the * following: * - * - Create MeasuredText by MeasuredText.buildForStaticLayout which measures in native. + * - Create MeasuredParagraph by MeasuredParagraph.buildForStaticLayout which measures in + * native. * - Run nComputeLineBreaks() to obtain line breaks for the paragraph. * * After all paragraphs, call finish() to release expensive buffers. @@ -650,34 +651,34 @@ public class StaticLayout extends Layout { b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, indents, mLeftPaddings, mRightPaddings); - PremeasuredText premeasured = null; + MeasuredText measured = null; final Spanned spanned; - if (source instanceof PremeasuredText) { - premeasured = (PremeasuredText) source; + if (source instanceof MeasuredText) { + measured = (MeasuredText) source; - final CharSequence original = premeasured.getText(); + final CharSequence original = measured.getText(); spanned = (original instanceof Spanned) ? (Spanned) original : null; - if (bufStart != premeasured.getStart() || bufEnd != premeasured.getEnd()) { + if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) { // The buffer position has changed. Re-measure here. - premeasured = PremeasuredText.build(original, paint, textDir, bufStart, bufEnd); + measured = MeasuredText.build(original, paint, textDir, bufStart, bufEnd); } else { - // We can use premeasured information. + // We can use measured information. - // Overwrite with the one when premeasured. + // Overwrite with the one when emeasured. // TODO: Give an option for developer not to overwrite and measure again here? - textDir = premeasured.getTextDir(); - paint = premeasured.getPaint(); + textDir = measured.getTextDir(); + paint = measured.getPaint(); } } else { - premeasured = PremeasuredText.build(source, paint, textDir, bufStart, bufEnd); + measured = MeasuredText.build(source, paint, textDir, bufStart, bufEnd); spanned = (source instanceof Spanned) ? (Spanned) source : null; } try { - for (int paraIndex = 0; paraIndex < premeasured.getParagraphCount(); paraIndex++) { - final int paraStart = premeasured.getParagraphStart(paraIndex); - final int paraEnd = premeasured.getParagraphEnd(paraIndex); + for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) { + final int paraStart = measured.getParagraphStart(paraIndex); + final int paraEnd = measured.getParagraphEnd(paraIndex); int firstWidthLineCount = 1; int firstWidth = outerWidth; @@ -743,10 +744,10 @@ public class StaticLayout extends Layout { } } - final MeasuredText measured = premeasured.getMeasuredText(paraIndex); - final char[] chs = measured.getChars(); - final int[] spanEndCache = measured.getSpanEndCache().getRawArray(); - final int[] fmCache = measured.getFontMetrics().getRawArray(); + final MeasuredParagraph measuredPara = measured.getMeasuredParagraph(paraIndex); + final char[] chs = measuredPara.getChars(); + final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray(); + final int[] fmCache = measuredPara.getFontMetrics().getRawArray(); // TODO: Stop keeping duplicated width copy in native and Java. widths.resize(chs.length); @@ -759,7 +760,7 @@ public class StaticLayout extends Layout { // Inputs chs, - measured.getNativePtr(), + measuredPara.getNativePtr(), paraEnd - paraStart, firstWidth, firstWidthLineCount, @@ -863,7 +864,7 @@ public class StaticLayout extends Layout { v = out(source, here, endPos, ascent, descent, fmTop, fmBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, - flags[breakIndex], needMultiply, measured, bufEnd, + flags[breakIndex], needMultiply, measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(), paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, moreChars); @@ -894,8 +895,8 @@ public class StaticLayout extends Layout { if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) && mLineCount < mMaximumVisibleLineCount) { - final MeasuredText measured = - MeasuredText.buildForBidi(source, bufEnd, bufEnd, textDir, null); + final MeasuredParagraph measuredPara = + MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null); paint.getFontMetricsInt(fm); v = out(source, bufEnd, bufEnd, fm.ascent, fm.descent, @@ -903,7 +904,7 @@ public class StaticLayout extends Layout { v, spacingmult, spacingadd, null, null, fm, 0, - needMultiply, measured, bufEnd, + needMultiply, measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, null, null, bufStart, ellipsize, ellipsizedWidth, 0, paint, false); @@ -918,7 +919,7 @@ public class StaticLayout extends Layout { private int out(final CharSequence text, final int start, final int end, int above, int below, int top, int bottom, int v, final float spacingmult, final float spacingadd, final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm, - final int flags, final boolean needMultiply, @NonNull final MeasuredText measured, + final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured, final int bufEnd, final boolean includePad, final boolean trackPad, final boolean addLastLineLineSpacing, final char[] chs, final float[] widths, final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth, diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 9c9fbf23832f..409e51438d20 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -1250,10 +1250,10 @@ public class TextUtils { @NonNull String ellipsis) { final int len = text.length(); - MeasuredText mt = null; - MeasuredText resultMt = null; + MeasuredParagraph mt = null; + MeasuredParagraph resultMt = null; try { - mt = MeasuredText.buildForMeasurement(paint, text, 0, text.length(), textDir, mt); + mt = MeasuredParagraph.buildForMeasurement(paint, text, 0, text.length(), textDir, mt); float width = mt.getWholeWidth(); if (width <= avail) { @@ -1332,7 +1332,7 @@ public class TextUtils { if (remaining == 0) { // All text is gone. textFits = true; } else { - resultMt = MeasuredText.buildForMeasurement( + resultMt = MeasuredParagraph.buildForMeasurement( paint, result, 0, result.length(), textDir, resultMt); width = resultMt.getWholeWidth(); if (width <= avail) { @@ -1479,11 +1479,11 @@ public class TextUtils { public static CharSequence commaEllipsize(CharSequence text, TextPaint p, float avail, String oneMore, String more, TextDirectionHeuristic textDir) { - MeasuredText mt = null; - MeasuredText tempMt = null; + MeasuredParagraph mt = null; + MeasuredParagraph tempMt = null; try { int len = text.length(); - mt = MeasuredText.buildForMeasurement(p, text, 0, len, textDir, mt); + mt = MeasuredParagraph.buildForMeasurement(p, text, 0, len, textDir, mt); final float width = mt.getWholeWidth(); if (width <= avail) { return text; @@ -1523,7 +1523,7 @@ public class TextUtils { } // XXX this is probably ok, but need to look at it more - tempMt = MeasuredText.buildForMeasurement( + tempMt = MeasuredParagraph.buildForMeasurement( p, format, 0, format.length(), textDir, tempMt); float moreWid = tempMt.getWholeWidth(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index ff374fa90d1c..42c9eeb93eca 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -77,8 +77,8 @@ import android.text.GraphicsOperations; import android.text.InputFilter; import android.text.InputType; import android.text.Layout; +import android.text.MeasuredText; import android.text.ParcelableSpan; -import android.text.PremeasuredText; import android.text.Selection; import android.text.SpanWatcher; import android.text.Spannable; @@ -5393,7 +5393,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (imm != null) imm.restartInput(this); } else if (type == BufferType.SPANNABLE || mMovement != null) { text = mSpannableFactory.newSpannable(text); - } else if (!(text instanceof PremeasuredText || text instanceof CharWrapper)) { + } else if (!(text instanceof MeasuredText || text instanceof CharWrapper)) { text = TextUtils.stringOrSpannedString(text); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index b3f66e9652f6..96f3308ec178 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -84,7 +84,7 @@ cc_library_shared { "android_view_VelocityTracker.cpp", "android_text_AndroidCharacter.cpp", "android_text_Hyphenator.cpp", - "android_text_MeasuredText.cpp", + "android_text_MeasuredParagraph.cpp", "android_text_StaticLayout.cpp", "android_os_Debug.cpp", "android_os_GraphicsEnvironment.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 6d7fe056acdf..6569b4783e67 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -178,7 +178,7 @@ extern int register_android_net_LocalSocketImpl(JNIEnv* env); extern int register_android_net_NetworkUtils(JNIEnv* env); extern int register_android_text_AndroidCharacter(JNIEnv *env); extern int register_android_text_Hyphenator(JNIEnv *env); -extern int register_android_text_MeasuredText(JNIEnv* env); +extern int register_android_text_MeasuredParagraph(JNIEnv* env); extern int register_android_text_StaticLayout(JNIEnv *env); extern int register_android_opengl_classes(JNIEnv *env); extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); @@ -1342,7 +1342,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_XmlBlock), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), - REG_JNI(register_android_text_MeasuredText), + REG_JNI(register_android_text_MeasuredParagraph), REG_JNI(register_android_text_StaticLayout), REG_JNI(register_android_view_InputDevice), REG_JNI(register_android_view_KeyCharacterMap), diff --git a/core/jni/android_text_MeasuredText.cpp b/core/jni/android_text_MeasuredParagraph.cpp index af9d13122201..bdae0b22987f 100644 --- a/core/jni/android_text_MeasuredText.cpp +++ b/core/jni/android_text_MeasuredParagraph.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "MeasuredText" +#define LOG_TAG "MeasuredParagraph" #include "ScopedIcuLocale.h" #include "unicode/locid.h" @@ -49,7 +49,7 @@ static inline Paint* toPaint(jlong ptr) { return reinterpret_cast<Paint*>(ptr); } -static inline minikin::MeasuredText* toMeasuredText(jlong ptr) { +static inline minikin::MeasuredText* toMeasuredParagraph(jlong ptr) { return reinterpret_cast<minikin::MeasuredText*>(ptr); } @@ -57,8 +57,8 @@ template<typename Ptr> static inline jlong toJLong(Ptr ptr) { return reinterpret_cast<jlong>(ptr); } -static void releaseMeasuredText(jlong measuredTextPtr) { - delete toMeasuredText(measuredTextPtr); +static void releaseMeasuredParagraph(jlong measuredTextPtr) { + delete toMeasuredParagraph(measuredTextPtr); } // Regular JNI @@ -84,7 +84,7 @@ static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong } // Regular JNI -static jlong nBuildNativeMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr, +static jlong nBuildNativeMeasuredParagraph(JNIEnv* env, jclass /* unused */, jlong builderPtr, jcharArray javaText) { ScopedCharArrayRO text(env, javaText); const minikin::U16StringPiece textBuffer(text.get(), text.size()); @@ -100,23 +100,23 @@ static void nFreeBuilder(JNIEnv* env, jclass /* unused */, jlong builderPtr) { // CriticalNative static jlong nGetReleaseFunc() { - return toJLong(&releaseMeasuredText); + return toJLong(&releaseMeasuredParagraph); } static const JNINativeMethod gMethods[] = { - // MeasuredTextBuilder native functions. + // MeasuredParagraphBuilder native functions. {"nInitBuilder", "()J", (void*) nInitBuilder}, {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun}, {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun}, - {"nBuildNativeMeasuredText", "(J[C)J", (void*) nBuildNativeMeasuredText}, + {"nBuildNativeMeasuredParagraph", "(J[C)J", (void*) nBuildNativeMeasuredParagraph}, {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, - // MeasuredText native functions. + // MeasuredParagraph native functions. {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives }; -int register_android_text_MeasuredText(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/text/MeasuredText", gMethods, NELEM(gMethods)); +int register_android_text_MeasuredParagraph(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/text/MeasuredParagraph", gMethods, NELEM(gMethods)); } } diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp index b5c23dfa873d..682dc8739869 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_StaticLayout.cpp @@ -174,7 +174,7 @@ static const JNINativeMethod gMethods[] = { // Inputs "[C" // text - "J" // MeasuredText ptr. + "J" // MeasuredParagraph ptr. "I" // length "F" // firstWidth "I" // firstWidthLineCount diff --git a/core/tests/coretests/src/android/text/MeasuredTextTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java index ddef0c651b31..5d33397e13f2 100644 --- a/core/tests/coretests/src/android/text/MeasuredTextTest.java +++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java @@ -31,7 +31,7 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) -public class MeasuredTextTest { +public class MeasuredParagraphTest { private static final TextDirectionHeuristic LTR = TextDirectionHeuristics.LTR; private static final TextDirectionHeuristic RTL = TextDirectionHeuristics.RTL; @@ -60,9 +60,9 @@ public class MeasuredTextTest { @Test public void buildForBidi() { - MeasuredText mt = null; + MeasuredParagraph mt = null; - mt = MeasuredText.buildForBidi("XXX", 0, 3, LTR, null); + mt = MeasuredParagraph.buildForBidi("XXX", 0, 3, LTR, null); assertNotNull(mt); assertNotNull(mt.getChars()); assertEquals("XXX", charsToString(mt.getChars())); @@ -75,7 +75,7 @@ public class MeasuredTextTest { assertEquals(0, mt.getNativePtr()); // Recycle it - MeasuredText mt2 = MeasuredText.buildForBidi("_VVV_", 1, 4, RTL, mt); + MeasuredParagraph mt2 = MeasuredParagraph.buildForBidi("_VVV_", 1, 4, RTL, mt); assertEquals(mt2, mt); assertNotNull(mt2.getChars()); assertEquals("VVV", charsToString(mt.getChars())); @@ -91,9 +91,9 @@ public class MeasuredTextTest { @Test public void buildForMeasurement() { - MeasuredText mt = null; + MeasuredParagraph mt = null; - mt = MeasuredText.buildForMeasurement(PAINT, "XXX", 0, 3, LTR, null); + mt = MeasuredParagraph.buildForMeasurement(PAINT, "XXX", 0, 3, LTR, null); assertNotNull(mt); assertNotNull(mt.getChars()); assertEquals("XXX", charsToString(mt.getChars())); @@ -109,7 +109,8 @@ public class MeasuredTextTest { assertEquals(0, mt.getNativePtr()); // Recycle it - MeasuredText mt2 = MeasuredText.buildForMeasurement(PAINT, "_VVV_", 1, 4, RTL, mt); + MeasuredParagraph mt2 = + MeasuredParagraph.buildForMeasurement(PAINT, "_VVV_", 1, 4, RTL, mt); assertEquals(mt2, mt); assertNotNull(mt2.getChars()); assertEquals("VVV", charsToString(mt.getChars())); @@ -129,9 +130,9 @@ public class MeasuredTextTest { @Test public void buildForStaticLayout() { - MeasuredText mt = null; + MeasuredParagraph mt = null; - mt = MeasuredText.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null); + mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null); assertNotNull(mt); assertNotNull(mt.getChars()); assertEquals("XXX", charsToString(mt.getChars())); @@ -145,7 +146,8 @@ public class MeasuredTextTest { assertNotEquals(0, mt.getNativePtr()); // Recycle it - MeasuredText mt2 = MeasuredText.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt); + MeasuredParagraph mt2 = + MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt); assertEquals(mt2, mt); assertNotNull(mt2.getChars()); assertEquals("VVV", charsToString(mt.getChars())); @@ -163,6 +165,6 @@ public class MeasuredTextTest { @Test public void testFor70146381() { - MeasuredText.buildForMeasurement(PAINT, "X…", 0, 2, RTL, null); + MeasuredParagraph.buildForMeasurement(PAINT, "X…", 0, 2, RTL, null); } } |