diff options
| author | 2024-03-22 18:03:15 +0000 | |
|---|---|---|
| committer | 2024-03-22 18:03:15 +0000 | |
| commit | 248f57ad9ee032b3e3e1dc242fce5735a1593c44 (patch) | |
| tree | 82e27c6b389a397e5fe2158600621d7067934d96 | |
| parent | dc05136f5ee48790c5920784517672c8b1987944 (diff) | |
| parent | 1474d79ff91c1a40e479376fb9462c78d1b9f080 (diff) | |
Merge "Revert "Add trace point for the text measurement/draw"" into main
| -rw-r--r-- | core/java/android/text/BoringLayout.java | 83 | ||||
| -rw-r--r-- | core/java/android/text/DynamicLayout.java | 342 | ||||
| -rw-r--r-- | core/java/android/text/Layout.java | 75 | ||||
| -rw-r--r-- | core/java/android/text/PrecomputedText.java | 160 | ||||
| -rw-r--r-- | core/java/android/text/StaticLayout.java | 493 | ||||
| -rw-r--r-- | core/java/android/text/TextLine.java | 157 |
6 files changed, 577 insertions, 733 deletions
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java index e95b2166e71d..9db8aa167498 100644 --- a/core/java/android/text/BoringLayout.java +++ b/core/java/android/text/BoringLayout.java @@ -28,7 +28,6 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.graphics.text.LineBreakConfig; -import android.os.Trace; import android.text.style.ParagraphStyle; /** @@ -568,59 +567,49 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing, @Nullable Paint.FontMetrics minimumFontMetrics, @Nullable Metrics metrics) { - if (TRACE_LAYOUT) { - Trace.beginSection("BoringLayout#isBoring"); - Trace.setCounter("BoringLayout#textLength", text.length()); + final int textLength = text.length(); + if (hasAnyInterestingChars(text, textLength)) { + return null; // There are some interesting characters. Not boring. } - try { - final int textLength = text.length(); - if (hasAnyInterestingChars(text, textLength)) { - return null; // There are some interesting characters. Not boring. - } - if (textDir != null && textDir.isRtl(text, 0, textLength)) { - return null; // The heuristic considers the whole text RTL. Not boring. - } - if (text instanceof Spanned) { - Spanned sp = (Spanned) text; - Object[] styles = sp.getSpans(0, textLength, ParagraphStyle.class); - if (styles.length > 0) { - return null; // There are some ParagraphStyle spans. Not boring. - } + if (textDir != null && textDir.isRtl(text, 0, textLength)) { + return null; // The heuristic considers the whole text RTL. Not boring. + } + if (text instanceof Spanned) { + Spanned sp = (Spanned) text; + Object[] styles = sp.getSpans(0, textLength, ParagraphStyle.class); + if (styles.length > 0) { + return null; // There are some ParagraphStyle spans. Not boring. } + } - Metrics fm = metrics; - if (fm == null) { - fm = new Metrics(); - } else { - fm.reset(); - } + Metrics fm = metrics; + if (fm == null) { + fm = new Metrics(); + } else { + fm.reset(); + } - if (ClientFlags.fixLineHeightForLocale()) { - if (minimumFontMetrics != null) { - fm.set(minimumFontMetrics); - // Because the font metrics is provided by public APIs, adjust the top/bottom - // with ascent/descent: top must be smaller than ascent, bottom must be larger - // than descent. - fm.top = Math.min(fm.top, fm.ascent); - fm.bottom = Math.max(fm.bottom, fm.descent); - } + if (ClientFlags.fixLineHeightForLocale()) { + if (minimumFontMetrics != null) { + fm.set(minimumFontMetrics); + // Because the font metrics is provided by public APIs, adjust the top/bottom with + // ascent/descent: top must be smaller than ascent, bottom must be larger than + // descent. + fm.top = Math.min(fm.top, fm.ascent); + fm.bottom = Math.max(fm.bottom, fm.descent); } + } - TextLine line = TextLine.obtain(); - line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT, - Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null, - 0 /* ellipsisStart, 0 since text has not been ellipsized at this point */, - 0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */, - useFallbackLineSpacing); - fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false, null)); - TextLine.recycle(line); + TextLine line = TextLine.obtain(); + line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT, + Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null, + 0 /* ellipsisStart, 0 since text has not been ellipsized at this point */, + 0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */, + useFallbackLineSpacing); + fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false, null)); + TextLine.recycle(line); - return fm; - } finally { - if (TRACE_LAYOUT) { - Trace.endSection(); - } - } + return fm; } @Override diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 99ce0ef9fdf1..cce4f7bf7b1f 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -31,7 +31,6 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.text.LineBreakConfig; import android.os.Build; -import android.os.Trace; import android.text.method.OffsetMapping; import android.text.style.ReplacementSpan; import android.text.style.UpdateLayout; @@ -637,224 +636,207 @@ public class DynamicLayout extends Layout { /** @hide */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public void reflow(CharSequence s, int where, int before, int after) { - if (TRACE_LAYOUT) { - Trace.beginSection("DynamicLayout#reflow"); - } - try { - if (s != mBase) { - return; - } + if (s != mBase) + return; - CharSequence text = mDisplay; - int len = text.length(); + CharSequence text = mDisplay; + int len = text.length(); - // seek back to the start of the paragraph + // seek back to the start of the paragraph - int find = TextUtils.lastIndexOf(text, '\n', where - 1); - if (find < 0) { - find = 0; - } else { - find = find + 1; - } + int find = TextUtils.lastIndexOf(text, '\n', where - 1); + if (find < 0) + find = 0; + else + find = find + 1; - { - int diff = where - find; - before += diff; - after += diff; - where -= diff; - } - - // seek forward to the end of the paragraph - - int look = TextUtils.indexOf(text, '\n', where + after); - if (look < 0) { - look = len; - } else { - look++; // we want the index after the \n - } + { + int diff = where - find; + before += diff; + after += diff; + where -= diff; + } - int change = look - (where + after); - before += change; - after += change; + // seek forward to the end of the paragraph - // seek further out to cover anything that is forced to wrap together + int look = TextUtils.indexOf(text, '\n', where + after); + if (look < 0) + look = len; + else + look++; // we want the index after the \n - if (text instanceof Spanned) { - Spanned sp = (Spanned) text; - boolean again; + int change = look - (where + after); + before += change; + after += change; - do { - again = false; + // seek further out to cover anything that is forced to wrap together - Object[] force = sp.getSpans(where, where + after, - WrapTogetherSpan.class); + if (text instanceof Spanned) { + Spanned sp = (Spanned) text; + boolean again; - for (int i = 0; i < force.length; i++) { - int st = sp.getSpanStart(force[i]); - int en = sp.getSpanEnd(force[i]); + do { + again = false; - if (st < where) { - again = true; + Object[] force = sp.getSpans(where, where + after, + WrapTogetherSpan.class); - int diff = where - st; - before += diff; - after += diff; - where -= diff; - } + for (int i = 0; i < force.length; i++) { + int st = sp.getSpanStart(force[i]); + int en = sp.getSpanEnd(force[i]); - if (en > where + after) { - again = true; + if (st < where) { + again = true; - int diff = en - (where + after); - before += diff; - after += diff; - } + int diff = where - st; + before += diff; + after += diff; + where -= diff; } - } while (again); - } - // find affected region of old layout - - int startline = getLineForOffset(where); - int startv = getLineTop(startline); - - int endline = getLineForOffset(where + before); - if (where + after == len) { - endline = getLineCount(); - } - int endv = getLineTop(endline); - boolean islast = (endline == getLineCount()); + if (en > where + after) { + again = true; - // generate new layout for affected text - - StaticLayout reflowed; - StaticLayout.Builder b; + int diff = en - (where + after); + before += diff; + after += diff; + } + } + } while (again); + } - synchronized (sLock) { - reflowed = sStaticLayout; - b = sBuilder; - sStaticLayout = null; - sBuilder = null; - } + // find affected region of old layout - if (b == null) { - b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth()); - } + int startline = getLineForOffset(where); + int startv = getLineTop(startline); - b.setText(text, where, where + after) - .setPaint(getPaint()) - .setWidth(getWidth()) - .setTextDirection(getTextDirectionHeuristic()) - .setLineSpacing(getSpacingAdd(), getSpacingMultiplier()) - .setUseLineSpacingFromFallbacks(mFallbackLineSpacing) - .setEllipsizedWidth(mEllipsizedWidth) - .setEllipsize(mEllipsizeAt) - .setBreakStrategy(mBreakStrategy) - .setHyphenationFrequency(mHyphenationFrequency) - .setJustificationMode(mJustificationMode) - .setLineBreakConfig(mLineBreakConfig) - .setAddLastLineLineSpacing(!islast) - .setIncludePad(false) - .setUseBoundsForWidth(mUseBoundsForWidth) - .setShiftDrawingOffsetForStartOverhang(mShiftDrawingOffsetForStartOverhang) - .setMinimumFontMetrics(mMinimumFontMetrics) - .setCalculateBounds(true); - - reflowed = b.buildPartialStaticLayoutForDynamicLayout(true /* trackpadding */, - reflowed); - int n = reflowed.getLineCount(); - // If the new layout has a blank line at the end, but it is not - // the very end of the buffer, then we already have a line that - // starts there, so disregard the blank line. - - if (where + after != len && reflowed.getLineStart(n - 1) == where + after) { - n--; - } + int endline = getLineForOffset(where + before); + if (where + after == len) + endline = getLineCount(); + int endv = getLineTop(endline); + boolean islast = (endline == getLineCount()); - // remove affected lines from old layout - mInts.deleteAt(startline, endline - startline); - mObjects.deleteAt(startline, endline - startline); + // generate new layout for affected text - // adjust offsets in layout for new height and offsets + StaticLayout reflowed; + StaticLayout.Builder b; - int ht = reflowed.getLineTop(n); - int toppad = 0, botpad = 0; + synchronized (sLock) { + reflowed = sStaticLayout; + b = sBuilder; + sStaticLayout = null; + sBuilder = null; + } - if (mIncludePad && startline == 0) { - toppad = reflowed.getTopPadding(); - mTopPadding = toppad; - ht -= toppad; - } - if (mIncludePad && islast) { - botpad = reflowed.getBottomPadding(); - mBottomPadding = botpad; - ht += botpad; - } + if (b == null) { + b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth()); + } - mInts.adjustValuesBelow(startline, START, after - before); - mInts.adjustValuesBelow(startline, TOP, startv - endv + ht); + b.setText(text, where, where + after) + .setPaint(getPaint()) + .setWidth(getWidth()) + .setTextDirection(getTextDirectionHeuristic()) + .setLineSpacing(getSpacingAdd(), getSpacingMultiplier()) + .setUseLineSpacingFromFallbacks(mFallbackLineSpacing) + .setEllipsizedWidth(mEllipsizedWidth) + .setEllipsize(mEllipsizeAt) + .setBreakStrategy(mBreakStrategy) + .setHyphenationFrequency(mHyphenationFrequency) + .setJustificationMode(mJustificationMode) + .setLineBreakConfig(mLineBreakConfig) + .setAddLastLineLineSpacing(!islast) + .setIncludePad(false) + .setUseBoundsForWidth(mUseBoundsForWidth) + .setShiftDrawingOffsetForStartOverhang(mShiftDrawingOffsetForStartOverhang) + .setMinimumFontMetrics(mMinimumFontMetrics) + .setCalculateBounds(true); + + reflowed = b.buildPartialStaticLayoutForDynamicLayout(true /* trackpadding */, reflowed); + int n = reflowed.getLineCount(); + // If the new layout has a blank line at the end, but it is not + // the very end of the buffer, then we already have a line that + // starts there, so disregard the blank line. + + if (where + after != len && reflowed.getLineStart(n - 1) == where + after) + n--; + + // remove affected lines from old layout + mInts.deleteAt(startline, endline - startline); + mObjects.deleteAt(startline, endline - startline); + + // adjust offsets in layout for new height and offsets + + int ht = reflowed.getLineTop(n); + int toppad = 0, botpad = 0; + + if (mIncludePad && startline == 0) { + toppad = reflowed.getTopPadding(); + mTopPadding = toppad; + ht -= toppad; + } + if (mIncludePad && islast) { + botpad = reflowed.getBottomPadding(); + mBottomPadding = botpad; + ht += botpad; + } - // insert new layout + mInts.adjustValuesBelow(startline, START, after - before); + mInts.adjustValuesBelow(startline, TOP, startv - endv + ht); - int[] ints; + // insert new layout - if (mEllipsize) { - ints = new int[COLUMNS_ELLIPSIZE]; - ints[ELLIPSIS_START] = ELLIPSIS_UNDEFINED; - } else { - ints = new int[COLUMNS_NORMAL]; - } + int[] ints; - Directions[] objects = new Directions[1]; + if (mEllipsize) { + ints = new int[COLUMNS_ELLIPSIZE]; + ints[ELLIPSIS_START] = ELLIPSIS_UNDEFINED; + } else { + ints = new int[COLUMNS_NORMAL]; + } - for (int i = 0; i < n; i++) { - final int start = reflowed.getLineStart(i); - ints[START] = start; - ints[DIR] |= reflowed.getParagraphDirection(i) << DIR_SHIFT; - ints[TAB] |= reflowed.getLineContainsTab(i) ? TAB_MASK : 0; + Directions[] objects = new Directions[1]; - int top = reflowed.getLineTop(i) + startv; - if (i > 0) { - top -= toppad; - } - ints[TOP] = top; + for (int i = 0; i < n; i++) { + final int start = reflowed.getLineStart(i); + ints[START] = start; + ints[DIR] |= reflowed.getParagraphDirection(i) << DIR_SHIFT; + ints[TAB] |= reflowed.getLineContainsTab(i) ? TAB_MASK : 0; - int desc = reflowed.getLineDescent(i); - if (i == n - 1) { - desc += botpad; - } + int top = reflowed.getLineTop(i) + startv; + if (i > 0) + top -= toppad; + ints[TOP] = top; - ints[DESCENT] = desc; - ints[EXTRA] = reflowed.getLineExtra(i); - objects[0] = reflowed.getLineDirections(i); + int desc = reflowed.getLineDescent(i); + if (i == n - 1) + desc += botpad; - final int end = (i == n - 1) ? where + after : reflowed.getLineStart(i + 1); - ints[HYPHEN] = StaticLayout.packHyphenEdit( - reflowed.getStartHyphenEdit(i), reflowed.getEndHyphenEdit(i)); - ints[MAY_PROTRUDE_FROM_TOP_OR_BOTTOM] |= - contentMayProtrudeFromLineTopOrBottom(text, start, end) - ? MAY_PROTRUDE_FROM_TOP_OR_BOTTOM_MASK : 0; + ints[DESCENT] = desc; + ints[EXTRA] = reflowed.getLineExtra(i); + objects[0] = reflowed.getLineDirections(i); - if (mEllipsize) { - ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i); - ints[ELLIPSIS_COUNT] = reflowed.getEllipsisCount(i); - } + final int end = (i == n - 1) ? where + after : reflowed.getLineStart(i + 1); + ints[HYPHEN] = StaticLayout.packHyphenEdit( + reflowed.getStartHyphenEdit(i), reflowed.getEndHyphenEdit(i)); + ints[MAY_PROTRUDE_FROM_TOP_OR_BOTTOM] |= + contentMayProtrudeFromLineTopOrBottom(text, start, end) ? + MAY_PROTRUDE_FROM_TOP_OR_BOTTOM_MASK : 0; - mInts.insertAt(startline + i, ints); - mObjects.insertAt(startline + i, objects); + if (mEllipsize) { + ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i); + ints[ELLIPSIS_COUNT] = reflowed.getEllipsisCount(i); } - updateBlocks(startline, endline - 1, n); + mInts.insertAt(startline + i, ints); + mObjects.insertAt(startline + i, objects); + } - b.finish(); - synchronized (sLock) { - sStaticLayout = reflowed; - sBuilder = b; - } - } finally { - if (TRACE_LAYOUT) { - Trace.endSection(); - } + updateBlocks(startline, endline - 1, n); + + b.finish(); + synchronized (sLock) { + sStaticLayout = reflowed; + sBuilder = b; } } diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index ce238a77ad87..8dee4b19c6d3 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -39,7 +39,6 @@ import android.graphics.RectF; import android.graphics.text.LineBreakConfig; import android.graphics.text.LineBreaker; import android.os.Build; -import android.os.Trace; import android.text.method.TextKeyListener; import android.text.style.AlignmentSpan; import android.text.style.LeadingMarginSpan; @@ -71,11 +70,6 @@ import java.util.Locale; * For text that will not change, use a {@link StaticLayout}. */ public abstract class Layout { - - /** @hide */ - protected static final boolean TRACE_LAYOUT = Build.isDebuggable(); - - /** @hide */ @IntDef(prefix = { "BREAK_STRATEGY_" }, value = { LineBreaker.BREAK_STRATEGY_SIMPLE, @@ -478,51 +472,40 @@ public abstract class Layout { @Nullable Path selectionPath, @Nullable Paint selectionPaint, int cursorOffsetVertical) { - if (TRACE_LAYOUT) { - Trace.beginSection("Layout#draw"); - } - try { - float leftShift = 0; - if (mUseBoundsForWidth && mShiftDrawingOffsetForStartOverhang) { - RectF drawingRect = computeDrawingBoundingBox(); - if (drawingRect.left < 0) { - leftShift = -drawingRect.left; - canvas.translate(leftShift, 0); - } + float leftShift = 0; + if (mUseBoundsForWidth && mShiftDrawingOffsetForStartOverhang) { + RectF drawingRect = computeDrawingBoundingBox(); + if (drawingRect.left < 0) { + leftShift = -drawingRect.left; + canvas.translate(leftShift, 0); } - final long lineRange = getLineRangeForDraw(canvas); - int firstLine = TextUtils.unpackRangeStartFromLong(lineRange); - int lastLine = TextUtils.unpackRangeEndFromLong(lineRange); - if (lastLine < 0) return; + } + final long lineRange = getLineRangeForDraw(canvas); + int firstLine = TextUtils.unpackRangeStartFromLong(lineRange); + int lastLine = TextUtils.unpackRangeEndFromLong(lineRange); + if (lastLine < 0) return; - if (shouldDrawHighlightsOnTop(canvas)) { - drawBackground(canvas, firstLine, lastLine); - } else { - drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, - selectionPaint, - cursorOffsetVertical, firstLine, lastLine); - } + if (shouldDrawHighlightsOnTop(canvas)) { + drawBackground(canvas, firstLine, lastLine); + } else { + drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, + cursorOffsetVertical, firstLine, lastLine); + } - drawText(canvas, firstLine, lastLine); + drawText(canvas, firstLine, lastLine); - // Since high contrast text draws a solid rectangle background behind the text, it - // covers up the highlights and selections. In this case we draw over the top of the - // text with a blend mode that ensures the text stays high-contrast. - if (shouldDrawHighlightsOnTop(canvas)) { - drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, - selectionPaint, - cursorOffsetVertical, firstLine, lastLine); - } + // Since high contrast text draws a solid rectangle background behind the text, it covers up + // the highlights and selections. In this case we draw over the top of the text with a + // blend mode that ensures the text stays high-contrast. + if (shouldDrawHighlightsOnTop(canvas)) { + drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, + cursorOffsetVertical, firstLine, lastLine); + } - if (leftShift != 0) { - // Manually translate back to the original position because of b/324498002, using - // save/restore disappears the toggle switch drawables. - canvas.translate(-leftShift, 0); - } - } finally { - if (TRACE_LAYOUT) { - Trace.endSection(); - } + if (leftShift != 0) { + // Manually translate back to the original position because of b/324498002, using + // save/restore disappears the toggle switch drawables. + canvas.translate(-leftShift, 0); } } diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java index 14401a6b231b..5f6a9bd068c9 100644 --- a/core/java/android/text/PrecomputedText.java +++ b/core/java/android/text/PrecomputedText.java @@ -25,8 +25,6 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.text.LineBreakConfig; import android.graphics.text.MeasuredText; -import android.os.Build; -import android.os.Trace; import android.text.style.MetricAffectingSpan; import com.android.internal.util.Preconditions; @@ -80,8 +78,6 @@ import java.util.Objects; public class PrecomputedText implements Spannable { private static final char LINE_FEED = '\n'; - private static final boolean TRACE_PCT = Build.isDebuggable(); - /** * The information required for building {@link PrecomputedText}. * @@ -451,47 +447,35 @@ public class PrecomputedText implements Spannable { private static ParagraphInfo[] createMeasuredParagraphsFromPrecomputedText( @NonNull PrecomputedText pct, @NonNull Params params, boolean computeLayout) { - if (TRACE_PCT) { - Trace.beginSection("PrecomputedText#createMeasuredParagraphsFromPrecomputedText"); - Trace.setCounter("PrecomputedText#textCharCount", pct.length()); + final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE + && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; + final int hyphenationMode; + if (needHyphenation) { + hyphenationMode = isFastHyphenation(params.getHyphenationFrequency()) + ? MeasuredText.Builder.HYPHENATION_MODE_FAST : + MeasuredText.Builder.HYPHENATION_MODE_NORMAL; + } else { + hyphenationMode = MeasuredText.Builder.HYPHENATION_MODE_NONE; } - try { - final boolean needHyphenation = - params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE - && params.getHyphenationFrequency() - != Layout.HYPHENATION_FREQUENCY_NONE; - final int hyphenationMode; - if (needHyphenation) { - hyphenationMode = isFastHyphenation(params.getHyphenationFrequency()) - ? MeasuredText.Builder.HYPHENATION_MODE_FAST : - MeasuredText.Builder.HYPHENATION_MODE_NORMAL; - } else { - hyphenationMode = MeasuredText.Builder.HYPHENATION_MODE_NONE; - } - LineBreakConfig config = params.getLineBreakConfig(); - if (config.getLineBreakWordStyle() == LineBreakConfig.LINE_BREAK_WORD_STYLE_AUTO - && pct.getParagraphCount() != 1) { - // If the text has multiple paragraph, resolve line break word style auto to none. - config = new LineBreakConfig.Builder() - .merge(config) - .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE) - .build(); - } - ArrayList<ParagraphInfo> result = new ArrayList<>(); - for (int i = 0; i < pct.getParagraphCount(); ++i) { - final int paraStart = pct.getParagraphStart(i); - final int paraEnd = pct.getParagraphEnd(i); - result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout( - params.getTextPaint(), config, pct, paraStart, paraEnd, - params.getTextDirection(), hyphenationMode, computeLayout, true, - pct.getMeasuredParagraph(i), null /* no recycle */))); - } - return result.toArray(new ParagraphInfo[result.size()]); - } finally { - if (TRACE_PCT) { - Trace.endSection(); - } + LineBreakConfig config = params.getLineBreakConfig(); + if (config.getLineBreakWordStyle() == LineBreakConfig.LINE_BREAK_WORD_STYLE_AUTO + && pct.getParagraphCount() != 1) { + // If the text has multiple paragraph, resolve line break word style auto to none. + config = new LineBreakConfig.Builder() + .merge(config) + .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE) + .build(); + } + ArrayList<ParagraphInfo> result = new ArrayList<>(); + for (int i = 0; i < pct.getParagraphCount(); ++i) { + final int paraStart = pct.getParagraphStart(i); + final int paraEnd = pct.getParagraphEnd(i); + result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout( + params.getTextPaint(), config, pct, paraStart, paraEnd, + params.getTextDirection(), hyphenationMode, computeLayout, true, + pct.getMeasuredParagraph(i), null /* no recycle */))); } + return result.toArray(new ParagraphInfo[result.size()]); } /** @hide */ @@ -499,65 +483,53 @@ public class PrecomputedText implements Spannable { @NonNull CharSequence text, @NonNull Params params, @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout, boolean computeBounds) { - if (TRACE_PCT) { - Trace.beginSection("PrecomputedText#createMeasuredParagraphs"); - Trace.setCounter("PrecomputedText#textCharCount", text.length()); + ArrayList<ParagraphInfo> result = new ArrayList<>(); + + Preconditions.checkNotNull(text); + Preconditions.checkNotNull(params); + final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE + && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; + final int hyphenationMode; + if (needHyphenation) { + hyphenationMode = isFastHyphenation(params.getHyphenationFrequency()) + ? MeasuredText.Builder.HYPHENATION_MODE_FAST : + MeasuredText.Builder.HYPHENATION_MODE_NORMAL; + } else { + hyphenationMode = MeasuredText.Builder.HYPHENATION_MODE_NONE; } - try { - ArrayList<ParagraphInfo> result = new ArrayList<>(); - Preconditions.checkNotNull(text); - Preconditions.checkNotNull(params); - final boolean needHyphenation = - params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE - && params.getHyphenationFrequency() - != Layout.HYPHENATION_FREQUENCY_NONE; - final int hyphenationMode; - if (needHyphenation) { - hyphenationMode = isFastHyphenation(params.getHyphenationFrequency()) - ? MeasuredText.Builder.HYPHENATION_MODE_FAST : - MeasuredText.Builder.HYPHENATION_MODE_NORMAL; + LineBreakConfig config = null; + 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 { - hyphenationMode = MeasuredText.Builder.HYPHENATION_MODE_NONE; + paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. } - LineBreakConfig config = null; - 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. - } - - if (config == null) { - config = params.getLineBreakConfig(); - if (config.getLineBreakWordStyle() == LineBreakConfig.LINE_BREAK_WORD_STYLE_AUTO - && !(paraStart == start && paraEnd == end)) { - // If the text has multiple paragraph, resolve line break word style auto to - // none. - config = new LineBreakConfig.Builder() - .merge(config) - .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE) - .build(); - } + if (config == null) { + config = params.getLineBreakConfig(); + if (config.getLineBreakWordStyle() == LineBreakConfig.LINE_BREAK_WORD_STYLE_AUTO + && !(paraStart == start && paraEnd == end)) { + // If the text has multiple paragraph, resolve line break word style auto to + // none. + config = new LineBreakConfig.Builder() + .merge(config) + .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE) + .build(); } - - result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout( - params.getTextPaint(), config, text, paraStart, paraEnd, - params.getTextDirection(), hyphenationMode, computeLayout, computeBounds, - null /* no hint */, - null /* no recycle */))); - } - return result.toArray(new ParagraphInfo[result.size()]); - } finally { - if (TRACE_PCT) { - Trace.endSection(); } + + result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout( + params.getTextPaint(), config, text, paraStart, paraEnd, + params.getTextDirection(), hyphenationMode, computeLayout, computeBounds, + null /* no hint */, + null /* no recycle */))); } + return result.toArray(new ParagraphInfo[result.size()]); } // Use PrecomputedText.create instead. diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index d1b14d14c7d8..3dd3a9ea8baf 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -542,20 +542,10 @@ public class StaticLayout extends Layout { */ @NonNull public StaticLayout build() { - if (TRACE_LAYOUT) { - Trace.beginSection("StaticLayout#build"); - Trace.setCounter("StaticLayout#textLength", mText.length()); - } - try { - StaticLayout result = new StaticLayout(this, mIncludePad, mEllipsize != null - ? COLUMNS_ELLIPSIZE : COLUMNS_NORMAL); - Builder.recycle(this); - return result; - } finally { - if (TRACE_LAYOUT) { - Trace.endSection(); - } - } + StaticLayout result = new StaticLayout(this, mIncludePad, mEllipsize != null + ? COLUMNS_ELLIPSIZE : COLUMNS_NORMAL); + Builder.recycle(this); + return result; } /** @@ -572,21 +562,16 @@ public class StaticLayout extends Layout { */ /* package */ @NonNull StaticLayout buildPartialStaticLayoutForDynamicLayout( boolean trackpadding, StaticLayout recycle) { - if (TRACE_LAYOUT) { - Trace.beginSection("StaticLayout#forDynamicLayout"); - Trace.setCounter("StaticLayout#textLength", mText.length()); + if (recycle == null) { + recycle = new StaticLayout(); } + Trace.beginSection("Generating StaticLayout For DynamicLayout"); try { - if (recycle == null) { - recycle = new StaticLayout(); - } recycle.generate(this, mIncludePad, trackpadding); - return recycle; } finally { - if (TRACE_LAYOUT) { - Trace.endSection(); - } + Trace.endSection(); } + return recycle; } private CharSequence mText; @@ -742,7 +727,12 @@ public class StaticLayout extends Layout { mLeftIndents = b.mLeftIndents; mRightIndents = b.mRightIndents; - generate(b, b.mIncludePad, trackPadding); + Trace.beginSection("Constructing StaticLayout"); + try { + generate(b, b.mIncludePad, trackPadding); + } finally { + Trace.endSection(); + } } private static int getBaseHyphenationFrequency(int frequency) { @@ -852,23 +842,14 @@ public class StaticLayout extends Layout { case PrecomputedText.Params.UNUSABLE: break; case PrecomputedText.Params.NEED_RECOMPUTE: - if (TRACE_LAYOUT) { - Trace.beginSection("StaticLayout#recomputePct"); - } - try { - final PrecomputedText.Params newParams = - new PrecomputedText.Params.Builder(paint) - .setBreakStrategy(b.mBreakStrategy) - .setHyphenationFrequency(b.mHyphenationFrequency) - .setTextDirection(textDir) - .setLineBreakConfig(b.mLineBreakConfig) - .build(); - precomputed = PrecomputedText.create(precomputed, newParams); - } finally { - if (TRACE_LAYOUT) { - Trace.endSection(); - } - } + final PrecomputedText.Params newParams = + new PrecomputedText.Params.Builder(paint) + .setBreakStrategy(b.mBreakStrategy) + .setHyphenationFrequency(b.mHyphenationFrequency) + .setTextDirection(textDir) + .setLineBreakConfig(b.mLineBreakConfig) + .build(); + precomputed = PrecomputedText.create(precomputed, newParams); paragraphInfo = precomputed.getParagraphInfo(); break; case PrecomputedText.Params.USABLE: @@ -879,261 +860,232 @@ public class StaticLayout extends Layout { } if (paragraphInfo == null) { - if (TRACE_LAYOUT) { - Trace.beginSection("StaticLayout#computePct"); - } - try { - final PrecomputedText.Params param = new PrecomputedText.Params(paint, - b.mLineBreakConfig, textDir, b.mBreakStrategy, b.mHyphenationFrequency); - paragraphInfo = PrecomputedText.createMeasuredParagraphs(source, param, bufStart, - bufEnd, false /* computeLayout */, b.mCalculateBounds); - } finally { - if (TRACE_LAYOUT) { - Trace.endSection(); - } - } + final PrecomputedText.Params param = new PrecomputedText.Params(paint, + b.mLineBreakConfig, textDir, b.mBreakStrategy, b.mHyphenationFrequency); + paragraphInfo = PrecomputedText.createMeasuredParagraphs(source, param, bufStart, + bufEnd, false /* computeLayout */, b.mCalculateBounds); } for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) { - if (TRACE_LAYOUT) { - Trace.beginSection("StaticLayout#processParagraph"); - Trace.setCounter("StaticLayout#paragraph", paraIndex); - } - try { - final int paraStart = paraIndex == 0 - ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd; - final int paraEnd = paragraphInfo[paraIndex].paragraphEnd; - - int firstWidthLineCount = 1; - int firstWidth = outerWidth; - int restWidth = outerWidth; - - LineHeightSpan[] chooseHt = null; - if (spanned != null) { - LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, - LeadingMarginSpan.class); - for (int i = 0; i < sp.length; i++) { - LeadingMarginSpan lms = sp[i]; - firstWidth -= sp[i].getLeadingMargin(true); - restWidth -= sp[i].getLeadingMargin(false); - - // LeadingMarginSpan2 is odd. The count affects all - // leading margin spans, not just this particular one - if (lms instanceof LeadingMarginSpan2) { - LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms; - firstWidthLineCount = Math.max(firstWidthLineCount, - lms2.getLeadingMarginLineCount()); - } + final int paraStart = paraIndex == 0 + ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd; + final int paraEnd = paragraphInfo[paraIndex].paragraphEnd; + + int firstWidthLineCount = 1; + int firstWidth = outerWidth; + int restWidth = outerWidth; + + LineHeightSpan[] chooseHt = null; + if (spanned != null) { + LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, + LeadingMarginSpan.class); + for (int i = 0; i < sp.length; i++) { + LeadingMarginSpan lms = sp[i]; + firstWidth -= sp[i].getLeadingMargin(true); + restWidth -= sp[i].getLeadingMargin(false); + + // LeadingMarginSpan2 is odd. The count affects all + // leading margin spans, not just this particular one + if (lms instanceof LeadingMarginSpan2) { + LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms; + firstWidthLineCount = Math.max(firstWidthLineCount, + lms2.getLeadingMarginLineCount()); } + } - chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); + chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); - if (chooseHt.length == 0) { - chooseHt = null; // So that out() would not assume it has any contents - } else { - if (chooseHtv == null || chooseHtv.length < chooseHt.length) { - chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length); - } + if (chooseHt.length == 0) { + chooseHt = null; // So that out() would not assume it has any contents + } else { + if (chooseHtv == null || chooseHtv.length < chooseHt.length) { + chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length); + } - for (int i = 0; i < chooseHt.length; i++) { - int o = spanned.getSpanStart(chooseHt[i]); + for (int i = 0; i < chooseHt.length; i++) { + int o = spanned.getSpanStart(chooseHt[i]); - if (o < paraStart) { - // starts in this layout, before the - // current paragraph + if (o < paraStart) { + // starts in this layout, before the + // current paragraph - chooseHtv[i] = getLineTop(getLineForOffset(o)); - } else { - // starts in this paragraph + chooseHtv[i] = getLineTop(getLineForOffset(o)); + } else { + // starts in this paragraph - chooseHtv[i] = v; - } + chooseHtv[i] = v; } } } - // tab stop locations - float[] variableTabStops = null; - if (spanned != null) { - TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, - paraEnd, TabStopSpan.class); - if (spans.length > 0) { - float[] stops = new float[spans.length]; - for (int i = 0; i < spans.length; i++) { - stops[i] = (float) spans[i].getTabStop(); - } - Arrays.sort(stops, 0, stops.length); - variableTabStops = stops; + } + // tab stop locations + float[] variableTabStops = null; + if (spanned != null) { + TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, + paraEnd, TabStopSpan.class); + if (spans.length > 0) { + float[] stops = new float[spans.length]; + for (int i = 0; i < spans.length; i++) { + stops[i] = (float) spans[i].getTabStop(); } + Arrays.sort(stops, 0, stops.length); + variableTabStops = stops; } + } - final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured; - final char[] chs = measuredPara.getChars(); - final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray(); - final int[] fmCache = measuredPara.getFontMetrics().getRawArray(); + final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured; + final char[] chs = measuredPara.getChars(); + final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray(); + final int[] fmCache = measuredPara.getFontMetrics().getRawArray(); + + constraints.setWidth(restWidth); + constraints.setIndent(firstWidth, firstWidthLineCount); + constraints.setTabStops(variableTabStops, TAB_INCREMENT); + + LineBreaker.Result res = lineBreaker.computeLineBreaks( + measuredPara.getMeasuredText(), constraints, mLineCount); + int breakCount = res.getLineCount(); + if (lineBreakCapacity < breakCount) { + lineBreakCapacity = breakCount; + breaks = new int[lineBreakCapacity]; + lineWidths = new float[lineBreakCapacity]; + ascents = new float[lineBreakCapacity]; + descents = new float[lineBreakCapacity]; + hasTabs = new boolean[lineBreakCapacity]; + hyphenEdits = new int[lineBreakCapacity]; + } - constraints.setWidth(restWidth); - constraints.setIndent(firstWidth, firstWidthLineCount); - constraints.setTabStops(variableTabStops, TAB_INCREMENT); + for (int i = 0; i < breakCount; ++i) { + breaks[i] = res.getLineBreakOffset(i); + lineWidths[i] = res.getLineWidth(i); + ascents[i] = res.getLineAscent(i); + descents[i] = res.getLineDescent(i); + hasTabs[i] = res.hasLineTab(i); + hyphenEdits[i] = + packHyphenEdit(res.getStartLineHyphenEdit(i), res.getEndLineHyphenEdit(i)); + } - if (TRACE_LAYOUT) { - Trace.beginSection("LineBreaker#computeLineBreaks"); - } - LineBreaker.Result res; - try { - res = lineBreaker.computeLineBreaks( - measuredPara.getMeasuredText(), constraints, mLineCount); - } finally { - if (TRACE_LAYOUT) { - Trace.endSection(); + final int remainingLineCount = mMaximumVisibleLineCount - mLineCount; + final boolean ellipsisMayBeApplied = ellipsize != null + && (ellipsize == TextUtils.TruncateAt.END + || (mMaximumVisibleLineCount == 1 + && ellipsize != TextUtils.TruncateAt.MARQUEE)); + if (0 < remainingLineCount && remainingLineCount < breakCount + && ellipsisMayBeApplied) { + // Calculate width + float width = 0; + boolean hasTab = false; // XXX May need to also have starting hyphen edit + for (int i = remainingLineCount - 1; i < breakCount; i++) { + if (i == breakCount - 1) { + width += lineWidths[i]; + } else { + for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) { + width += measuredPara.getCharWidthAt(j); + } } + hasTab |= hasTabs[i]; } - int breakCount = res.getLineCount(); - if (lineBreakCapacity < breakCount) { - lineBreakCapacity = breakCount; - breaks = new int[lineBreakCapacity]; - lineWidths = new float[lineBreakCapacity]; - ascents = new float[lineBreakCapacity]; - descents = new float[lineBreakCapacity]; - hasTabs = new boolean[lineBreakCapacity]; - hyphenEdits = new int[lineBreakCapacity]; - } + // Treat the last line and overflowed lines as a single line. + breaks[remainingLineCount - 1] = breaks[breakCount - 1]; + lineWidths[remainingLineCount - 1] = width; + hasTabs[remainingLineCount - 1] = hasTab; - for (int i = 0; i < breakCount; ++i) { - breaks[i] = res.getLineBreakOffset(i); - lineWidths[i] = res.getLineWidth(i); - ascents[i] = res.getLineAscent(i); - descents[i] = res.getLineDescent(i); - hasTabs[i] = res.hasLineTab(i); - hyphenEdits[i] = - packHyphenEdit(res.getStartLineHyphenEdit(i), res.getEndLineHyphenEdit(i)); - } + breakCount = remainingLineCount; + } - final int remainingLineCount = mMaximumVisibleLineCount - mLineCount; - final boolean ellipsisMayBeApplied = ellipsize != null - && (ellipsize == TextUtils.TruncateAt.END - || (mMaximumVisibleLineCount == 1 - && ellipsize != TextUtils.TruncateAt.MARQUEE)); - if (0 < remainingLineCount && remainingLineCount < breakCount - && ellipsisMayBeApplied) { - // Calculate width - float width = 0; - boolean hasTab = false; // XXX May need to also have starting hyphen edit - for (int i = remainingLineCount - 1; i < breakCount; i++) { - if (i == breakCount - 1) { - width += lineWidths[i]; - } else { - for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) { - width += measuredPara.getCharWidthAt(j); - } - } - hasTab |= hasTabs[i]; - } - // Treat the last line and overflowed lines as a single line. - breaks[remainingLineCount - 1] = breaks[breakCount - 1]; - lineWidths[remainingLineCount - 1] = width; - hasTabs[remainingLineCount - 1] = hasTab; + // here is the offset of the starting character of the line we are currently + // measuring + int here = paraStart; + + int fmTop = defaultTop; + int fmBottom = defaultBottom; + int fmAscent = defaultAscent; + int fmDescent = defaultDescent; + int fmCacheIndex = 0; + int spanEndCacheIndex = 0; + int breakIndex = 0; + for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { + // retrieve end of span + spanEnd = spanEndCache[spanEndCacheIndex++]; + + // retrieve cached metrics, order matches above + fm.top = fmCache[fmCacheIndex * 4 + 0]; + fm.bottom = fmCache[fmCacheIndex * 4 + 1]; + fm.ascent = fmCache[fmCacheIndex * 4 + 2]; + fm.descent = fmCache[fmCacheIndex * 4 + 3]; + fmCacheIndex++; + + if (fm.top < fmTop) { + fmTop = fm.top; + } + if (fm.ascent < fmAscent) { + fmAscent = fm.ascent; + } + if (fm.descent > fmDescent) { + fmDescent = fm.descent; + } + if (fm.bottom > fmBottom) { + fmBottom = fm.bottom; + } - breakCount = remainingLineCount; + // skip breaks ending before current span range + while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) { + breakIndex++; } - // here is the offset of the starting character of the line we are currently - // measuring - int here = paraStart; - - int fmTop = defaultTop; - int fmBottom = defaultBottom; - int fmAscent = defaultAscent; - int fmDescent = defaultDescent; - int fmCacheIndex = 0; - int spanEndCacheIndex = 0; - int breakIndex = 0; - for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { - // retrieve end of span - spanEnd = spanEndCache[spanEndCacheIndex++]; - - // retrieve cached metrics, order matches above - fm.top = fmCache[fmCacheIndex * 4 + 0]; - fm.bottom = fmCache[fmCacheIndex * 4 + 1]; - fm.ascent = fmCache[fmCacheIndex * 4 + 2]; - fm.descent = fmCache[fmCacheIndex * 4 + 3]; - fmCacheIndex++; - - if (fm.top < fmTop) { - fmTop = fm.top; - } - if (fm.ascent < fmAscent) { - fmAscent = fm.ascent; - } - if (fm.descent > fmDescent) { - fmDescent = fm.descent; - } - if (fm.bottom > fmBottom) { - fmBottom = fm.bottom; - } + while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) { + int endPos = paraStart + breaks[breakIndex]; - // skip breaks ending before current span range - while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) { - breakIndex++; - } + boolean moreChars = (endPos < bufEnd); - while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) { - int endPos = paraStart + breaks[breakIndex]; - - boolean moreChars = (endPos < bufEnd); - - final int ascent = isFallbackLineSpacing - ? Math.min(fmAscent, Math.round(ascents[breakIndex])) - : fmAscent; - final int descent = isFallbackLineSpacing - ? Math.max(fmDescent, Math.round(descents[breakIndex])) - : fmDescent; - - // The fallback ascent/descent may be larger than top/bottom of the default - // font metrics. Adjust top/bottom with ascent/descent for avoiding - // unexpected clipping. - if (isFallbackLineSpacing) { - if (ascent < fmTop) { - fmTop = ascent; - } - if (descent > fmBottom) { - fmBottom = descent; - } - } + final int ascent = isFallbackLineSpacing + ? Math.min(fmAscent, Math.round(ascents[breakIndex])) + : fmAscent; + final int descent = isFallbackLineSpacing + ? Math.max(fmDescent, Math.round(descents[breakIndex])) + : fmDescent; - v = out(source, here, endPos, - ascent, descent, fmTop, fmBottom, - v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, - hasTabs[breakIndex], hyphenEdits[breakIndex], needMultiply, - measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, chs, - paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], - paint, moreChars); - - if (endPos < spanEnd) { - // preserve metrics for current span - fmTop = Math.min(defaultTop, fm.top); - fmBottom = Math.max(defaultBottom, fm.bottom); - fmAscent = Math.min(defaultAscent, fm.ascent); - fmDescent = Math.max(defaultDescent, fm.descent); - } else { - fmTop = fmBottom = fmAscent = fmDescent = 0; + // The fallback ascent/descent may be larger than top/bottom of the default font + // metrics. Adjust top/bottom with ascent/descent for avoiding unexpected + // clipping. + if (isFallbackLineSpacing) { + if (ascent < fmTop) { + fmTop = ascent; } + if (descent > fmBottom) { + fmBottom = descent; + } + } - here = endPos; - breakIndex++; + v = out(source, here, endPos, + ascent, descent, fmTop, fmBottom, + v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, + hasTabs[breakIndex], hyphenEdits[breakIndex], needMultiply, + measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, chs, + paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], + paint, moreChars); + + if (endPos < spanEnd) { + // preserve metrics for current span + fmTop = Math.min(defaultTop, fm.top); + fmBottom = Math.max(defaultBottom, fm.bottom); + fmAscent = Math.min(defaultAscent, fm.ascent); + fmDescent = Math.max(defaultDescent, fm.descent); + } else { + fmTop = fmBottom = fmAscent = fmDescent = 0; + } - if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) { - return; - } + here = endPos; + breakIndex++; + + if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) { + return; } } + } - if (paraEnd == bufEnd) { - break; - } - } finally { - if (TRACE_LAYOUT) { - Trace.endSection(); - } + if (paraEnd == bufEnd) { + break; } } @@ -1227,18 +1179,9 @@ public class StaticLayout extends Layout { (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) && ellipsize == TextUtils.TruncateAt.END); if (doEllipsis) { - if (TRACE_LAYOUT) { - Trace.beginSection("StaticLayout#calculateEllipsis"); - } - try { - calculateEllipsis(start, end, measured, widthStart, - ellipsisWidth, ellipsize, j, - textWidth, paint, forceEllipsis); - } finally { - if (TRACE_LAYOUT) { - Trace.endSection(); - } - } + calculateEllipsis(start, end, measured, widthStart, + ellipsisWidth, ellipsize, j, + textWidth, paint, forceEllipsis); } else { mLines[mColumns * j + ELLIPSIS_START] = 0; mLines[mColumns * j + ELLIPSIS_COUNT] = 0; diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index a43947806fa7..bde9c7770eb7 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -28,7 +28,6 @@ import android.graphics.RectF; import android.graphics.text.PositionedGlyphs; import android.graphics.text.TextRunShaper; import android.os.Build; -import android.os.Trace; import android.text.Layout.Directions; import android.text.Layout.TabStops; import android.text.style.CharacterStyle; @@ -57,8 +56,6 @@ import java.util.ArrayList; public class TextLine { private static final boolean DEBUG = false; - private static final boolean TRACE_TEXTLINE = Build.isDebuggable(); - private static final char TAB_CHAR = '\t'; private TextPaint mPaint; @@ -433,37 +430,28 @@ public class TextLine { * @param bottom the bottom of the line */ void draw(Canvas c, float x, int top, int y, int bottom) { - if (TRACE_TEXTLINE) { - Trace.beginSection("TextLine#draw"); - } - try { - float h = 0; - final int runCount = mDirections.getRunCount(); - for (int runIndex = 0; runIndex < runCount; runIndex++) { - final int runStart = mDirections.getRunStart(runIndex); - if (runStart > mLen) break; - final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen); - final boolean runIsRtl = mDirections.isRunRtl(runIndex); - - final int runFlag = calculateRunFlag(runIndex, runCount, mDir); - - int segStart = runStart; - for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { - if (j == runLimit || charAt(j) == TAB_CHAR) { - h += drawRun(c, segStart, j, runIsRtl, x + h, top, y, bottom, - runIndex != (runCount - 1) || j != mLen, runFlag); - - if (j != runLimit) { // charAt(j) == TAB_CHAR - h = mDir * nextTab(h * mDir); - } - segStart = j + 1; + float h = 0; + final int runCount = mDirections.getRunCount(); + for (int runIndex = 0; runIndex < runCount; runIndex++) { + final int runStart = mDirections.getRunStart(runIndex); + if (runStart > mLen) break; + final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen); + final boolean runIsRtl = mDirections.isRunRtl(runIndex); + + final int runFlag = calculateRunFlag(runIndex, runCount, mDir); + + int segStart = runStart; + for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { + if (j == runLimit || charAt(j) == TAB_CHAR) { + h += drawRun(c, segStart, j, runIsRtl, x + h, top, y, bottom, + runIndex != (runCount - 1) || j != mLen, runFlag); + + if (j != runLimit) { // charAt(j) == TAB_CHAR + h = mDir * nextTab(h * mDir); } + segStart = j + 1; } } - } finally { - if (TRACE_TEXTLINE) { - Trace.endSection(); - } } } @@ -576,76 +564,63 @@ public class TextLine { */ public float measure(@IntRange(from = 0) int offset, boolean trailing, @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable LineInfo lineInfo) { - if (TRACE_TEXTLINE) { - Trace.beginSection("TextLine#measure"); + if (offset > mLen) { + throw new IndexOutOfBoundsException( + "offset(" + offset + ") should be less than line limit(" + mLen + ")"); + } + if (lineInfo != null) { + lineInfo.setClusterCount(0); + } + final int target = trailing ? offset - 1 : offset; + if (target < 0) { + return 0; } - try { - if (offset > mLen) { - throw new IndexOutOfBoundsException( - "offset(" + offset + ") should be less than line limit(" + mLen + ")"); - } - if (lineInfo != null) { - lineInfo.setClusterCount(0); - } - final int target = trailing ? offset - 1 : offset; - if (target < 0) { - return 0; - } - float h = 0; - final int runCount = mDirections.getRunCount(); - for (int runIndex = 0; runIndex < runCount; runIndex++) { - final int runStart = mDirections.getRunStart(runIndex); - if (runStart > mLen) break; - final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen); - final boolean runIsRtl = mDirections.isRunRtl(runIndex); - final int runFlag = calculateRunFlag(runIndex, runCount, mDir); - - int segStart = runStart; - for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { - if (j == runLimit || charAt(j) == TAB_CHAR) { - final boolean targetIsInThisSegment = target >= segStart && target < j; - final boolean sameDirection = - (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; - - if (targetIsInThisSegment && sameDirection) { - return h + measureRun(segStart, offset, j, runIsRtl, fmi, drawBounds, - null, - 0, h, lineInfo, runFlag); - } + float h = 0; + final int runCount = mDirections.getRunCount(); + for (int runIndex = 0; runIndex < runCount; runIndex++) { + final int runStart = mDirections.getRunStart(runIndex); + if (runStart > mLen) break; + final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen); + final boolean runIsRtl = mDirections.isRunRtl(runIndex); + final int runFlag = calculateRunFlag(runIndex, runCount, mDir); - final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi, - drawBounds, - null, 0, h, lineInfo, runFlag); - h += sameDirection ? segmentWidth : -segmentWidth; + int segStart = runStart; + for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { + if (j == runLimit || charAt(j) == TAB_CHAR) { + final boolean targetIsInThisSegment = target >= segStart && target < j; + final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; - if (targetIsInThisSegment) { - return h + measureRun(segStart, offset, j, runIsRtl, null, null, null, - 0, - h, lineInfo, runFlag); - } + if (targetIsInThisSegment && sameDirection) { + return h + measureRun(segStart, offset, j, runIsRtl, fmi, drawBounds, null, + 0, h, lineInfo, runFlag); + } - if (j != runLimit) { // charAt(j) == TAB_CHAR - if (offset == j) { - return h; - } - h = mDir * nextTab(h * mDir); - if (target == j) { - return h; - } - } + final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi, drawBounds, + null, 0, h, lineInfo, runFlag); + h += sameDirection ? segmentWidth : -segmentWidth; - segStart = j + 1; + if (targetIsInThisSegment) { + return h + measureRun(segStart, offset, j, runIsRtl, null, null, null, 0, + h, lineInfo, runFlag); + } + + if (j != runLimit) { // charAt(j) == TAB_CHAR + if (offset == j) { + return h; + } + h = mDir * nextTab(h * mDir); + if (target == j) { + return h; + } } - } - } - return h; - } finally { - if (TRACE_TEXTLINE) { - Trace.endSection(); + segStart = j + 1; + } } } + + return h; } /** |