Loading core/java/android/text/BoringLayout.java +8 −1 Original line number Diff line number Diff line Loading @@ -347,7 +347,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback TextLine line = TextLine.obtain(); line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT, Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null); if (text instanceof MeasuredText) { MeasuredText mt = (MeasuredText) text; // Reaching here means there is only one paragraph. MeasuredParagraph mp = mt.getMeasuredParagraph(0); fm.width = (int) Math.ceil(mp.getWidth(0, mp.getTextLength())); } else { fm.width = (int) Math.ceil(line.metrics(fm)); } TextLine.recycle(line); return fm; Loading core/java/android/text/MeasuredParagraph.java +50 −10 Original line number Diff line number Diff line Loading @@ -175,6 +175,15 @@ public class MeasuredParagraph { unbindNativeObject(); } /** * Returns the length of the paragraph. * * This is always available. */ public int getTextLength() { return mTextLength; } /** * Returns the characters to be measured. * Loading Loading @@ -212,7 +221,7 @@ public class MeasuredParagraph { /** * 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() { Loading @@ -222,7 +231,7 @@ public class MeasuredParagraph { /** * 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() { Loading @@ -234,7 +243,7 @@ public class MeasuredParagraph { * * 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() { Loading @@ -246,7 +255,7 @@ public class MeasuredParagraph { * * 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() { Loading @@ -256,13 +265,37 @@ public class MeasuredParagraph { /** * 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() { return mNativePtr; } /** * 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. * Loading Loading @@ -357,6 +390,7 @@ public class MeasuredParagraph { @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); Loading @@ -367,7 +401,7 @@ public class MeasuredParagraph { try { mt.bindNativeObject( nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer, computeHyphenation)); computeHyphenation, computeLayout)); } finally { nFreeBuilder(nativeBuilderPtr); } Loading Loading @@ -397,7 +431,7 @@ public class MeasuredParagraph { } } mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer, computeHyphenation)); computeHyphenation, computeLayout)); } finally { nFreeBuilder(nativeBuilderPtr); } Loading Loading @@ -595,7 +629,7 @@ public class MeasuredParagraph { * * 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) { Loading Loading @@ -626,7 +660,7 @@ public class MeasuredParagraph { /** * 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) { Loading Loading @@ -672,10 +706,16 @@ public class MeasuredParagraph { 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(); } core/java/android/text/MeasuredText.java +52 −2 Original line number Diff line number Diff line Loading @@ -155,6 +155,11 @@ public class MeasuredText implements Spanned { * @return the measured text. */ public @NonNull MeasuredText build() { return build(true /* build full layout result */); } /** @hide */ public @NonNull MeasuredText build(boolean computeLayout) { final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE; Loading @@ -175,7 +180,7 @@ public class MeasuredText implements Spanned { paragraphEnds.add(paraEnd); measuredTexts.add(MeasuredParagraph.buildForStaticLayout( mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation, null /* no recycle */)); computeLayout, null /* no recycle */)); } return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy, Loading @@ -198,7 +203,8 @@ public class MeasuredText implements Spanned { mText = text; mStart = start; mEnd = end; mPaint = paint; // Copy the paint so that we can keep the reference of typeface in native layout result. mPaint = new TextPaint(paint); mMeasuredParagraphs = measuredTexts; mParagraphBreakPoints = paragraphBreakPoints; mTextDir = textDir; Loading Loading @@ -283,6 +289,50 @@ public class MeasuredText implements Spanned { return mHyphenationFrequency; } /** * Returns true if the given TextPaint gives the same result of text layout for this text. * @hide */ public boolean canUseMeasuredResult(@NonNull TextPaint paint) { return mPaint.getTextSize() == paint.getTextSize() && mPaint.getTextSkewX() == paint.getTextSkewX() && mPaint.getTextScaleX() == paint.getTextScaleX() && mPaint.getLetterSpacing() == paint.getLetterSpacing() && mPaint.getWordSpacing() == paint.getWordSpacing() && mPaint.getFlags() == paint.getFlags() // Maybe not all flag affects text layout. && mPaint.getTextLocales() == paint.getTextLocales() // need to be equals? && mPaint.getFontVariationSettings() == paint.getFontVariationSettings() && mPaint.getTypeface() == paint.getTypeface() && TextUtils.equals(mPaint.getFontFeatureSettings(), paint.getFontFeatureSettings()); } /** @hide */ public int findParaIndex(@IntRange(from = 0) int pos) { // TODO: Maybe good to remove paragraph concept from MeasuredText and add substring layout // support to StaticLayout. for (int i = 0; i < mParagraphBreakPoints.length; ++i) { if (pos < mParagraphBreakPoints[i]) { return i; } } throw new IndexOutOfBoundsException( "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1] + ", gave " + pos); } /** @hide */ public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) { final int paraIndex = findParaIndex(start); final int paraStart = getParagraphStart(paraIndex); final int paraEnd = getParagraphEnd(paraIndex); if (start < paraStart || paraEnd < end) { throw new RuntimeException("Cannot measured across the paragraph:" + "para: (" + paraStart + ", " + paraEnd + "), " + "request: (" + start + ", " + end + ")"); } return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart); } /////////////////////////////////////////////////////////////////////////////////////////////// // Spanned overrides // Loading core/java/android/text/StaticLayout.java +1 −1 Original line number Diff line number Diff line Loading @@ -678,7 +678,7 @@ public class StaticLayout extends Layout { .setTextDirection(textDir) .setBreakStrategy(b.mBreakStrategy) .setHyphenationFrequency(b.mHyphenationFrequency) .build(); .build(false /* full layout is not necessary for line breaking */); spanned = (source instanceof Spanned) ? (Spanned) source : null; } else { final CharSequence original = measured.getText(); Loading core/java/android/text/TextLine.java +17 −2 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ public class TextLine { private char[] mChars; private boolean mCharsValid; private Spanned mSpanned; private MeasuredText mMeasured; // Additional width of whitespace for justification. This value is per whitespace, thus // the line width will increase by mAddedWidth x (number of stretchable whitespaces). Loading Loading @@ -118,6 +119,7 @@ public class TextLine { tl.mSpanned = null; tl.mTabs = null; tl.mChars = null; tl.mMeasured = null; tl.mMetricAffectingSpanSpanSet.recycle(); tl.mCharacterStyleSpanSet.recycle(); Loading Loading @@ -168,6 +170,14 @@ public class TextLine { hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; } mMeasured = null; if (text instanceof MeasuredText) { MeasuredText mt = (MeasuredText) text; if (mt.canUseMeasuredResult(paint)) { mMeasured = mt; } } mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; if (mCharsValid) { Loading Loading @@ -736,8 +746,13 @@ public class TextLine { return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset); } else { final int delta = mStart; if (mMeasured == null) { // TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text. return wp.getRunAdvance(mText, delta + start, delta + end, delta + contextStart, delta + contextEnd, runIsRtl, delta + offset); } else { return mMeasured.getWidth(start + delta, end + delta); } } } Loading Loading
core/java/android/text/BoringLayout.java +8 −1 Original line number Diff line number Diff line Loading @@ -347,7 +347,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback TextLine line = TextLine.obtain(); line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT, Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null); if (text instanceof MeasuredText) { MeasuredText mt = (MeasuredText) text; // Reaching here means there is only one paragraph. MeasuredParagraph mp = mt.getMeasuredParagraph(0); fm.width = (int) Math.ceil(mp.getWidth(0, mp.getTextLength())); } else { fm.width = (int) Math.ceil(line.metrics(fm)); } TextLine.recycle(line); return fm; Loading
core/java/android/text/MeasuredParagraph.java +50 −10 Original line number Diff line number Diff line Loading @@ -175,6 +175,15 @@ public class MeasuredParagraph { unbindNativeObject(); } /** * Returns the length of the paragraph. * * This is always available. */ public int getTextLength() { return mTextLength; } /** * Returns the characters to be measured. * Loading Loading @@ -212,7 +221,7 @@ public class MeasuredParagraph { /** * 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() { Loading @@ -222,7 +231,7 @@ public class MeasuredParagraph { /** * 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() { Loading @@ -234,7 +243,7 @@ public class MeasuredParagraph { * * 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() { Loading @@ -246,7 +255,7 @@ public class MeasuredParagraph { * * 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() { Loading @@ -256,13 +265,37 @@ public class MeasuredParagraph { /** * 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() { return mNativePtr; } /** * 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. * Loading Loading @@ -357,6 +390,7 @@ public class MeasuredParagraph { @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); Loading @@ -367,7 +401,7 @@ public class MeasuredParagraph { try { mt.bindNativeObject( nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer, computeHyphenation)); computeHyphenation, computeLayout)); } finally { nFreeBuilder(nativeBuilderPtr); } Loading Loading @@ -397,7 +431,7 @@ public class MeasuredParagraph { } } mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer, computeHyphenation)); computeHyphenation, computeLayout)); } finally { nFreeBuilder(nativeBuilderPtr); } Loading Loading @@ -595,7 +629,7 @@ public class MeasuredParagraph { * * 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) { Loading Loading @@ -626,7 +660,7 @@ public class MeasuredParagraph { /** * 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) { Loading Loading @@ -672,10 +706,16 @@ public class MeasuredParagraph { 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(); }
core/java/android/text/MeasuredText.java +52 −2 Original line number Diff line number Diff line Loading @@ -155,6 +155,11 @@ public class MeasuredText implements Spanned { * @return the measured text. */ public @NonNull MeasuredText build() { return build(true /* build full layout result */); } /** @hide */ public @NonNull MeasuredText build(boolean computeLayout) { final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE; Loading @@ -175,7 +180,7 @@ public class MeasuredText implements Spanned { paragraphEnds.add(paraEnd); measuredTexts.add(MeasuredParagraph.buildForStaticLayout( mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation, null /* no recycle */)); computeLayout, null /* no recycle */)); } return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy, Loading @@ -198,7 +203,8 @@ public class MeasuredText implements Spanned { mText = text; mStart = start; mEnd = end; mPaint = paint; // Copy the paint so that we can keep the reference of typeface in native layout result. mPaint = new TextPaint(paint); mMeasuredParagraphs = measuredTexts; mParagraphBreakPoints = paragraphBreakPoints; mTextDir = textDir; Loading Loading @@ -283,6 +289,50 @@ public class MeasuredText implements Spanned { return mHyphenationFrequency; } /** * Returns true if the given TextPaint gives the same result of text layout for this text. * @hide */ public boolean canUseMeasuredResult(@NonNull TextPaint paint) { return mPaint.getTextSize() == paint.getTextSize() && mPaint.getTextSkewX() == paint.getTextSkewX() && mPaint.getTextScaleX() == paint.getTextScaleX() && mPaint.getLetterSpacing() == paint.getLetterSpacing() && mPaint.getWordSpacing() == paint.getWordSpacing() && mPaint.getFlags() == paint.getFlags() // Maybe not all flag affects text layout. && mPaint.getTextLocales() == paint.getTextLocales() // need to be equals? && mPaint.getFontVariationSettings() == paint.getFontVariationSettings() && mPaint.getTypeface() == paint.getTypeface() && TextUtils.equals(mPaint.getFontFeatureSettings(), paint.getFontFeatureSettings()); } /** @hide */ public int findParaIndex(@IntRange(from = 0) int pos) { // TODO: Maybe good to remove paragraph concept from MeasuredText and add substring layout // support to StaticLayout. for (int i = 0; i < mParagraphBreakPoints.length; ++i) { if (pos < mParagraphBreakPoints[i]) { return i; } } throw new IndexOutOfBoundsException( "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1] + ", gave " + pos); } /** @hide */ public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) { final int paraIndex = findParaIndex(start); final int paraStart = getParagraphStart(paraIndex); final int paraEnd = getParagraphEnd(paraIndex); if (start < paraStart || paraEnd < end) { throw new RuntimeException("Cannot measured across the paragraph:" + "para: (" + paraStart + ", " + paraEnd + "), " + "request: (" + start + ", " + end + ")"); } return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart); } /////////////////////////////////////////////////////////////////////////////////////////////// // Spanned overrides // Loading
core/java/android/text/StaticLayout.java +1 −1 Original line number Diff line number Diff line Loading @@ -678,7 +678,7 @@ public class StaticLayout extends Layout { .setTextDirection(textDir) .setBreakStrategy(b.mBreakStrategy) .setHyphenationFrequency(b.mHyphenationFrequency) .build(); .build(false /* full layout is not necessary for line breaking */); spanned = (source instanceof Spanned) ? (Spanned) source : null; } else { final CharSequence original = measured.getText(); Loading
core/java/android/text/TextLine.java +17 −2 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ public class TextLine { private char[] mChars; private boolean mCharsValid; private Spanned mSpanned; private MeasuredText mMeasured; // Additional width of whitespace for justification. This value is per whitespace, thus // the line width will increase by mAddedWidth x (number of stretchable whitespaces). Loading Loading @@ -118,6 +119,7 @@ public class TextLine { tl.mSpanned = null; tl.mTabs = null; tl.mChars = null; tl.mMeasured = null; tl.mMetricAffectingSpanSpanSet.recycle(); tl.mCharacterStyleSpanSet.recycle(); Loading Loading @@ -168,6 +170,14 @@ public class TextLine { hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; } mMeasured = null; if (text instanceof MeasuredText) { MeasuredText mt = (MeasuredText) text; if (mt.canUseMeasuredResult(paint)) { mMeasured = mt; } } mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; if (mCharsValid) { Loading Loading @@ -736,8 +746,13 @@ public class TextLine { return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset); } else { final int delta = mStart; if (mMeasured == null) { // TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text. return wp.getRunAdvance(mText, delta + start, delta + end, delta + contextStart, delta + contextEnd, runIsRtl, delta + offset); } else { return mMeasured.getWidth(start + delta, end + delta); } } } Loading