fix(high contrast text): draw a solid rectangle background behind text instead of a stroke border

Bug: 186567103
Flag: com.android.graphics.hwui.flags.high_contrast_text_small_text_rect
Test: manual
1. adb shell setenforce 0 && adb shell setprop persist.device_config.aconfig_flags.accessibility.com.android.graphics.hwui.flags.high_contrast_text_small_text_rect true && adb shell stop && adb shell start
2. Settings -> Accessibility -> Display Size and Text
3. Turn on High Contrast Text

Change-Id: Ib2ef3925acc89b01c71addb63194d2a403c99cb6
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index c156c46..72ddecc 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -22,6 +22,13 @@
 }
 
 flag {
+  name: "high_contrast_text_small_text_rect"
+  namespace: "accessibility"
+  description: "Draw a solid rectangle background behind text instead of a stroke outline"
+  bug: "186567103"
+}
+
+flag {
   name: "hdr_10bit_plus"
   namespace: "core_graphics"
   description: "Use 10101010 and FP16 formats for HDR-UI when available"
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 80b6c03..e9f4b81c 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -18,6 +18,7 @@
 
 #include <SkFontMetrics.h>
 #include <SkRRect.h>
+#include <minikin/MinikinRect.h>
 
 #include "FeatureFlags.h"
 #include "MinikinUtils.h"
@@ -107,7 +108,13 @@
     // care of all alignment.
     paint.setTextAlign(Paint::kLeft_Align);
 
-    DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance());
+    minikin::MinikinRect bounds;
+    // We only need the bounds to draw a rectangular background in high contrast mode. Let's save
+    // the cycles otherwise.
+    if (flags::high_contrast_text_small_text_rect() && isHighContrastText()) {
+        MinikinUtils::getBounds(&paint, bidiFlags, typeface, text, textSize, &bounds);
+    }
+    DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance(), bounds);
     MinikinUtils::forFontRun(layout, &paint, f);
 
     if (text_feature::fix_double_underline()) {
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index 8f99990..ba65439 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -33,6 +33,8 @@
 
 namespace android {
 
+inline constexpr int kHighContrastTextBorderWidth = 4;
+
 static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
                               const Paint& paint, Canvas* canvas) {
     const SkScalar strokeWidth = fmax(thickness, 1.0f);
@@ -45,15 +47,26 @@
     paint->setShader(nullptr);
     paint->setColorFilter(nullptr);
     paint->setLooper(nullptr);
-    paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize());
+    paint->setStrokeWidth(kHighContrastTextBorderWidth + 0.04 * paint->getSkFont().getSize());
     paint->setStrokeJoin(SkPaint::kRound_Join);
     paint->setLooper(nullptr);
 }
 
 class DrawTextFunctor {
 public:
+    /**
+     * Creates a Functor to draw the given text layout.
+     *
+     * @param layout
+     * @param canvas
+     * @param paint
+     * @param x
+     * @param y
+     * @param totalAdvance
+     * @param bounds bounds of the text. Only required if high contrast text mode is enabled.
+     */
     DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x,
-                    float y, float totalAdvance)
+                    float y, float totalAdvance, const minikin::MinikinRect& bounds)
             : layout(layout)
             , canvas(canvas)
             , paint(paint)
@@ -61,7 +74,8 @@
             , y(y)
             , totalAdvance(totalAdvance)
             , underlinePosition(0)
-            , underlineThickness(0) {}
+            , underlineThickness(0)
+            , bounds(bounds) {}
 
     void operator()(size_t start, size_t end) {
         auto glyphFunc = [&](uint16_t* text, float* positions) {
@@ -91,7 +105,16 @@
             Paint outlinePaint(paint);
             simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
             outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
-            canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
+            if (flags::high_contrast_text_small_text_rect()) {
+                auto bgBounds(bounds);
+                auto padding = kHighContrastTextBorderWidth + 0.1f * paint.getSkFont().getSize();
+                bgBounds.offset(x, y);
+                canvas->drawRect(bgBounds.mLeft - padding, bgBounds.mTop - padding,
+                                 bgBounds.mRight + padding, bgBounds.mBottom + padding,
+                                 outlinePaint);
+            } else {
+                canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
+            }
 
             // inner
             gDrawTextBlobMode = DrawTextBlobMode::HctInner;
@@ -146,6 +169,7 @@
     float totalAdvance;
     float underlinePosition;
     float underlineThickness;
+    const minikin::MinikinRect& bounds;
 };
 
 }  // namespace android
diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp
index c70a304..9911bfa 100644
--- a/libs/hwui/tests/unit/UnderlineTest.cpp
+++ b/libs/hwui/tests/unit/UnderlineTest.cpp
@@ -103,8 +103,9 @@
     // Create minikin::Layout
     std::unique_ptr<Typeface> typeface(makeTypeface());
     minikin::Layout layout = doLayout(text, *paint, typeface.get());
+    minikin::MinikinRect bounds;
 
-    DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance());
+    DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance(), bounds);
     MinikinUtils::forFontRun(layout, paint, f);
     return f;
 }