diff options
| author | Roozbeh Pournader <roozbeh@google.com> | 2017-05-23 16:26:48 -0700 |
|---|---|---|
| committer | Roozbeh Pournader <roozbeh@google.com> | 2017-05-24 13:35:19 -0700 |
| commit | 203ffaa103e796545701de30351d92787c1e3ce3 (patch) | |
| tree | 55a04bd35a543fa5191858f5601264dc0807a0c0 /core/java/android/text/TextLine.java | |
| parent | d9f25618051dfb96f2327071e67960c5ae2b6ec2 (diff) | |
Don't let UnderlineSpans affect text width
Previously, text that was partially annotated with an UnderlineSpan
could have a different width than text without it, since underlined
text was separated into a separate piece of text and was measured
separately, causing kerning at the span boundaries to disappear.
Change-Id: I118de8524c500fb6a4b05b1bf65fe93dc67f204c
Bug: 32907446
Test: cts-tradefed run cts-dev --module CtsTextTestCases
Test: manual
Diffstat (limited to 'core/java/android/text/TextLine.java')
| -rw-r--r-- | core/java/android/text/TextLine.java | 93 |
1 files changed, 65 insertions, 28 deletions
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 756e9a0e2f2a..e287bf7741f6 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -24,6 +24,8 @@ import android.text.Layout.TabStops; import android.text.style.CharacterStyle; import android.text.style.MetricAffectingSpan; import android.text.style.ReplacementSpan; +import android.text.style.UnderlineSpan; +import android.util.IntArray; import android.util.Log; import com.android.internal.util.ArrayUtils; @@ -66,6 +68,8 @@ class TextLine { private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet = new SpanSet<ReplacementSpan>(ReplacementSpan.class); + private final IntArray mUnderlines = new IntArray(); + private static final TextLine[] sCached = new TextLine[3]; /** @@ -695,6 +699,37 @@ class TextLine { fmi.leading = Math.max(fmi.leading, previousLeading); } + private static void drawUnderline(TextPaint wp, Canvas c, int color, float thickness, + float xstart, float xend, int baseline) { + // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h + final float underlineTop = baseline + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize(); + + final int previousColor = wp.getColor(); + final Paint.Style previousStyle = wp.getStyle(); + final boolean previousAntiAlias = wp.isAntiAlias(); + + wp.setStyle(Paint.Style.FILL); + wp.setAntiAlias(true); + + wp.setColor(color); + c.drawRect(xstart, underlineTop, xend, underlineTop + thickness, wp); + + wp.setStyle(previousStyle); + wp.setColor(previousColor); + wp.setAntiAlias(previousAntiAlias); + } + + private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd, + boolean runIsRtl, int offset) { + if (mCharsValid) { + return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset); + } else { + final int delta = mStart; + return wp.getRunAdvance(mText, delta + start, delta + end, + delta + contextStart, delta + contextEnd, runIsRtl, delta + offset); + } + } + /** * Utility function for measuring and rendering text. The text must * not include a tab. @@ -734,14 +769,7 @@ class TextLine { float ret = 0; if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) { - if (mCharsValid) { - ret = wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, - runIsRtl, offset); - } else { - int delta = mStart; - ret = wp.getRunAdvance(mText, delta + start, delta + end, - delta + contextStart, delta + contextEnd, runIsRtl, delta + offset); - } + ret = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset); } if (c != null) { @@ -762,22 +790,23 @@ class TextLine { } if (wp.underlineColor != 0) { - // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h - float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize(); - - int previousColor = wp.getColor(); - Paint.Style previousStyle = wp.getStyle(); - boolean previousAntiAlias = wp.isAntiAlias(); - - wp.setStyle(Paint.Style.FILL); - wp.setAntiAlias(true); - - wp.setColor(wp.underlineColor); - c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThickness, wp); + drawUnderline(wp, c, wp.underlineColor, wp.underlineThickness, x, x + ret, y); + } - wp.setStyle(previousStyle); - wp.setColor(previousColor); - wp.setAntiAlias(previousAntiAlias); + final int numUnderlines = mUnderlines.size(); + if (numUnderlines != 0) { + // kStdUnderline_Thickness = 1/18, defined in SkTextFormatParams.h + final float thickness = (1.0f / 18.0f) * wp.getTextSize(); + for (int i = 0; i < numUnderlines; i += 2) { + final int underlineStart = Math.max(mUnderlines.get(i), start); + final int underlineEnd = Math.min(mUnderlines.get(i + 1), offset); + final float underlineXStart = getRunAdvance( + wp, start, end, contextStart, contextEnd, runIsRtl, underlineStart); + final float underlineXEnd = getRunAdvance( + wp, start, end, contextStart, contextEnd, runIsRtl, underlineEnd); + drawUnderline(wp, c, wp.getColor(), thickness, + underlineXStart, underlineXEnd, y); + } } drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl, @@ -950,19 +979,27 @@ class TextLine { continue; } + mUnderlines.clear(); for (int j = i, jnext; j < mlimit; j = jnext) { - jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) - - mStart; + jnext = mCharacterStyleSpanSet.getNextTransitionSkipping( + UnderlineSpan.class, mStart + j, mStart + inext + ) - mStart; int offset = Math.min(jnext, mlimit); wp.set(mPaint); for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) { + final int spanStart = mCharacterStyleSpanSet.spanStarts[k]; + final int spanEnd = mCharacterStyleSpanSet.spanEnds[k]; // Intentionally using >= and <= as explained above - if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) || - (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue; + if ((spanStart >= mStart + offset) || (spanEnd <= mStart + j)) continue; CharacterStyle span = mCharacterStyleSpanSet.spans[k]; - span.updateDrawState(wp); + if (span.getClass() == UnderlineSpan.class) { + mUnderlines.add(spanStart); + mUnderlines.add(spanEnd); + } else { + span.updateDrawState(wp); + } } wp.setHyphenEdit(adjustHyphenEdit(j, jnext, wp.getHyphenEdit())); |
