Loading core/java/android/text/BoringLayout.java +47 −36 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ 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; /** Loading Loading @@ -567,6 +568,11 @@ 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()); } try { final int textLength = text.length(); if (hasAnyInterestingChars(text, textLength)) { return null; // There are some interesting characters. Not boring. Loading @@ -592,9 +598,9 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback 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. // 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); } Loading @@ -610,6 +616,11 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback TextLine.recycle(line); return fm; } finally { if (TRACE_LAYOUT) { Trace.endSection(); } } } @Override Loading core/java/android/text/DynamicLayout.java +180 −162 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ 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; Loading Loading @@ -636,8 +637,13 @@ public class DynamicLayout extends Layout { /** @hide */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public void reflow(CharSequence s, int where, int before, int after) { if (s != mBase) if (TRACE_LAYOUT) { Trace.beginSection("DynamicLayout#reflow"); } try { if (s != mBase) { return; } CharSequence text = mDisplay; int len = text.length(); Loading @@ -645,10 +651,11 @@ public class DynamicLayout extends Layout { // seek back to the start of the paragraph int find = TextUtils.lastIndexOf(text, '\n', where - 1); if (find < 0) if (find < 0) { find = 0; else } else { find = find + 1; } { int diff = where - find; Loading @@ -660,10 +667,11 @@ public class DynamicLayout extends Layout { // seek forward to the end of the paragraph int look = TextUtils.indexOf(text, '\n', where + after); if (look < 0) if (look < 0) { look = len; else } else { look++; // we want the index after the \n } int change = look - (where + after); before += change; Loading Loading @@ -711,8 +719,9 @@ public class DynamicLayout extends Layout { int startv = getLineTop(startline); int endline = getLineForOffset(where + before); if (where + after == len) if (where + after == len) { endline = getLineCount(); } int endv = getLineTop(endline); boolean islast = (endline == getLineCount()); Loading Loading @@ -751,14 +760,16 @@ public class DynamicLayout extends Layout { .setMinimumFontMetrics(mMinimumFontMetrics) .setCalculateBounds(true); reflowed = b.buildPartialStaticLayoutForDynamicLayout(true /* trackpadding */, reflowed); 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) if (where + after != len && reflowed.getLineStart(n - 1) == where + after) { n--; } // remove affected lines from old layout mInts.deleteAt(startline, endline - startline); Loading Loading @@ -803,13 +814,15 @@ public class DynamicLayout extends Layout { ints[TAB] |= reflowed.getLineContainsTab(i) ? TAB_MASK : 0; int top = reflowed.getLineTop(i) + startv; if (i > 0) if (i > 0) { top -= toppad; } ints[TOP] = top; int desc = reflowed.getLineDescent(i); if (i == n - 1) if (i == n - 1) { desc += botpad; } ints[DESCENT] = desc; ints[EXTRA] = reflowed.getLineExtra(i); Loading @@ -819,8 +832,8 @@ public class DynamicLayout extends Layout { 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; contentMayProtrudeFromLineTopOrBottom(text, start, end) ? MAY_PROTRUDE_FROM_TOP_OR_BOTTOM_MASK : 0; if (mEllipsize) { ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i); Loading @@ -838,6 +851,11 @@ public class DynamicLayout extends Layout { sStaticLayout = reflowed; sBuilder = b; } } finally { if (TRACE_LAYOUT) { Trace.endSection(); } } } private boolean contentMayProtrudeFromLineTopOrBottom(CharSequence text, int start, int end) { Loading core/java/android/text/Layout.java +46 −29 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ 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; Loading Loading @@ -70,6 +71,11 @@ 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, Loading Loading @@ -472,6 +478,10 @@ 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(); Loading @@ -488,17 +498,19 @@ public abstract class Layout { if (shouldDrawHighlightsOnTop(canvas)) { drawBackground(canvas, firstLine, lastLine); } else { drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, cursorOffsetVertical, 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. // 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, drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, cursorOffsetVertical, firstLine, lastLine); } Loading @@ -507,6 +519,11 @@ public abstract class Layout { // save/restore disappears the toggle switch drawables. canvas.translate(-leftShift, 0); } } finally { if (TRACE_LAYOUT) { Trace.endSection(); } } } private static boolean shouldDrawHighlightsOnTop(Canvas canvas) { Loading core/java/android/text/PrecomputedText.java +94 −66 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ 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; Loading Loading @@ -78,6 +80,8 @@ 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}. * Loading Loading @@ -447,8 +451,15 @@ public class PrecomputedText implements Spannable { private static ParagraphInfo[] createMeasuredParagraphsFromPrecomputedText( @NonNull PrecomputedText pct, @NonNull Params params, boolean computeLayout) { final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; if (TRACE_PCT) { Trace.beginSection("PrecomputedText#createMeasuredParagraphsFromPrecomputedText"); Trace.setCounter("PrecomputedText#textCharCount", pct.length()); } 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()) Loading Loading @@ -476,6 +487,11 @@ public class PrecomputedText implements Spannable { pct.getMeasuredParagraph(i), null /* no recycle */))); } return result.toArray(new ParagraphInfo[result.size()]); } finally { if (TRACE_PCT) { Trace.endSection(); } } } /** @hide */ Loading @@ -483,12 +499,19 @@ 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()); } 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 boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; final int hyphenationMode; if (needHyphenation) { hyphenationMode = isFastHyphenation(params.getHyphenationFrequency()) Loading Loading @@ -530,6 +553,11 @@ public class PrecomputedText implements Spannable { null /* no recycle */))); } return result.toArray(new ParagraphInfo[result.size()]); } finally { if (TRACE_PCT) { Trace.endSection(); } } } // Use PrecomputedText.create instead. Loading core/java/android/text/StaticLayout.java +275 −218 Original line number Diff line number Diff line Loading @@ -542,10 +542,20 @@ 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(); } } } /** Loading @@ -562,16 +572,21 @@ 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()); } try { if (recycle == null) { recycle = new StaticLayout(); } Trace.beginSection("Generating StaticLayout For DynamicLayout"); try { recycle.generate(this, mIncludePad, trackpadding); return recycle; } finally { if (TRACE_LAYOUT) { Trace.endSection(); } return recycle; } } private CharSequence mText; Loading Loading @@ -727,12 +742,7 @@ public class StaticLayout extends Layout { mLeftIndents = b.mLeftIndents; mRightIndents = b.mRightIndents; Trace.beginSection("Constructing StaticLayout"); try { generate(b, b.mIncludePad, trackPadding); } finally { Trace.endSection(); } } private static int getBaseHyphenationFrequency(int frequency) { Loading Loading @@ -842,6 +852,10 @@ 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) Loading @@ -850,6 +864,11 @@ public class StaticLayout extends Layout { .setLineBreakConfig(b.mLineBreakConfig) .build(); precomputed = PrecomputedText.create(precomputed, newParams); } finally { if (TRACE_LAYOUT) { Trace.endSection(); } } paragraphInfo = precomputed.getParagraphInfo(); break; case PrecomputedText.Params.USABLE: Loading @@ -860,13 +879,27 @@ 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(); } } } 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; Loading Loading @@ -942,8 +975,18 @@ public class StaticLayout extends Layout { constraints.setIndent(firstWidth, firstWidthLineCount); constraints.setTabStops(variableTabStops, TAB_INCREMENT); LineBreaker.Result res = lineBreaker.computeLineBreaks( 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; Loading Loading @@ -1045,9 +1088,9 @@ public class StaticLayout extends Layout { ? 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. // 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; Loading Loading @@ -1087,6 +1130,11 @@ public class StaticLayout extends Layout { if (paraEnd == bufEnd) { break; } } finally { if (TRACE_LAYOUT) { Trace.endSection(); } } } if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) Loading Loading @@ -1179,9 +1227,18 @@ 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(); } } } else { mLines[mColumns * j + ELLIPSIS_START] = 0; mLines[mColumns * j + ELLIPSIS_COUNT] = 0; Loading Loading
core/java/android/text/BoringLayout.java +47 −36 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ 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; /** Loading Loading @@ -567,6 +568,11 @@ 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()); } try { final int textLength = text.length(); if (hasAnyInterestingChars(text, textLength)) { return null; // There are some interesting characters. Not boring. Loading @@ -592,9 +598,9 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback 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. // 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); } Loading @@ -610,6 +616,11 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback TextLine.recycle(line); return fm; } finally { if (TRACE_LAYOUT) { Trace.endSection(); } } } @Override Loading
core/java/android/text/DynamicLayout.java +180 −162 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ 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; Loading Loading @@ -636,8 +637,13 @@ public class DynamicLayout extends Layout { /** @hide */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public void reflow(CharSequence s, int where, int before, int after) { if (s != mBase) if (TRACE_LAYOUT) { Trace.beginSection("DynamicLayout#reflow"); } try { if (s != mBase) { return; } CharSequence text = mDisplay; int len = text.length(); Loading @@ -645,10 +651,11 @@ public class DynamicLayout extends Layout { // seek back to the start of the paragraph int find = TextUtils.lastIndexOf(text, '\n', where - 1); if (find < 0) if (find < 0) { find = 0; else } else { find = find + 1; } { int diff = where - find; Loading @@ -660,10 +667,11 @@ public class DynamicLayout extends Layout { // seek forward to the end of the paragraph int look = TextUtils.indexOf(text, '\n', where + after); if (look < 0) if (look < 0) { look = len; else } else { look++; // we want the index after the \n } int change = look - (where + after); before += change; Loading Loading @@ -711,8 +719,9 @@ public class DynamicLayout extends Layout { int startv = getLineTop(startline); int endline = getLineForOffset(where + before); if (where + after == len) if (where + after == len) { endline = getLineCount(); } int endv = getLineTop(endline); boolean islast = (endline == getLineCount()); Loading Loading @@ -751,14 +760,16 @@ public class DynamicLayout extends Layout { .setMinimumFontMetrics(mMinimumFontMetrics) .setCalculateBounds(true); reflowed = b.buildPartialStaticLayoutForDynamicLayout(true /* trackpadding */, reflowed); 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) if (where + after != len && reflowed.getLineStart(n - 1) == where + after) { n--; } // remove affected lines from old layout mInts.deleteAt(startline, endline - startline); Loading Loading @@ -803,13 +814,15 @@ public class DynamicLayout extends Layout { ints[TAB] |= reflowed.getLineContainsTab(i) ? TAB_MASK : 0; int top = reflowed.getLineTop(i) + startv; if (i > 0) if (i > 0) { top -= toppad; } ints[TOP] = top; int desc = reflowed.getLineDescent(i); if (i == n - 1) if (i == n - 1) { desc += botpad; } ints[DESCENT] = desc; ints[EXTRA] = reflowed.getLineExtra(i); Loading @@ -819,8 +832,8 @@ public class DynamicLayout extends Layout { 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; contentMayProtrudeFromLineTopOrBottom(text, start, end) ? MAY_PROTRUDE_FROM_TOP_OR_BOTTOM_MASK : 0; if (mEllipsize) { ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i); Loading @@ -838,6 +851,11 @@ public class DynamicLayout extends Layout { sStaticLayout = reflowed; sBuilder = b; } } finally { if (TRACE_LAYOUT) { Trace.endSection(); } } } private boolean contentMayProtrudeFromLineTopOrBottom(CharSequence text, int start, int end) { Loading
core/java/android/text/Layout.java +46 −29 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ 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; Loading Loading @@ -70,6 +71,11 @@ 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, Loading Loading @@ -472,6 +478,10 @@ 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(); Loading @@ -488,17 +498,19 @@ public abstract class Layout { if (shouldDrawHighlightsOnTop(canvas)) { drawBackground(canvas, firstLine, lastLine); } else { drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, cursorOffsetVertical, 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. // 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, drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint, cursorOffsetVertical, firstLine, lastLine); } Loading @@ -507,6 +519,11 @@ public abstract class Layout { // save/restore disappears the toggle switch drawables. canvas.translate(-leftShift, 0); } } finally { if (TRACE_LAYOUT) { Trace.endSection(); } } } private static boolean shouldDrawHighlightsOnTop(Canvas canvas) { Loading
core/java/android/text/PrecomputedText.java +94 −66 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ 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; Loading Loading @@ -78,6 +80,8 @@ 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}. * Loading Loading @@ -447,8 +451,15 @@ public class PrecomputedText implements Spannable { private static ParagraphInfo[] createMeasuredParagraphsFromPrecomputedText( @NonNull PrecomputedText pct, @NonNull Params params, boolean computeLayout) { final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; if (TRACE_PCT) { Trace.beginSection("PrecomputedText#createMeasuredParagraphsFromPrecomputedText"); Trace.setCounter("PrecomputedText#textCharCount", pct.length()); } 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()) Loading Loading @@ -476,6 +487,11 @@ public class PrecomputedText implements Spannable { pct.getMeasuredParagraph(i), null /* no recycle */))); } return result.toArray(new ParagraphInfo[result.size()]); } finally { if (TRACE_PCT) { Trace.endSection(); } } } /** @hide */ Loading @@ -483,12 +499,19 @@ 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()); } 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 boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; final int hyphenationMode; if (needHyphenation) { hyphenationMode = isFastHyphenation(params.getHyphenationFrequency()) Loading Loading @@ -530,6 +553,11 @@ public class PrecomputedText implements Spannable { null /* no recycle */))); } return result.toArray(new ParagraphInfo[result.size()]); } finally { if (TRACE_PCT) { Trace.endSection(); } } } // Use PrecomputedText.create instead. Loading
core/java/android/text/StaticLayout.java +275 −218 Original line number Diff line number Diff line Loading @@ -542,10 +542,20 @@ 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(); } } } /** Loading @@ -562,16 +572,21 @@ 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()); } try { if (recycle == null) { recycle = new StaticLayout(); } Trace.beginSection("Generating StaticLayout For DynamicLayout"); try { recycle.generate(this, mIncludePad, trackpadding); return recycle; } finally { if (TRACE_LAYOUT) { Trace.endSection(); } return recycle; } } private CharSequence mText; Loading Loading @@ -727,12 +742,7 @@ public class StaticLayout extends Layout { mLeftIndents = b.mLeftIndents; mRightIndents = b.mRightIndents; Trace.beginSection("Constructing StaticLayout"); try { generate(b, b.mIncludePad, trackPadding); } finally { Trace.endSection(); } } private static int getBaseHyphenationFrequency(int frequency) { Loading Loading @@ -842,6 +852,10 @@ 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) Loading @@ -850,6 +864,11 @@ public class StaticLayout extends Layout { .setLineBreakConfig(b.mLineBreakConfig) .build(); precomputed = PrecomputedText.create(precomputed, newParams); } finally { if (TRACE_LAYOUT) { Trace.endSection(); } } paragraphInfo = precomputed.getParagraphInfo(); break; case PrecomputedText.Params.USABLE: Loading @@ -860,13 +879,27 @@ 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(); } } } 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; Loading Loading @@ -942,8 +975,18 @@ public class StaticLayout extends Layout { constraints.setIndent(firstWidth, firstWidthLineCount); constraints.setTabStops(variableTabStops, TAB_INCREMENT); LineBreaker.Result res = lineBreaker.computeLineBreaks( 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; Loading Loading @@ -1045,9 +1088,9 @@ public class StaticLayout extends Layout { ? 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. // 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; Loading Loading @@ -1087,6 +1130,11 @@ public class StaticLayout extends Layout { if (paraEnd == bufEnd) { break; } } finally { if (TRACE_LAYOUT) { Trace.endSection(); } } } if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) Loading Loading @@ -1179,9 +1227,18 @@ 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(); } } } else { mLines[mColumns * j + ELLIPSIS_START] = 0; mLines[mColumns * j + ELLIPSIS_COUNT] = 0; Loading