Reland "SkParagraph"

This is a reland of 10ad0b9b01e4b8a4721ae2ec1adee9ca7d0fe534

Original change's description:
> SkParagraph
>
> Change-Id: I0a4be75fd0c18021c201bcc1edfdfad8556edeff
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/192100
> Reviewed-by: Ben Wagner <bungeman@google.com>
> Reviewed-by: Mike Reed <reed@google.com>
> Commit-Queue: Julia Lavrova <jlavrova@google.com>

Change-Id: I46cf43eae693edf68e45345acd0eb39e04e02bfc
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/219863
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
diff --git a/modules/skparagraph/src/TextShadow.cpp b/modules/skparagraph/src/TextShadow.cpp
new file mode 100644
index 0000000..00ef8f2
--- /dev/null
+++ b/modules/skparagraph/src/TextShadow.cpp
@@ -0,0 +1,30 @@
+// Copyright 2019 Google LLC.
+#include "modules/skparagraph/include/TextShadow.h"
+#include "include/core/SkColor.h"
+
+namespace skia {
+namespace textlayout {
+
+TextShadow::TextShadow() = default;
+TextShadow::TextShadow(SkColor color, SkPoint offset, double blurRadius)
+        : fColor(color), fOffset(offset), fBlurRadius(blurRadius) {}
+
+bool TextShadow::operator==(const TextShadow& other) const {
+    if (fColor != other.fColor) return false;
+    if (fOffset != other.fOffset) return false;
+    if (fBlurRadius != other.fBlurRadius) return false;
+
+    return true;
+}
+
+bool TextShadow::operator!=(const TextShadow& other) const { return !(*this == other); }
+
+bool TextShadow::hasShadow() const {
+    if (!fOffset.isZero()) return true;
+    if (fBlurRadius != 0.0) return true;
+
+    return false;
+}
+
+}  // namespace textlayout
+}  // namespace skia