summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tyler Freeman <fuego@google.com> 2024-05-01 17:40:00 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-05-01 17:40:00 +0000
commit377bcd05cbb05ec62f3f8edf76479bd1c7df554d (patch)
tree910c4010f8748dbb31a7ab3cdb1b8ff0eebb87bb
parenta4524293549d034cf4f439b88726269768190419 (diff)
parent9997ea77db34a95c9dabd31c66f70d630649c4f7 (diff)
Merge "fix(high contrast text): fix background rect overlaps other characters with multiple spans" into main
-rw-r--r--core/java/android/text/Layout.java122
-rw-r--r--core/tests/coretests/src/android/text/LayoutTest.java200
-rw-r--r--libs/hwui/hwui/DrawTextFunctor.h46
3 files changed, 271 insertions, 97 deletions
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 8dee4b19c6d3..c674968bba8a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -70,6 +70,11 @@ import java.util.Locale;
* For text that will not change, use a {@link StaticLayout}.
*/
public abstract class Layout {
+
+ // These should match the constants in framework/base/libs/hwui/hwui/DrawTextFunctor.h
+ private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 4f;
+ private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0.2f;
+
/** @hide */
@IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
LineBreaker.BREAK_STRATEGY_SIMPLE,
@@ -494,9 +499,9 @@ public abstract class Layout {
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 thick border on the text, the highlight actually makes
+ // it harder to read. 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);
@@ -922,6 +927,9 @@ public abstract class Layout {
public void drawBackground(
@NonNull Canvas canvas,
int firstLine, int lastLine) {
+
+ drawHighContrastBackground(canvas, firstLine, lastLine);
+
// First, draw LineBackgroundSpans.
// LineBackgroundSpans know nothing about the alignment, margins, or
// direction of the layout or line. XXX: Should they?
@@ -988,6 +996,66 @@ public abstract class Layout {
}
/**
+ * Draws a solid rectangle behind the text, the same color as the high contrast stroke border,
+ * to make it even easier to read.
+ *
+ * <p>We draw it here instead of in DrawTextFunctor so that multiple spans don't draw
+ * backgrounds over each other's text.
+ */
+ private void drawHighContrastBackground(@NonNull Canvas canvas, int firstLine, int lastLine) {
+ if (!shouldDrawHighlightsOnTop(canvas)) {
+ return;
+ }
+
+ var padding = Math.max(HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX,
+ mPaint.getTextSize() * HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR);
+
+ var bgPaint = mWorkPlainPaint;
+ bgPaint.reset();
+ bgPaint.setColor(isHighContrastTextDark() ? Color.WHITE : Color.BLACK);
+ bgPaint.setStyle(Paint.Style.FILL);
+
+ int start = getLineStart(firstLine);
+ int end = getLineEnd(lastLine);
+ // Draw a separate background rectangle for each line of text, that only surrounds the
+ // characters on that line.
+ forEachCharacterBounds(
+ start,
+ end,
+ firstLine,
+ lastLine,
+ new CharacterBoundsListener() {
+ int mLastLineNum = -1;
+ final RectF mLineBackground = new RectF();
+
+ @Override
+ public void onCharacterBounds(int index, int lineNum, float left, float top,
+ float right, float bottom) {
+ if (lineNum != mLastLineNum) {
+ drawRect();
+ mLineBackground.set(left, top, right, bottom);
+ mLastLineNum = lineNum;
+ } else {
+ mLineBackground.union(left, top, right, bottom);
+ }
+ }
+
+ @Override
+ public void onEnd() {
+ drawRect();
+ }
+
+ private void drawRect() {
+ if (!mLineBackground.isEmpty()) {
+ mLineBackground.inset(-padding, -padding);
+ canvas.drawRect(mLineBackground, bgPaint);
+ }
+ }
+ }
+ );
+ }
+
+ /**
* @param canvas
* @return The range of lines that need to be drawn, possibly empty.
* @hide
@@ -1682,7 +1750,7 @@ public abstract class Layout {
}
if (bounds == null) {
- throw new IllegalArgumentException("bounds can't be null.");
+ throw new IllegalArgumentException("bounds can't be null.");
}
final int neededLength = 4 * (end - start);
@@ -1698,6 +1766,34 @@ public abstract class Layout {
final int startLine = getLineForOffset(start);
final int endLine = getLineForOffset(end - 1);
+
+ forEachCharacterBounds(start, end, startLine, endLine,
+ (index, lineNum, left, lineTop, right, lineBottom) -> {
+ final int boundsIndex = boundsStart + 4 * (index - start);
+ bounds[boundsIndex] = left;
+ bounds[boundsIndex + 1] = lineTop;
+ bounds[boundsIndex + 2] = right;
+ bounds[boundsIndex + 3] = lineBottom;
+ });
+ }
+
+ /**
+ * Return the characters' bounds in the given range. The coordinates are in local text layout.
+ *
+ * @param start the start index to compute the character bounds, inclusive.
+ * @param end the end index to compute the character bounds, exclusive.
+ * @param startLine index of the line that contains {@code start}
+ * @param endLine index of the line that contains {@code end}
+ * @param listener called for each character with its bounds
+ *
+ */
+ private void forEachCharacterBounds(
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @IntRange(from = 0) int startLine,
+ @IntRange(from = 0) int endLine,
+ CharacterBoundsListener listener
+ ) {
float[] horizontalBounds = null;
for (int line = startLine; line <= endLine; ++line) {
final int lineStart = getLineStart(line);
@@ -1722,13 +1818,10 @@ public abstract class Layout {
final float left = horizontalBounds[offset * 2] + lineStartPos;
final float right = horizontalBounds[offset * 2 + 1] + lineStartPos;
- final int boundsIndex = boundsStart + 4 * (index - start);
- bounds[boundsIndex] = left;
- bounds[boundsIndex + 1] = lineTop;
- bounds[boundsIndex + 2] = right;
- bounds[boundsIndex + 3] = lineBottom;
+ listener.onCharacterBounds(index, line, left, lineTop, right, lineBottom);
}
}
+ listener.onEnd();
}
/**
@@ -4443,4 +4536,15 @@ public abstract class Layout {
public Paint.FontMetrics getMinimumFontMetrics() {
return mMinimumFontMetrics;
}
+
+ /**
+ * Callback for {@link #forEachCharacterBounds(int, int, int, int, CharacterBoundsListener)}
+ */
+ private interface CharacterBoundsListener {
+ void onCharacterBounds(int index, int lineNum, float left, float top, float right,
+ float bottom);
+
+ /** Called after the last character has been sent to {@link #onCharacterBounds}. */
+ default void onEnd() {}
+ }
}
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index f60eff69690a..1c1236279b61 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -18,9 +18,6 @@ package android.text;
import static com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -47,6 +44,8 @@ import android.text.style.StrikethroughSpan;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -63,7 +62,13 @@ public class LayoutTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
+ public final Expect expect = Expect.create();
+
+ // Line count when using MockLayout
private static final int LINE_COUNT = 5;
+ // Actual line count when using StaticLayout
+ private static final int STATIC_LINE_COUNT = 9;
private static final int LINE_HEIGHT = 12;
private static final int LINE_DESCENT = 4;
private static final CharSequence LAYOUT_TEXT = "alwei\t;sdfs\ndf @";
@@ -655,8 +660,8 @@ public class LayoutTest {
@Test
@RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
public void highContrastTextEnabled_testDrawSelectionAndHighlight_drawsHighContrastSelectionAndHighlight() {
- Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
- mAlign, mSpacingMult, mSpacingAdd);
+ Layout layout = new StaticLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
List<Path> highlightPaths = new ArrayList<>();
List<Paint> highlightPaints = new ArrayList<>();
@@ -677,9 +682,12 @@ public class LayoutTest {
layout.draw(c, highlightPaths, highlightPaints, selectionPath, selectionPaint,
/* cursorOffsetVertical= */ 0);
List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
- var textsDrawn = LINE_COUNT;
+ var textsDrawn = STATIC_LINE_COUNT;
var highlightsDrawn = 2;
- assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+ var backgroundRectsDrawn = STATIC_LINE_COUNT;
+ expect.withMessage("wrong number of drawCommands: " + drawCommands)
+ .that(drawCommands.size())
+ .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
var highlightsFound = 0;
var curLineIndex = 0;
@@ -687,29 +695,26 @@ public class LayoutTest {
MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
if (drawCommand.path != null) {
- assertThat(drawCommand.path).isEqualTo(selectionPath);
- assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
- assertThat(drawCommand.paint.getBlendMode()).isNotNull();
+ expect.that(drawCommand.path).isEqualTo(selectionPath);
+ expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+ expect.that(drawCommand.paint.getBlendMode()).isNotNull();
highlightsFound++;
} else if (drawCommand.text != null) {
- int start = layout.getLineStart(curLineIndex);
- int end = layout.getLineEnd(curLineIndex);
- assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
curLineIndex++;
- assertWithMessage("highlight is drawn on top of text")
+ expect.withMessage("highlight is drawn on top of text")
.that(highlightsFound).isEqualTo(0);
}
}
- assertThat(highlightsFound).isEqualTo(2);
+ expect.that(highlightsFound).isEqualTo(2);
}
@Test
@RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
public void highContrastTextEnabled_testDrawHighlight_drawsHighContrastHighlight() {
- Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
- mAlign, mSpacingMult, mSpacingAdd);
+ Layout layout = new StaticLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
List<Path> highlightPaths = new ArrayList<>();
List<Paint> highlightPaints = new ArrayList<>();
@@ -730,9 +735,12 @@ public class LayoutTest {
layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
/* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
- var textsDrawn = LINE_COUNT;
+ var textsDrawn = STATIC_LINE_COUNT;
var highlightsDrawn = 1;
- assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+ var backgroundRectsDrawn = STATIC_LINE_COUNT;
+ expect.withMessage("wrong number of drawCommands: " + drawCommands)
+ .that(drawCommands.size())
+ .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
var highlightsFound = 0;
var curLineIndex = 0;
@@ -740,29 +748,26 @@ public class LayoutTest {
MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
if (drawCommand.path != null) {
- assertThat(drawCommand.path).isEqualTo(selectionPath);
- assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
- assertThat(drawCommand.paint.getBlendMode()).isNotNull();
+ expect.that(drawCommand.path).isEqualTo(selectionPath);
+ expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+ expect.that(drawCommand.paint.getBlendMode()).isNotNull();
highlightsFound++;
} else if (drawCommand.text != null) {
- int start = layout.getLineStart(curLineIndex);
- int end = layout.getLineEnd(curLineIndex);
- assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
curLineIndex++;
- assertWithMessage("highlight is drawn on top of text")
+ expect.withMessage("highlight is drawn on top of text")
.that(highlightsFound).isEqualTo(0);
}
}
- assertThat(highlightsFound).isEqualTo(1);
+ expect.that(highlightsFound).isEqualTo(1);
}
@Test
@RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
public void highContrastTextDisabledByDefault_testDrawHighlight_drawsNormalHighlightBehind() {
- Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
- mAlign, mSpacingMult, mSpacingAdd);
+ Layout layout = new StaticLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
List<Path> highlightPaths = new ArrayList<>();
List<Paint> highlightPaints = new ArrayList<>();
@@ -782,9 +787,12 @@ public class LayoutTest {
layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
/* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
- var textsDrawn = LINE_COUNT;
+ var textsDrawn = STATIC_LINE_COUNT;
var highlightsDrawn = 1;
- assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+ var backgroundRectsDrawn = 0;
+ expect.withMessage("wrong number of drawCommands: " + drawCommands)
+ .that(drawCommands.size())
+ .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
var highlightsFound = 0;
var curLineIndex = 0;
@@ -792,29 +800,26 @@ public class LayoutTest {
MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
if (drawCommand.path != null) {
- assertThat(drawCommand.path).isEqualTo(selectionPath);
- assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
- assertThat(drawCommand.paint.getBlendMode()).isNull();
+ expect.that(drawCommand.path).isEqualTo(selectionPath);
+ expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+ expect.that(drawCommand.paint.getBlendMode()).isNull();
highlightsFound++;
} else if (drawCommand.text != null) {
- int start = layout.getLineStart(curLineIndex);
- int end = layout.getLineEnd(curLineIndex);
- assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
curLineIndex++;
- assertWithMessage("highlight is drawn behind text")
+ expect.withMessage("highlight is drawn behind text")
.that(highlightsFound).isGreaterThan(0);
}
}
- assertThat(highlightsFound).isEqualTo(1);
+ expect.that(highlightsFound).isEqualTo(1);
}
@Test
@RequiresFlagsDisabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
public void highContrastTextEnabledButFlagOff_testDrawHighlight_drawsNormalHighlightBehind() {
- Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
- mAlign, mSpacingMult, mSpacingAdd);
+ Layout layout = new StaticLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
List<Path> highlightPaths = new ArrayList<>();
List<Paint> highlightPaints = new ArrayList<>();
@@ -835,9 +840,12 @@ public class LayoutTest {
layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
/* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
- var textsDrawn = LINE_COUNT;
+ var textsDrawn = STATIC_LINE_COUNT;
var highlightsDrawn = 1;
- assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+ var backgroundRectsDrawn = 0;
+ expect.withMessage("wrong number of drawCommands: " + drawCommands)
+ .that(drawCommands.size())
+ .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
var highlightsFound = 0;
var curLineIndex = 0;
@@ -845,33 +853,84 @@ public class LayoutTest {
MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
if (drawCommand.path != null) {
- assertThat(drawCommand.path).isEqualTo(selectionPath);
- assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
- assertThat(drawCommand.paint.getBlendMode()).isNull();
+ expect.that(drawCommand.path).isEqualTo(selectionPath);
+ expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+ expect.that(drawCommand.paint.getBlendMode()).isNull();
highlightsFound++;
} else if (drawCommand.text != null) {
- int start = layout.getLineStart(curLineIndex);
- int end = layout.getLineEnd(curLineIndex);
- assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
curLineIndex++;
- assertWithMessage("highlight is drawn behind text")
+ expect.withMessage("highlight is drawn behind text")
.that(highlightsFound).isGreaterThan(0);
}
}
- assertThat(highlightsFound).isEqualTo(1);
+ expect.that(highlightsFound).isEqualTo(1);
}
@Test
public void mockCanvasHighContrastOverridesCorrectly() {
var canvas = new MockCanvas(100, 100);
- assertThat(canvas.isHighContrastTextEnabled()).isFalse();
+ expect.that(canvas.isHighContrastTextEnabled()).isFalse();
canvas.setHighContrastTextEnabled(true);
- assertThat(canvas.isHighContrastTextEnabled()).isTrue();
+ expect.that(canvas.isHighContrastTextEnabled()).isTrue();
canvas.setHighContrastTextEnabled(false);
- assertThat(canvas.isHighContrastTextEnabled()).isFalse();
+ expect.that(canvas.isHighContrastTextEnabled()).isFalse();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabled_testDrawLightText_drawsBlackBackgroundRects() {
+ mTextPaint.setColor(Color.parseColor("#CCAA33"));
+ Layout layout = new StaticLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(
+ c,
+ /* highlightPaths= */ null,
+ /* highlightPaints= */ null,
+ /* selectionPath= */ null,
+ /* selectionPaint= */ null,
+ /* cursorOffsetVertical= */ 0
+ );
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = STATIC_LINE_COUNT;
+ var highlightsDrawn = 0;
+ var backgroundRectsDrawn = STATIC_LINE_COUNT;
+ expect.withMessage("wrong number of drawCommands: " + drawCommands)
+ .that(drawCommands.size())
+ .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
+
+ int numBackgroundsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.rect != null) {
+ numBackgroundsFound++;
+ expect.that(drawCommand.paint.getColor()).isEqualTo(Color.BLACK);
+ expect.that(drawCommand.rect.height()).isAtLeast(LINE_HEIGHT);
+ expect.that(drawCommand.rect.width()).isGreaterThan(0);
+ float expectedY = (numBackgroundsFound) * (LINE_HEIGHT + LINE_DESCENT);
+ expect.that(drawCommand.rect.bottom).isAtLeast(expectedY);
+ } else if (drawCommand.text != null) {
+ // draw text
+ curLineIndex++;
+
+ expect.withMessage("background is drawn on top of text")
+ .that(numBackgroundsFound).isEqualTo(backgroundRectsDrawn);
+ } else {
+ fail("unexpected path drawn");
+ }
+ }
+
+ // One for each line
+ expect.that(numBackgroundsFound).isEqualTo(backgroundRectsDrawn);
}
private static final class MockCanvas extends Canvas {
@@ -881,22 +940,46 @@ public class LayoutTest {
public final float x;
public final float y;
public final Path path;
+ public final RectF rect;
public final Paint paint;
DrawCommand(String text, float x, float y, Paint paint) {
this.text = text;
this.x = x;
this.y = y;
- this.paint = paint;
+ this.paint = new Paint(paint);
path = null;
+ rect = null;
}
DrawCommand(Path path, Paint paint) {
this.path = path;
- this.paint = paint;
+ this.paint = new Paint(paint);
y = 0;
x = 0;
text = null;
+ rect = null;
+ }
+
+ DrawCommand(RectF rect, Paint paint) {
+ this.rect = new RectF(rect);
+ this.paint = new Paint(paint);
+ path = null;
+ y = 0;
+ x = 0;
+ text = null;
+ }
+
+ @Override
+ public String toString() {
+ return "DrawCommand{"
+ + "text='" + text + '\''
+ + ", x=" + x
+ + ", y=" + y
+ + ", path=" + path
+ + ", rect=" + rect
+ + ", paint=" + paint
+ + '}';
}
}
@@ -956,6 +1039,11 @@ public class LayoutTest {
mDrawCommands.add(new DrawCommand(path, p));
}
+ @Override
+ public void drawRect(RectF rect, Paint p) {
+ mDrawCommands.add(new DrawCommand(rect, p));
+ }
+
List<DrawCommand> getDrawCommands() {
return mDrawCommands;
}
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index 1fcb6920db14..cfca48084d97 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -34,7 +34,9 @@ namespace flags = com::android::graphics::hwui::flags;
namespace android {
-inline constexpr int kHighContrastTextBorderWidth = 4;
+// These should match the constants in framework/base/core/java/android/text/Layout.java
+inline constexpr float kHighContrastTextBorderWidth = 4.0f;
+inline constexpr float kHighContrastTextBorderWidthFactor = 0.2f;
static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
const Paint& paint, Canvas* canvas) {
@@ -48,7 +50,16 @@ static void simplifyPaint(int color, Paint* paint) {
paint->setShader(nullptr);
paint->setColorFilter(nullptr);
paint->setLooper(nullptr);
- paint->setStrokeWidth(kHighContrastTextBorderWidth + 0.04 * paint->getSkFont().getSize());
+
+ if (flags::high_contrast_text_small_text_rect()) {
+ paint->setStrokeWidth(
+ std::max(kHighContrastTextBorderWidth,
+ kHighContrastTextBorderWidthFactor * paint->getSkFont().getSize()));
+ } else {
+ auto borderWidthFactor = 0.04f;
+ paint->setStrokeWidth(kHighContrastTextBorderWidth +
+ borderWidthFactor * paint->getSkFont().getSize());
+ }
paint->setStrokeJoin(SkPaint::kRound_Join);
paint->setLooper(nullptr);
}
@@ -106,36 +117,7 @@ public:
Paint outlinePaint(paint);
simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
- if (flags::high_contrast_text_small_text_rect()) {
- const SkFont& font = paint.getSkFont();
- auto padding = kHighContrastTextBorderWidth + 0.1f * font.getSize();
-
- // Draw the background only behind each glyph's bounds. We do this instead of using
- // the bounds of the entire layout, because the layout includes alignment whitespace
- // etc which can obscure other text from separate passes (e.g. emojis).
- // Merge all the glyph bounds into one rect for this line, since drawing a rect for
- // each glyph is expensive.
- SkRect glyphBounds;
- SkRect bgBounds;
- for (size_t i = start; i < end; i++) {
- auto glyph = layout.getGlyphId(i);
-
- font.getBounds(reinterpret_cast<const SkGlyphID*>(&glyph), 1, &glyphBounds,
- &paint);
- glyphBounds.offset(layout.getX(i), layout.getY(i));
-
- bgBounds.join(glyphBounds);
- }
-
- if (!bgBounds.isEmpty()) {
- bgBounds.offset(x, y);
- bgBounds.outset(padding, padding);
- canvas->drawRect(bgBounds.fLeft, bgBounds.fTop, bgBounds.fRight,
- bgBounds.fBottom, outlinePaint);
- }
- } else {
- canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
- }
+ canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
// inner
gDrawTextBlobMode = DrawTextBlobMode::HctInner;