Loading core/java/android/text/SpanSet.java +11 −0 Original line number Diff line number Diff line Loading @@ -98,7 +98,18 @@ public class SpanSet<E> { * Similar to {@link Spanned#nextSpanTransition(int, int, Class)} */ int getNextTransition(int start, int limit) { return getNextTransitionSkipping(null, start, limit); } /** * Similar to {@link #getNextTransition(int, int)}, but skipping over spans with the exact class * provided. */ int getNextTransitionSkipping(Class skip, int start, int limit) { for (int i = 0; i < numberOfSpans; i++) { if (spans[i].getClass() == skip) { continue; } final int spanStart = spanStarts[i]; final int spanEnd = spanEnds[i]; if (spanStart > start && spanStart < limit) limit = spanStart; Loading core/java/android/text/TextLine.java +65 −28 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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]; /** Loading Loading @@ -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. Loading Loading @@ -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) { Loading @@ -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, Loading Loading @@ -950,20 +979,28 @@ 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]; if (span.getClass() == UnderlineSpan.class) { mUnderlines.add(spanStart); mUnderlines.add(spanEnd); } else { span.updateDrawState(wp); } } wp.setHyphenEdit(adjustHyphenEdit(j, jnext, wp.getHyphenEdit())); Loading Loading
core/java/android/text/SpanSet.java +11 −0 Original line number Diff line number Diff line Loading @@ -98,7 +98,18 @@ public class SpanSet<E> { * Similar to {@link Spanned#nextSpanTransition(int, int, Class)} */ int getNextTransition(int start, int limit) { return getNextTransitionSkipping(null, start, limit); } /** * Similar to {@link #getNextTransition(int, int)}, but skipping over spans with the exact class * provided. */ int getNextTransitionSkipping(Class skip, int start, int limit) { for (int i = 0; i < numberOfSpans; i++) { if (spans[i].getClass() == skip) { continue; } final int spanStart = spanStarts[i]; final int spanEnd = spanEnds[i]; if (spanStart > start && spanStart < limit) limit = spanStart; Loading
core/java/android/text/TextLine.java +65 −28 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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]; /** Loading Loading @@ -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. Loading Loading @@ -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) { Loading @@ -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, Loading Loading @@ -950,20 +979,28 @@ 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]; if (span.getClass() == UnderlineSpan.class) { mUnderlines.add(spanStart); mUnderlines.add(spanEnd); } else { span.updateDrawState(wp); } } wp.setHyphenEdit(adjustHyphenEdit(j, jnext, wp.getHyphenEdit())); Loading