Merge "Revert "Add trace point for the text measurement/draw"" into main
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index e95b216..9db8aa1 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -28,7 +28,6 @@
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 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.
- }
- }
-
- 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);
- }
- }
-
- 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();
+ 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();
+ }
+
+ 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);
+
+ return fm;
}
@Override
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 99ce0ef..cce4f7b 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -31,7 +31,6 @@
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 @@
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void reflow(CharSequence s, int where, int before, int after) {
- if (TRACE_LAYOUT) {
- Trace.beginSection("DynamicLayout#reflow");
+ if (s != mBase)
+ return;
+
+ CharSequence text = mDisplay;
+ int len = text.length();
+
+ // 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 diff = where - find;
+ before += diff;
+ after += diff;
+ where -= diff;
}
- try {
- if (s != mBase) {
- return;
- }
- CharSequence text = mDisplay;
- int len = text.length();
+ // seek forward to the end of the paragraph
- // seek back to the start 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 find = TextUtils.lastIndexOf(text, '\n', where - 1);
- if (find < 0) {
- find = 0;
- } else {
- find = find + 1;
- }
+ int change = look - (where + after);
+ before += change;
+ after += change;
- {
- int diff = where - find;
- before += diff;
- after += diff;
- where -= diff;
- }
+ // seek further out to cover anything that is forced to wrap together
- // seek forward to the end of the paragraph
+ if (text instanceof Spanned) {
+ Spanned sp = (Spanned) text;
+ boolean again;
- int look = TextUtils.indexOf(text, '\n', where + after);
- if (look < 0) {
- look = len;
- } else {
- look++; // we want the index after the \n
- }
+ do {
+ again = false;
- int change = look - (where + after);
- before += change;
- after += change;
+ Object[] force = sp.getSpans(where, where + after,
+ WrapTogetherSpan.class);
- // seek further out to cover anything that is forced to wrap together
+ for (int i = 0; i < force.length; i++) {
+ int st = sp.getSpanStart(force[i]);
+ int en = sp.getSpanEnd(force[i]);
- if (text instanceof Spanned) {
- Spanned sp = (Spanned) text;
- boolean again;
+ if (st < where) {
+ again = true;
- do {
- again = false;
-
- Object[] force = sp.getSpans(where, where + after,
- WrapTogetherSpan.class);
-
- for (int i = 0; i < force.length; i++) {
- int st = sp.getSpanStart(force[i]);
- int en = sp.getSpanEnd(force[i]);
-
- if (st < where) {
- again = true;
-
- int diff = where - st;
- before += diff;
- after += diff;
- where -= diff;
- }
-
- if (en > where + after) {
- 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
+ if (en > where + after) {
+ again = true;
- int startline = getLineForOffset(where);
- int startv = getLineTop(startline);
+ int diff = en - (where + after);
+ before += diff;
+ after += diff;
+ }
+ }
+ } while (again);
+ }
- int endline = getLineForOffset(where + before);
- if (where + after == len) {
- endline = getLineCount();
- }
- int endv = getLineTop(endline);
- boolean islast = (endline == getLineCount());
+ // find affected region of old layout
- // generate new layout for affected text
+ int startline = getLineForOffset(where);
+ int startv = getLineTop(startline);
- StaticLayout reflowed;
- StaticLayout.Builder b;
+ int endline = getLineForOffset(where + before);
+ if (where + after == len)
+ endline = getLineCount();
+ int endv = getLineTop(endline);
+ boolean islast = (endline == getLineCount());
- synchronized (sLock) {
- reflowed = sStaticLayout;
- b = sBuilder;
- sStaticLayout = null;
- sBuilder = null;
- }
+ // generate new layout for affected text
- if (b == null) {
- b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth());
- }
+ StaticLayout reflowed;
+ StaticLayout.Builder b;
- 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);
+ synchronized (sLock) {
+ reflowed = sStaticLayout;
+ b = sBuilder;
+ sStaticLayout = null;
+ sBuilder = null;
+ }
- 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 (b == null) {
+ b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth());
+ }
- if (where + after != len && reflowed.getLineStart(n - 1) == where + after) {
- n--;
- }
+ 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);
- // remove affected lines from old layout
- mInts.deleteAt(startline, endline - startline);
- mObjects.deleteAt(startline, endline - startline);
+ 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.
- // adjust offsets in layout for new height and offsets
+ if (where + after != len && reflowed.getLineStart(n - 1) == where + after)
+ n--;
- int ht = reflowed.getLineTop(n);
- int toppad = 0, botpad = 0;
+ // remove affected lines from old layout
+ mInts.deleteAt(startline, endline - startline);
+ mObjects.deleteAt(startline, endline - startline);
- if (mIncludePad && startline == 0) {
- toppad = reflowed.getTopPadding();
- mTopPadding = toppad;
- ht -= toppad;
- }
- if (mIncludePad && islast) {
- botpad = reflowed.getBottomPadding();
- mBottomPadding = botpad;
- ht += botpad;
- }
+ // adjust offsets in layout for new height and offsets
- mInts.adjustValuesBelow(startline, START, after - before);
- mInts.adjustValuesBelow(startline, TOP, startv - endv + ht);
+ int ht = reflowed.getLineTop(n);
+ int toppad = 0, botpad = 0;
- // insert new layout
+ if (mIncludePad && startline == 0) {
+ toppad = reflowed.getTopPadding();
+ mTopPadding = toppad;
+ ht -= toppad;
+ }
+ if (mIncludePad && islast) {
+ botpad = reflowed.getBottomPadding();
+ mBottomPadding = botpad;
+ ht += botpad;
+ }
- int[] ints;
+ mInts.adjustValuesBelow(startline, START, after - before);
+ mInts.adjustValuesBelow(startline, TOP, startv - endv + ht);
+
+ // insert new layout
+
+ int[] ints;
+
+ if (mEllipsize) {
+ ints = new int[COLUMNS_ELLIPSIZE];
+ ints[ELLIPSIS_START] = ELLIPSIS_UNDEFINED;
+ } else {
+ ints = new int[COLUMNS_NORMAL];
+ }
+
+ Directions[] objects = new Directions[1];
+
+ 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 top = reflowed.getLineTop(i) + startv;
+ if (i > 0)
+ top -= toppad;
+ ints[TOP] = top;
+
+ int desc = reflowed.getLineDescent(i);
+ if (i == n - 1)
+ desc += botpad;
+
+ ints[DESCENT] = desc;
+ ints[EXTRA] = reflowed.getLineExtra(i);
+ objects[0] = reflowed.getLineDirections(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;
if (mEllipsize) {
- ints = new int[COLUMNS_ELLIPSIZE];
- ints[ELLIPSIS_START] = ELLIPSIS_UNDEFINED;
- } else {
- ints = new int[COLUMNS_NORMAL];
+ ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i);
+ ints[ELLIPSIS_COUNT] = reflowed.getEllipsisCount(i);
}
- Directions[] objects = new Directions[1];
+ mInts.insertAt(startline + i, ints);
+ mObjects.insertAt(startline + i, objects);
+ }
- 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;
+ updateBlocks(startline, endline - 1, n);
- int top = reflowed.getLineTop(i) + startv;
- if (i > 0) {
- top -= toppad;
- }
- ints[TOP] = top;
-
- int desc = reflowed.getLineDescent(i);
- if (i == n - 1) {
- desc += botpad;
- }
-
- ints[DESCENT] = desc;
- ints[EXTRA] = reflowed.getLineExtra(i);
- objects[0] = reflowed.getLineDirections(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;
-
- if (mEllipsize) {
- ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i);
- ints[ELLIPSIS_COUNT] = reflowed.getEllipsisCount(i);
- }
-
- mInts.insertAt(startline + i, ints);
- mObjects.insertAt(startline + i, objects);
- }
-
- updateBlocks(startline, endline - 1, n);
-
- b.finish();
- synchronized (sLock) {
- sStaticLayout = reflowed;
- sBuilder = b;
- }
- } finally {
- if (TRACE_LAYOUT) {
- Trace.endSection();
- }
+ 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 ce238a7..8dee4b1 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -39,7 +39,6 @@
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 @@
* 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 @@
@Nullable Path selectionPath,
@Nullable Paint selectionPaint,
int cursorOffsetVertical) {
- if (TRACE_LAYOUT) {
- Trace.beginSection("Layout#draw");
+ float leftShift = 0;
+ if (mUseBoundsForWidth && mShiftDrawingOffsetForStartOverhang) {
+ RectF drawingRect = computeDrawingBoundingBox();
+ if (drawingRect.left < 0) {
+ leftShift = -drawingRect.left;
+ canvas.translate(leftShift, 0);
+ }
}
- try {
- 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 14401a6..5f6a9bd 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -25,8 +25,6 @@
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 @@
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 @@
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 @@
@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());
- }
- try {
- ArrayList<ParagraphInfo> result = new ArrayList<>();
+ 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;
+ 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;
+ }
+
+ 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 d1b14d1..3dd3a9e 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -542,20 +542,10 @@
*/
@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 @@
*/
/* 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 @@
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 @@
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 @@
}
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);
+ 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);
+
+ 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]);
+
+ if (o < paraStart) {
+ // starts in this layout, before the
+ // current paragraph
+
+ chooseHtv[i] = getLineTop(getLineForOffset(o));
+ } else {
+ // starts in this paragraph
+
+ chooseHtv[i] = v;
+ }
+ }
+ }
}
- 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());
- }
+ // 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;
+ }
+ }
- chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
+ final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
+ final char[] chs = measuredPara.getChars();
+ final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
+ final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
- if (chooseHt.length == 0) {
- chooseHt = null; // So that out() would not assume it has any contents
+ 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];
+ }
+
+ 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));
+ }
+
+ 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 {
- 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]);
-
- if (o < paraStart) {
- // starts in this layout, before the
- // current paragraph
-
- chooseHtv[i] = getLineTop(getLineForOffset(o));
- } else {
- // starts in this paragraph
-
- chooseHtv[i] = v;
- }
+ for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
+ width += measuredPara.getCharWidthAt(j);
}
}
+ hasTab |= hasTabs[i];
}
- // 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();
+ // 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;
+
+ breakCount = remainingLineCount;
+ }
+
+ // 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;
+ }
+
+ // skip breaks ending before current span range
+ while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
+ breakIndex++;
+ }
+
+ 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;
}
- 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();
-
- constraints.setWidth(restWidth);
- constraints.setIndent(firstWidth, firstWidthLineCount);
- constraints.setTabStops(variableTabStops, TAB_INCREMENT);
-
- if (TRACE_LAYOUT) {
- Trace.beginSection("LineBreaker#computeLineBreaks");
- }
- LineBreaker.Result res;
- try {
- res = lineBreaker.computeLineBreaks(
- measuredPara.getMeasuredText(), constraints, mLineCount);
- } finally {
- if (TRACE_LAYOUT) {
- Trace.endSection();
- }
- }
- 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];
- }
-
- 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));
- }
-
- 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;
-
- breakCount = remainingLineCount;
- }
-
- // 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;
- }
-
- // skip breaks ending before current span range
- while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
- breakIndex++;
- }
-
- 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;
- }
- }
-
- 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;
- }
-
- here = endPos;
- breakIndex++;
-
- if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
- return;
+ if (descent > fmBottom) {
+ fmBottom = descent;
}
}
- }
- if (paraEnd == bufEnd) {
- break;
+ 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;
+ }
+
+ here = endPos;
+ breakIndex++;
+
+ if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
+ return;
+ }
}
- } finally {
- if (TRACE_LAYOUT) {
- Trace.endSection();
- }
+ }
+
+ if (paraEnd == bufEnd) {
+ break;
}
}
@@ -1227,18 +1179,9 @@
(!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 a439478..bde9c77 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -28,7 +28,6 @@
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 @@
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 @@
* @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);
+ 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 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);
+ 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;
+ 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 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 + ")");
}
- 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;
- }
+ 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);
+ 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;
+ 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);
- }
-
- final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi,
- drawBounds,
- null, 0, h, lineInfo, runFlag);
- h += sameDirection ? segmentWidth : -segmentWidth;
-
- 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;
- }
- }
-
- segStart = j + 1;
+ if (targetIsInThisSegment && sameDirection) {
+ return h + measureRun(segStart, offset, j, runIsRtl, fmi, drawBounds, null,
+ 0, h, lineInfo, runFlag);
}
+
+ final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi, drawBounds,
+ null, 0, h, lineInfo, runFlag);
+ h += sameDirection ? segmentWidth : -segmentWidth;
+
+ 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;
+ }
+ }
+
+ segStart = j + 1;
}
}
-
- return h;
- } finally {
- if (TRACE_TEXTLINE) {
- Trace.endSection();
- }
}
+
+ return h;
}
/**