Compress color using hex instead of rgb in SkSVGDevice.

By using hex color instead of RGB in SkSVGDevice, SVG byte size can be
reduced. For example, Quora landing page SVG size: 1588515 bytes (RGB)
vs. 1587899 bytes (Hex). Size reduces by 616 bytes.

Bug: NONE
Test: dm -v --match SVGDevice
Change-Id: I1d557c9d8a925c2dde96899e23833dfb89264903
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/269260
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp
index 3f21c04..413c474 100644
--- a/src/svg/SkSVGDevice.cpp
+++ b/src/svg/SkSVGDevice.cpp
@@ -66,10 +66,23 @@
         return SkString(nc);
     }
 
-    return SkStringPrintf("rgb(%u,%u,%u)",
-                          SkColorGetR(color),
-                          SkColorGetG(color),
-                          SkColorGetB(color));
+    uint8_t r = SkColorGetR(color);
+    uint8_t g = SkColorGetG(color);
+    uint8_t b = SkColorGetB(color);
+
+    // Some users care about every byte here, so we'll use hex colors with single-digit channels
+    // when possible.
+    uint8_t rh = r >> 4;
+    uint8_t rl = r & 0xf;
+    uint8_t gh = g >> 4;
+    uint8_t gl = g & 0xf;
+    uint8_t bh = b >> 4;
+    uint8_t bl = b & 0xf;
+    if ((rh == rl) && (gh == gl) && (bh == bl)) {
+        return SkStringPrintf("#%1X%1X%1X", rh, gh, bh);
+    }
+
+    return SkStringPrintf("#%02X%02X%02X", r, g, b);
 }
 
 static SkScalar svg_opacity(SkColor color) {
diff --git a/tests/SVGDeviceTest.cpp b/tests/SVGDeviceTest.cpp
index 5e61bbd..f9daca5 100644
--- a/tests/SVGDeviceTest.cpp
+++ b/tests/SVGDeviceTest.cpp
@@ -451,4 +451,79 @@
     }
 }
 
+DEF_TEST(SVGDevice_fill_rect_hex, reporter) {
+    SkDOM dom;
+    SkPaint paint;
+    paint.setColor(SK_ColorBLUE);
+    {
+        auto svgCanvas = MakeDOMCanvas(&dom);
+        SkRect bounds{0, 0, SkIntToScalar(100), SkIntToScalar(100)};
+        svgCanvas->drawRect(bounds, paint);
+    }
+    const SkDOM::Node* rootElement = dom.finishParsing();
+    ABORT_TEST(reporter, !rootElement, "root element not found");
+
+    const SkDOM::Node* rectElement = dom.getFirstChild(rootElement, "rect");
+    ABORT_TEST(reporter, !rectElement, "rect element not found");
+    REPORTER_ASSERT(reporter, strcmp(dom.findAttr(rectElement, "fill"), "blue") == 0);
+}
+
+DEF_TEST(SVGDevice_fill_rect_custom_hex, reporter) {
+    SkDOM dom;
+    {
+        SkPaint paint;
+        paint.setColor(0xFFAABCDE);
+        auto svgCanvas = MakeDOMCanvas(&dom);
+        SkRect bounds{0, 0, SkIntToScalar(100), SkIntToScalar(100)};
+        svgCanvas->drawRect(bounds, paint);
+        paint.setColor(0xFFAABBCC);
+        svgCanvas->drawRect(bounds, paint);
+        paint.setColor(0xFFAA1123);
+        svgCanvas->drawRect(bounds, paint);
+    }
+    const SkDOM::Node* rootElement = dom.finishParsing();
+    ABORT_TEST(reporter, !rootElement, "root element not found");
+
+    // Test 0xAABCDE filled rect.
+    const SkDOM::Node* rectElement = dom.getFirstChild(rootElement, "rect");
+    ABORT_TEST(reporter, !rectElement, "rect element not found");
+    REPORTER_ASSERT(reporter, strcmp(dom.findAttr(rectElement, "fill"), "#AABCDE") == 0);
+
+    // Test 0xAABBCC filled rect.
+    rectElement = dom.getNextSibling(rectElement, "rect");
+    ABORT_TEST(reporter, !rectElement, "rect element not found");
+    REPORTER_ASSERT(reporter, strcmp(dom.findAttr(rectElement, "fill"), "#ABC") == 0);
+
+    // Test 0xFFAA1123 filled rect. Make sure it does not turn into #A123.
+    rectElement = dom.getNextSibling(rectElement, "rect");
+    ABORT_TEST(reporter, !rectElement, "rect element not found");
+    REPORTER_ASSERT(reporter, strcmp(dom.findAttr(rectElement, "fill"), "#AA1123") == 0);
+}
+
+DEF_TEST(SVGDevice_fill_stroke_rect_hex, reporter) {
+    SkDOM dom;
+    {
+        auto svgCanvas = MakeDOMCanvas(&dom);
+        SkRect bounds{0, 0, SkIntToScalar(100), SkIntToScalar(100)};
+
+        SkPaint paint;
+        paint.setColor(0xFF00BBAC);
+        svgCanvas->drawRect(bounds, paint);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(0xFF123456);
+        paint.setStrokeWidth(1);
+        svgCanvas->drawRect(bounds, paint);
+    }
+    const SkDOM::Node* rootElement = dom.finishParsing();
+    ABORT_TEST(reporter, !rootElement, "root element not found");
+
+    const SkDOM::Node* rectNode = dom.getFirstChild(rootElement, "rect");
+    ABORT_TEST(reporter, !rectNode, "rect element not found");
+    REPORTER_ASSERT(reporter, strcmp(dom.findAttr(rectNode, "fill"), "#00BBAC") == 0);
+
+    rectNode = dom.getNextSibling(rectNode, "rect");
+    ABORT_TEST(reporter, !rectNode, "rect element not found");
+    REPORTER_ASSERT(reporter, strcmp(dom.findAttr(rectNode, "stroke"), "#123456") == 0);
+    REPORTER_ASSERT(reporter, strcmp(dom.findAttr(rectNode, "stroke-width"), "1") == 0);
+}
 #endif