summaryrefslogtreecommitdiff
path: root/core/java/android/text/TextLine.java
diff options
context:
space:
mode:
authorRoozbeh Pournader <roozbeh@google.com>2017-05-23 16:26:48 -0700
committerRoozbeh Pournader <roozbeh@google.com>2017-05-24 13:35:19 -0700
commit203ffaa103e796545701de30351d92787c1e3ce3 (patch)
tree55a04bd35a543fa5191858f5601264dc0807a0c0 /core/java/android/text/TextLine.java
parentd9f25618051dfb96f2327071e67960c5ae2b6ec2 (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.java93
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()));