Reland "Switch to using a Float32Array (bound as value array) for color."

This is a reland of 4e79b6730dfce84f2d115ad3babf7b848d44a2b5

Original change's description:
> Switch to using a Float32Array (bound as value array) for color.
> 
> Change-Id: I1bcca931954b1399c79f4074a3d57a68847ac785
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/276757
> Commit-Queue: Nathaniel Nifong <nifong@google.com>
> Reviewed-by: Kevin Lubick <kjlubick@google.com>

Change-Id: If6b9097b2fcd6b9dbf75c6dd22138e0b2531e70d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/278780
Commit-Queue: Nathaniel Nifong <nifong@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
diff --git a/modules/canvaskit/CHANGELOG.md b/modules/canvaskit/CHANGELOG.md
index bfce278..584c72c 100644
--- a/modules/canvaskit/CHANGELOG.md
+++ b/modules/canvaskit/CHANGELOG.md
@@ -6,9 +6,15 @@
 
 ## [Unreleased]
 
+### Changed
+ - CanvasKit colors are now represented with a TypedArray of four floats.
+
 ### Removed
+ - SkPaint.setColorf is obsolete and removed. setColor accepts a CanvasKit color which is
+   always composed of floats. 
  - localmatrix option for `SkShader.Lerp` and `SkShader.Blend`.
 
+
 ## [0.14.0] - 2020-03-18
 
 ### Added
diff --git a/modules/canvaskit/WasmAliases.h b/modules/canvaskit/WasmAliases.h
deleted file mode 100644
index ca017e1..0000000
--- a/modules/canvaskit/WasmAliases.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2019 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef WasmAliases_DEFINED
-#define WasmAliases_DEFINED
-
-#include <emscripten.h>
-#include <emscripten/bind.h>
-
-using namespace emscripten;
-
-// Self-documenting types
-using JSArray = emscripten::val;
-using JSObject = emscripten::val;
-using JSString = emscripten::val;
-using SkPathOrNull = emscripten::val;
-using Uint8Array = emscripten::val;
-using Float32Array = emscripten::val;
-
-#endif
diff --git a/modules/canvaskit/WasmCommon.h b/modules/canvaskit/WasmCommon.h
new file mode 100644
index 0000000..892ecc0
--- /dev/null
+++ b/modules/canvaskit/WasmCommon.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef WasmCommon_DEFINED
+#define WasmCommon_DEFINED
+
+#include <emscripten.h>
+#include <emscripten/bind.h>
+#include "include/core/SkColor.h"
+
+using namespace emscripten;
+
+// Self-documenting types
+using JSArray = emscripten::val;
+using JSObject = emscripten::val;
+using JSString = emscripten::val;
+using SkPathOrNull = emscripten::val;
+using Uint8Array = emscripten::val;
+using Float32Array = emscripten::val;
+
+// A struct used for binding the TypedArray  colors passed to to canvaskit functions.
+// Canvaskit functions returning colors return a Float32Array, which looks the same
+// on the javascript side.
+struct SimpleColor4f {
+	// A sensible but noticeable default value to let you know you've called the
+	// default constructor.
+    float r = 1.0;
+    float g = 0.0;
+    float b = 1.0;
+    float a = 1.0;
+
+    SkColor4f toSkColor4f() const {
+      return SkColor4f({r, g, b, a});
+    };
+    SkColor toSkColor() const {
+      return toSkColor4f().toSkColor();
+    };
+};
+
+#endif
diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp
index 1831f7d..b0f24e0 100644
--- a/modules/canvaskit/canvaskit_bindings.cpp
+++ b/modules/canvaskit/canvaskit_bindings.cpp
@@ -55,7 +55,7 @@
 #include <iostream>
 #include <string>
 
-#include "modules/canvaskit/WasmAliases.h"
+#include "modules/canvaskit/WasmCommon.h"
 #include <emscripten.h>
 #include <emscripten/bind.h>
 
@@ -134,6 +134,13 @@
     return m;
 }
 
+SimpleColor4f toSimpleColor4f(const SkColor4f c) {
+    SimpleColor4f color {
+        c.fR, c.fG, c.fB, c.fA,
+    };
+    return color;
+}
+
 // Surface creation structs and helpers
 struct SimpleImageInfo {
     int width;
@@ -668,14 +675,17 @@
 }
 
 struct TonalColors {
-    SkColor ambientColor;
-    SkColor spotColor;
+    SimpleColor4f ambientColor;
+    SimpleColor4f spotColor;
 };
 
-TonalColors computeTonalColors(const TonalColors& in) {
+TonalColors computeTonalColors(const TonalColors& in) {SkColor resultAmbient, resultSpot;
+    SkShadowUtils::ComputeTonalColors(
+        in.ambientColor.toSkColor(), in.spotColor.toSkColor(),
+        &resultAmbient, &resultSpot);
     TonalColors out;
-    SkShadowUtils::ComputeTonalColors(in.ambientColor, in.spotColor,
-        &out.ambientColor, &out.spotColor);
+    out.ambientColor = toSimpleColor4f(SkColor4f::FromColor(resultAmbient));
+    out.spotColor = toSimpleColor4f(SkColor4f::FromColor(resultSpot));
     return out;
 }
 
@@ -792,28 +802,29 @@
         return SkImage::MakeRasterData(info, pixelData, rowBytes);
     }), allow_raw_pointers());
     function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
-                                uintptr_t /* SkColor*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
                                 int count, SkTileMode mode, uint32_t flags)->sk_sp<SkShader> {
         SkPoint points[] = { start, end };
         // See comment above for uintptr_t explanation
-        const SkColor*  colors    = reinterpret_cast<const SkColor*> (cPtr);
-        const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
+        const SkColor4f* colors    = reinterpret_cast<const SkColor4f*>(cPtr);
+        const SkScalar*  positions = reinterpret_cast<const SkScalar*>(pPtr);
 
-        return SkGradientShader::MakeLinear(points, colors, positions, count,
+        // TODO(nifong): do not assume color space. Support and test wide gamut color gradients
+        return SkGradientShader::MakeLinear(points, colors, SkColorSpace::MakeSRGB(), positions, count,
                                             mode, flags, nullptr);
     }), allow_raw_pointers());
     function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
-                                uintptr_t /* SkColor*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
                                 int count, SkTileMode mode, uint32_t flags,
                                 const SimpleMatrix& lm)->sk_sp<SkShader> {
         SkPoint points[] = { start, end };
         // See comment above for uintptr_t explanation
-        const SkColor*  colors    = reinterpret_cast<const SkColor*> (cPtr);
+        const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*> (cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
 
         SkMatrix localMatrix = toSkMatrix(lm);
 
-        return SkGradientShader::MakeLinear(points, colors, positions, count,
+        return SkGradientShader::MakeLinear(points, colors, SkColorSpace::MakeSRGB(), positions, count,
                                             mode, flags, &localMatrix);
     }), allow_raw_pointers());
 #ifdef SK_SERIALIZE_SKP
@@ -827,89 +838,89 @@
     }), allow_raw_pointers());
 #endif
     function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
-                                uintptr_t /* SkColor*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
                                 int count, SkTileMode mode, uint32_t flags)->sk_sp<SkShader> {
         // See comment above for uintptr_t explanation
-        const SkColor*  colors    = reinterpret_cast<const SkColor*> (cPtr);
+        const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*> (cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
 
-        return SkGradientShader::MakeRadial(center, radius, colors, positions, count,
+        return SkGradientShader::MakeRadial(center, radius, colors, SkColorSpace::MakeSRGB(), positions, count,
                                             mode, flags, nullptr);
     }), allow_raw_pointers());
     function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
-                                uintptr_t /* SkColor*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
                                 int count, SkTileMode mode, uint32_t flags,
                                 const SimpleMatrix& lm)->sk_sp<SkShader> {
         // See comment above for uintptr_t explanation
-        const SkColor*  colors    = reinterpret_cast<const SkColor*> (cPtr);
+        const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*> (cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
 
         SkMatrix localMatrix = toSkMatrix(lm);
-        return SkGradientShader::MakeRadial(center, radius, colors, positions, count,
+        return SkGradientShader::MakeRadial(center, radius, colors, SkColorSpace::MakeSRGB(), positions, count,
                                             mode, flags, &localMatrix);
     }), allow_raw_pointers());
     function("_MakeSweepGradientShader", optional_override([](SkScalar cx, SkScalar cy,
-                                uintptr_t /* SkColor*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
                                 int count, SkTileMode mode,
                                 SkScalar startAngle, SkScalar endAngle,
                                 uint32_t flags,
                                 const SimpleMatrix& lm)->sk_sp<SkShader> {
         // See comment above for uintptr_t explanation
-        const SkColor*  colors    = reinterpret_cast<const SkColor*> (cPtr);
+        const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*> (cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
 
         SkMatrix localMatrix = toSkMatrix(lm);
-        return SkGradientShader::MakeSweep(cx, cy, colors, positions, count,
+        return SkGradientShader::MakeSweep(cx, cy, colors, SkColorSpace::MakeSRGB(), positions, count,
                                            mode, startAngle, endAngle, flags,
                                            &localMatrix);
     }), allow_raw_pointers());
     function("_MakeSweepGradientShader", optional_override([](SkScalar cx, SkScalar cy,
-                                uintptr_t /* SkColor*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
                                 int count, uint32_t flags,
                                 const SimpleMatrix& lm)->sk_sp<SkShader> {
         // See comment above for uintptr_t explanation
-        const SkColor*  colors    = reinterpret_cast<const SkColor*> (cPtr);
+        const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*> (cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
 
         SkMatrix localMatrix = toSkMatrix(lm);
-        return SkGradientShader::MakeSweep(cx, cy, colors, positions, count,
+        return SkGradientShader::MakeSweep(cx, cy, colors, SkColorSpace::MakeSRGB(), positions, count,
                                            flags, &localMatrix);
     }), allow_raw_pointers());
     function("_MakeSweepGradientShader", optional_override([](SkScalar cx, SkScalar cy,
-                                uintptr_t /* SkColor*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
                                 int count)->sk_sp<SkShader> {
         // See comment above for uintptr_t explanation
-        const SkColor*  colors    = reinterpret_cast<const SkColor*> (cPtr);
+        const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*> (cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
 
-        return SkGradientShader::MakeSweep(cx, cy, colors, positions, count);
+        return SkGradientShader::MakeSweep(cx, cy, colors, SkColorSpace::MakeSRGB(), positions, count);
     }), allow_raw_pointers());
     function("_MakeTwoPointConicalGradientShader", optional_override([](
                 SkPoint start, SkScalar startRadius,
                 SkPoint end, SkScalar endRadius,
-                uintptr_t /* SkColor*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
                 int count, SkTileMode mode, uint32_t flags)->sk_sp<SkShader> {
         // See comment above for uintptr_t explanation
-        const SkColor*  colors    = reinterpret_cast<const SkColor*> (cPtr);
+        const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*> (cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
 
         return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
-                                                     colors, positions, count, mode,
+                                                     colors, SkColorSpace::MakeSRGB(), positions, count, mode,
                                                      flags, nullptr);
     }), allow_raw_pointers());
     function("_MakeTwoPointConicalGradientShader", optional_override([](
                 SkPoint start, SkScalar startRadius,
                 SkPoint end, SkScalar endRadius,
-                uintptr_t /* SkColor*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
                 int count, SkTileMode mode, uint32_t flags,
                 const SimpleMatrix& lm)->sk_sp<SkShader> {
         // See comment above for uintptr_t explanation
-        const SkColor*  colors    = reinterpret_cast<const SkColor*> (cPtr);
+        const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*> (cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
 
         SkMatrix localMatrix = toSkMatrix(lm);
         return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
-                                                     colors, positions, count, mode,
+                                                     colors, SkColorSpace::MakeSRGB(), positions, count, mode,
                                                      flags, &localMatrix);
     }), allow_raw_pointers());
 
@@ -953,7 +964,9 @@
 
     class_<SkCanvas>("SkCanvas")
         .constructor<>()
-        .function("clear", &SkCanvas::clear)
+        .function("clear", optional_override([](SkCanvas& self, SimpleColor4f c) {
+            self.clear(c.toSkColor());
+        }))
         .function("clipPath", select_overload<void (const SkPath&, SkClipOp, bool)>(&SkCanvas::clipPath))
         .function("clipRRect", optional_override([](SkCanvas& self, const SimpleRRect& r, SkClipOp op, bool doAntiAlias) {
             self.clipRRect(toRRect(r), op, doAntiAlias);
@@ -963,6 +976,9 @@
             self.concat(toSkMatrix(m));
         }))
         .function("drawArc", &SkCanvas::drawArc)
+        // _drawAtlas takes an SkColor, unlike most private functions handling color.
+        // This is because it takes an array of colors. Converting it on the Javascript side allows
+        // an allocation to be avoided here.
         .function("_drawAtlas", optional_override([](SkCanvas& self,
                 const sk_sp<SkImage>& atlas, uintptr_t /* SkRSXform* */ xptr,
                 uintptr_t /* SkRect* */ rptr, uintptr_t /* SkColor* */ cptr, int count,
@@ -977,7 +993,12 @@
             self.drawAtlas(atlas, dstXforms, srcRects, colors, count, mode, nullptr, paint);
         }), allow_raw_pointers())
         .function("drawCircle", select_overload<void (SkScalar, SkScalar, SkScalar, const SkPaint& paint)>(&SkCanvas::drawCircle))
-        .function("drawColor", &SkCanvas::drawColor)
+        .function("drawColor", optional_override([](SkCanvas& self, SimpleColor4f c) {
+            self.drawColor(c.toSkColor());
+        }))
+        .function("drawColor", optional_override([](SkCanvas& self, SimpleColor4f c, SkBlendMode mode) {
+            self.drawColor(c.toSkColor(), mode);
+        }))
         .function("drawDRRect",optional_override([](SkCanvas& self, const SimpleRRect& o, const SimpleRRect& i, const SkPaint& paint) {
             self.drawDRRect(toRRect(o), toRRect(i), paint);
         }))
@@ -1026,10 +1047,10 @@
         .function("drawShadow", optional_override([](SkCanvas& self, const SkPath& path,
                                                      const SkPoint3& zPlaneParams,
                                                      const SkPoint3& lightPos, SkScalar lightRadius,
-                                                     SkColor ambientColor, SkColor spotColor,
+                                                     SimpleColor4f ambientColor, SimpleColor4f spotColor,
                                                      uint32_t flags) {
             SkShadowUtils::DrawShadow(&self, path, zPlaneParams, lightPos, lightRadius,
-                                      ambientColor, spotColor, flags);
+                                      ambientColor.toSkColor(), spotColor.toSkColor(), flags);
         }))
 #ifndef SK_NO_FONTS
         .function("_drawShapedText", &drawShapedText)
@@ -1111,7 +1132,9 @@
 
     class_<SkColorFilter>("SkColorFilter")
         .smart_ptr<sk_sp<SkColorFilter>>("sk_sp<SkColorFilter>>")
-        .class_function("MakeBlend", &SkColorFilters::Blend)
+        .class_function("MakeBlend", optional_override([](SimpleColor4f c, SkBlendMode mode)->sk_sp<SkColorFilter> {
+            return SkColorFilters::Blend(c.toSkColor(), mode);
+        }))
         .class_function("MakeCompose", &SkColorFilters::Compose)
         .class_function("MakeLerp", &SkColorFilters::Lerp)
         .class_function("MakeLinearToSRGBGamma", &SkColorFilters::LinearToSRGBGamma)
@@ -1305,7 +1328,11 @@
             return p;
         }))
         .function("getBlendMode", &SkPaint::getBlendMode)
-        .function("getColor", &SkPaint::getColor)
+        .function("getColor", optional_override([](SkPaint& self)->Float32Array {
+            const SimpleColor4f& c = toSimpleColor4f(self.getColor4f());
+            const float array[4] = {c.r, c.g, c.b, c.a};
+            return Float32Array(typed_memory_view(4, array));
+        }))
         .function("getFilterQuality", &SkPaint::getFilterQuality)
         .function("getStrokeCap", &SkPaint::getStrokeCap)
         .function("getStrokeJoin", &SkPaint::getStrokeJoin)
@@ -1314,12 +1341,8 @@
         .function("setAntiAlias", &SkPaint::setAntiAlias)
         .function("setAlphaf", &SkPaint::setAlphaf)
         .function("setBlendMode", &SkPaint::setBlendMode)
-        .function("setColor", optional_override([](SkPaint& self, SkColor c) {
-            self.setColor(c);
-        }))
-        .function("setColorf", optional_override([](SkPaint& self,
-                                                    float r, float g, float b, float a) {
-            self.setColor({r, g, b, a});
+        .function("setColor", optional_override([](SkPaint& self, SimpleColor4f c) {
+            self.setColor({c.r, c.g, c.b, c.a});
         }))
         .function("setColorFilter", &SkPaint::setColorFilter)
         .function("setFilterQuality", &SkPaint::setFilterQuality)
@@ -1465,7 +1488,11 @@
     class_<SkShader>("SkShader")
         .smart_ptr<sk_sp<SkShader>>("sk_sp<SkShader>")
         .class_function("Blend", select_overload<sk_sp<SkShader>(SkBlendMode, sk_sp<SkShader>, sk_sp<SkShader>)>(&SkShaders::Blend))
-        .class_function("Color", select_overload<sk_sp<SkShader>(SkColor)>(&SkShaders::Color))
+        .class_function("Color",
+            optional_override([](SimpleColor4f c)->sk_sp<SkShader> {
+                return SkShaders::Color(c.toSkColor4f(), SkColorSpace::MakeSRGB());
+            })
+        )
         .class_function("Lerp", select_overload<sk_sp<SkShader>(float, sk_sp<SkShader>, sk_sp<SkShader>)>(&SkShaders::Lerp));
 
 #ifdef SK_INCLUDE_RUNTIME_EFFECT
@@ -1824,16 +1851,12 @@
         .element(&SimpleM44::m8).element(&SimpleM44::m9).element(&SimpleM44::m10).element(&SimpleM44::m11)
         .element(&SimpleM44::m12).element(&SimpleM44::m13).element(&SimpleM44::m14).element(&SimpleM44::m15);
 
-    constant("TRANSPARENT", SK_ColorTRANSPARENT);
-    constant("RED",         SK_ColorRED);
-    constant("GREEN",       SK_ColorGREEN);
-    constant("BLUE",        SK_ColorBLUE);
-    constant("MAGENTA",     SK_ColorMAGENTA);
-    constant("YELLOW",      SK_ColorYELLOW);
-    constant("CYAN",        SK_ColorCYAN);
-    constant("BLACK",       SK_ColorBLACK);
-    constant("WHITE",       SK_ColorWHITE);
-    // TODO(?)
+    value_array<SimpleColor4f>("SkColor4f")
+        .element(&SimpleColor4f::r)
+        .element(&SimpleColor4f::g)
+        .element(&SimpleColor4f::b)
+        .element(&SimpleColor4f::a);
+
 
     constant("MOVE_VERB",  MOVE);
     constant("LINE_VERB",  LINE);
diff --git a/modules/canvaskit/externs.js b/modules/canvaskit/externs.js
index ddc2b3c..480fab3 100644
--- a/modules/canvaskit/externs.js
+++ b/modules/canvaskit/externs.js
@@ -25,6 +25,7 @@
 var CanvasKit = {
   // public API (i.e. things we declare in the pre-js file or in the cpp bindings)
   Color: function() {},
+  Color4f: function() {},
   /** @return {CanvasKit.SkRect} */
   LTRBRect: function() {},
   /** @return {CanvasKit.SkRect} */
@@ -534,12 +535,14 @@
   skottie: {},
 
   TRANSPARENT: {},
+  BLACK: {},
+  WHITE: {},
   RED: {},
+  GREEN: {},
   BLUE: {},
   YELLOW: {},
   CYAN: {},
-  BLACK: {},
-  WHITE: {},
+  MAGENTA: {},
 
   MOVE_VERB: {},
   LINE_VERB: {},
diff --git a/modules/canvaskit/helper.js b/modules/canvaskit/helper.js
index 2875261..485448c 100644
--- a/modules/canvaskit/helper.js
+++ b/modules/canvaskit/helper.js
@@ -4,34 +4,73 @@
   return Math.round(Math.max(0, Math.min(c || 0, 255)));
 }
 
-// Colors are just a 32 bit number with 8 bits each of a, r, g, b
-// The API is the same as CSS's representation of color rgba(), that is
+// Constructs a Color with the same API as CSS's rgba(), that is
 // r,g,b are 0-255, and a is 0.0 to 1.0.
 // if a is omitted, it will be assumed to be 1.0
+// Internally, Colors are a TypedArray of four unpremultiplied 32-bit floats: a, r, g, b
+// In order to construct one with more precision or in a wider gamut, use
+// CanvasKit.Color4f
 CanvasKit.Color = function(r, g, b, a) {
   if (a === undefined) {
       a = 1;
   }
-  // The >>> 0 converts the signed int to an unsigned int. Skia's
-  // SkColor object is an unsigned int.
-  // https://stackoverflow.com/a/14891172
-  return ((clamp(a*255) << 24) | (clamp(r) << 16) | (clamp(g) << 8) | (clamp(b) << 0)) >>> 0;
+  return CanvasKit.Color4f(clamp(r)/255, clamp(g)/255, clamp(b)/255, a);
 }
 
-// returns [r, g, b, a] from a color
+// Construct a 4-float color.
+// Opaque if opacity is omitted.
+CanvasKit.Color4f = function(r, g, b, a) {
+  if (a === undefined) {
+    a = 1;
+  }
+  return Float32Array.of(r, g, b, a);
+}
+
+// Color constants use property getters to prevent other code from accidentally
+// changing them.
+Object.defineProperty(CanvasKit, "TRANSPARENT", {
+    get: function() { return CanvasKit.Color4f(0, 0, 0, 0); }
+});
+Object.defineProperty(CanvasKit, "BLACK", {
+    get: function() { return CanvasKit.Color4f(0, 0, 0, 1); }
+});
+Object.defineProperty(CanvasKit, "WHITE", {
+    get: function() { return CanvasKit.Color4f(1, 1, 1, 1); }
+});
+Object.defineProperty(CanvasKit, "RED", {
+    get: function() { return CanvasKit.Color4f(1, 0, 0, 1); }
+});
+Object.defineProperty(CanvasKit, "GREEN", {
+    get: function() { return CanvasKit.Color4f(0, 1, 0, 1); }
+});
+Object.defineProperty(CanvasKit, "BLUE", {
+    get: function() { return CanvasKit.Color4f(0, 0, 1, 1); }
+});
+Object.defineProperty(CanvasKit, "YELLOW", {
+    get: function() { return CanvasKit.Color4f(1, 1, 0, 1); }
+});
+Object.defineProperty(CanvasKit, "CYAN", {
+    get: function() { return CanvasKit.Color4f(0, 1, 1, 1); }
+});
+Object.defineProperty(CanvasKit, "MAGENTA", {
+    get: function() { return CanvasKit.Color4f(1, 0, 1, 1); }
+});
+
+// returns a css style [r, g, b, a] from a CanvasKit.Color
+// where r, g, b are returned as ints in the range [0, 255]
 // where a is scaled between 0 and 1.0
 CanvasKit.getColorComponents = function(color) {
   return [
-     (color >> 16) & 0xFF,
-     (color >>  8) & 0xFF,
-     (color >>  0) & 0xFF,
-    ((color >> 24) & 0xFF) / 255,
-  ]
+    Math.floor(color[0]*255),
+    Math.floor(color[1]*255),
+    Math.floor(color[2]*255),
+    color[3]
+  ];
 }
 
 // parseColorString takes in a CSS color value and returns a CanvasKit.Color
-// (which is just a 32 bit integer, 8 bits per channel). An optional colorMap
-// may be provided which maps custom strings to values (e.g. {'springgreen':4278255487}).
+// (which is an array of 4 floats in RGBA order). An optional colorMap
+// may be provided which maps custom strings to values.
 // In the CanvasKit canvas2d shim layer, we provide this map for processing
 // canvas2d calls, but not here for code size reasons.
 CanvasKit.parseColorString = function(colorStr, colorMap) {
@@ -88,6 +127,26 @@
   return CanvasKit.BLACK;
 }
 
+function isCanvasKitColor(ob) {
+  if (!ob) {
+    return false;
+  }
+  return (ob.constructor === Float32Array && ob.length === 4);
+}
+
+// Warning information is lost by this conversion
+function toUint32Color(c) {
+  return ((clamp(c[3]*255) << 24) | (clamp(c[0]*255) << 16) | (clamp(c[1]*255) << 8) | (clamp(c[2]*255) << 0)) >>> 0;
+}
+function uIntColorToCanvasKitColor(c) {
+    return CanvasKit.Color(
+     (c >> 16) & 0xFF,
+     (c >>  8) & 0xFF,
+     (c >>  0) & 0xFF,
+    ((c >> 24) & 0xFF) / 255
+  );
+}
+
 function valueOrPercent(aStr) {
   if (aStr === undefined) {
     return 1; // default to opaque.
@@ -100,16 +159,10 @@
 }
 
 CanvasKit.multiplyByAlpha = function(color, alpha) {
-  if (alpha === 1) {
-    return color;
-  }
-  // extract as int from 0 to 255
-  var a = (color >> 24) & 0xFF;
-  a *= alpha;
-  // mask off the old alpha
-  color &= 0xFFFFFF;
-  // back to unsigned int to match SkColor.
-  return (clamp(a) << 24 | color) >>> 0;
+  // make a copy of the color so the function remains pure.
+  var result = color.slice();
+  result[3] = Math.max(0, Math.min(result[3] * alpha, 1));
+  return result;
 }
 
 function radiansToDegrees(rad) {
diff --git a/modules/canvaskit/htmlcanvas/canvas2dcontext.js b/modules/canvaskit/htmlcanvas/canvas2dcontext.js
index 16f2a8e..f4fc8dd 100644
--- a/modules/canvaskit/htmlcanvas/canvas2dcontext.js
+++ b/modules/canvaskit/htmlcanvas/canvas2dcontext.js
@@ -79,7 +79,7 @@
   Object.defineProperty(this, 'fillStyle', {
     enumerable: true,
     get: function() {
-      if (Number.isInteger(this._fillStyle)) {
+      if (isCanvasKitColor(this._fillStyle)) {
         return colorToString(this._fillStyle);
       }
       return this._fillStyle;
@@ -632,7 +632,7 @@
   this._fillPaint = function() {
     var paint = this._paint.copy();
     paint.setStyle(CanvasKit.PaintStyle.Fill);
-    if (Number.isInteger(this._fillStyle)) {
+    if (isCanvasKitColor(this._fillStyle)) {
       var alphaColor = CanvasKit.multiplyByAlpha(this._fillStyle, this._globalAlpha);
       paint.setColor(alphaColor);
     } else {
@@ -1047,7 +1047,7 @@
   this._strokePaint = function() {
     var paint = this._paint.copy();
     paint.setStyle(CanvasKit.PaintStyle.Stroke);
-    if (Number.isInteger(this._strokeStyle)) {
+    if (isCanvasKitColor(this._strokeStyle)) {
       var alphaColor = CanvasKit.multiplyByAlpha(this._strokeStyle, this._globalAlpha);
       paint.setColor(alphaColor);
     } else {
diff --git a/modules/canvaskit/htmlcanvas/color.js b/modules/canvaskit/htmlcanvas/color.js
index 606d92e..3b21b31 100644
--- a/modules/canvaskit/htmlcanvas/color.js
+++ b/modules/canvaskit/htmlcanvas/color.js
@@ -7,7 +7,158 @@
 // the map, which saves (a tiny amount of) startup time
 // and (a small amount of) code size.
 /* @dict */
-var colorMap = {'aliceblue':4293982463,'antiquewhite':4294634455,'aqua':4278255615,'aquamarine':4286578644,'azure':4293984255,'beige':4294309340,'bisque':4294960324,'black':4278190080,'blanchedalmond':4294962125,'blue':4278190335,'blueviolet':4287245282,'brown':4289014314,'burlywood':4292786311,'cadetblue':4284456608,'chartreuse':4286578432,'chocolate':4291979550,'coral':4294934352,'cornflowerblue':4284782061,'cornsilk':4294965468,'crimson':4292613180,'cyan':4278255615,'darkblue':4278190219,'darkcyan':4278225803,'darkgoldenrod':4290283019,'darkgray':4289309097,'darkgreen':4278215680,'darkgrey':4289309097,'darkkhaki':4290623339,'darkmagenta':4287299723,'darkolivegreen':4283788079,'darkorange':4294937600,'darkorchid':4288230092,'darkred':4287299584,'darksalmon':4293498490,'darkseagreen':4287609999,'darkslateblue':4282924427,'darkslategray':4281290575,'darkslategrey':4281290575,'darkturquoise':4278243025,'darkviolet':4287889619,'deeppink':4294907027,'deepskyblue':4278239231,'dimgray':4285098345,'dimgrey':4285098345,'dodgerblue':4280193279,'firebrick':4289864226,'floralwhite':4294966000,'forestgreen':4280453922,'fuchsia':4294902015,'gainsboro':4292664540,'ghostwhite':4294506751,'gold':4294956800,'goldenrod':4292519200,'gray':4286611584,'green':4278222848,'greenyellow':4289593135,'grey':4286611584,'honeydew':4293984240,'hotpink':4294928820,'indianred':4291648604,'indigo':4283105410,'ivory':4294967280,'khaki':4293977740,'lavender':4293322490,'lavenderblush':4294963445,'lawngreen':4286381056,'lemonchiffon':4294965965,'lightblue':4289583334,'lightcoral':4293951616,'lightcyan':4292935679,'lightgoldenrodyellow':4294638290,'lightgray':4292072403,'lightgreen':4287688336,'lightgrey':4292072403,'lightpink':4294948545,'lightsalmon':4294942842,'lightseagreen':4280332970,'lightskyblue':4287090426,'lightslategray':4286023833,'lightslategrey':4286023833,'lightsteelblue':4289774814,'lightyellow':4294967264,'lime':4278255360,'limegreen':4281519410,'linen':4294635750,'magenta':4294902015,'maroon':4286578688,'mediumaquamarine':4284927402,'mediumblue':4278190285,'mediumorchid':4290401747,'mediumpurple':4287852763,'mediumseagreen':4282168177,'mediumslateblue':4286277870,'mediumspringgreen':4278254234,'mediumturquoise':4282962380,'mediumvioletred':4291237253,'midnightblue':4279834992,'mintcream':4294311930,'mistyrose':4294960353,'moccasin':4294960309,'navajowhite':4294958765,'navy':4278190208,'oldlace':4294833638,'olive':4286611456,'olivedrab':4285238819,'orange':4294944000,'orangered':4294919424,'orchid':4292505814,'palegoldenrod':4293847210,'palegreen':4288215960,'paleturquoise':4289720046,'palevioletred':4292571283,'papayawhip':4294963157,'peachpuff':4294957753,'peru':4291659071,'pink':4294951115,'plum':4292714717,'powderblue':4289781990,'purple':4286578816,'rebeccapurple':4284887961,'red':4294901760,'rosybrown':4290547599,'royalblue':4282477025,'saddlebrown':4287317267,'salmon':4294606962,'sandybrown':4294222944,'seagreen':4281240407,'seashell':4294964718,'sienna':4288696877,'silver':4290822336,'skyblue':4287090411,'slateblue':4285160141,'slategray':4285563024,'slategrey':4285563024,'snow':4294966010,'springgreen':4278255487,'steelblue':4282811060,'tan':4291998860,'teal':4278222976,'thistle':4292394968,'transparent':0,'tomato':4294927175,'turquoise':4282441936,'violet':4293821166,'wheat':4294303411,'white':4294967295,'whitesmoke':4294309365,'yellow':4294967040,'yellowgreen':4288335154};
+var colorMap = {
+  'aliceblue':            Float32Array.of(0.941, 0.973, 1.000, 1.000),
+  'antiquewhite':         Float32Array.of(0.980, 0.922, 0.843, 1.000),
+  'aqua':                 Float32Array.of(0.000, 1.000, 1.000, 1.000),
+  'aquamarine':           Float32Array.of(0.498, 1.000, 0.831, 1.000),
+  'azure':                Float32Array.of(0.941, 1.000, 1.000, 1.000),
+  'beige':                Float32Array.of(0.961, 0.961, 0.863, 1.000),
+  'bisque':               Float32Array.of(1.000, 0.894, 0.769, 1.000),
+  'black':                Float32Array.of(0.000, 0.000, 0.000, 1.000),
+  'blanchedalmond':       Float32Array.of(1.000, 0.922, 0.804, 1.000),
+  'blue':                 Float32Array.of(0.000, 0.000, 1.000, 1.000),
+  'blueviolet':           Float32Array.of(0.541, 0.169, 0.886, 1.000),
+  'brown':                Float32Array.of(0.647, 0.165, 0.165, 1.000),
+  'burlywood':            Float32Array.of(0.871, 0.722, 0.529, 1.000),
+  'cadetblue':            Float32Array.of(0.373, 0.620, 0.627, 1.000),
+  'chartreuse':           Float32Array.of(0.498, 1.000, 0.000, 1.000),
+  'chocolate':            Float32Array.of(0.824, 0.412, 0.118, 1.000),
+  'coral':                Float32Array.of(1.000, 0.498, 0.314, 1.000),
+  'cornflowerblue':       Float32Array.of(0.392, 0.584, 0.929, 1.000),
+  'cornsilk':             Float32Array.of(1.000, 0.973, 0.863, 1.000),
+  'crimson':              Float32Array.of(0.863, 0.078, 0.235, 1.000),
+  'cyan':                 Float32Array.of(0.000, 1.000, 1.000, 1.000),
+  'darkblue':             Float32Array.of(0.000, 0.000, 0.545, 1.000),
+  'darkcyan':             Float32Array.of(0.000, 0.545, 0.545, 1.000),
+  'darkgoldenrod':        Float32Array.of(0.722, 0.525, 0.043, 1.000),
+  'darkgray':             Float32Array.of(0.663, 0.663, 0.663, 1.000),
+  'darkgreen':            Float32Array.of(0.000, 0.392, 0.000, 1.000),
+  'darkgrey':             Float32Array.of(0.663, 0.663, 0.663, 1.000),
+  'darkkhaki':            Float32Array.of(0.741, 0.718, 0.420, 1.000),
+  'darkmagenta':          Float32Array.of(0.545, 0.000, 0.545, 1.000),
+  'darkolivegreen':       Float32Array.of(0.333, 0.420, 0.184, 1.000),
+  'darkorange':           Float32Array.of(1.000, 0.549, 0.000, 1.000),
+  'darkorchid':           Float32Array.of(0.600, 0.196, 0.800, 1.000),
+  'darkred':              Float32Array.of(0.545, 0.000, 0.000, 1.000),
+  'darksalmon':           Float32Array.of(0.914, 0.588, 0.478, 1.000),
+  'darkseagreen':         Float32Array.of(0.561, 0.737, 0.561, 1.000),
+  'darkslateblue':        Float32Array.of(0.282, 0.239, 0.545, 1.000),
+  'darkslategray':        Float32Array.of(0.184, 0.310, 0.310, 1.000),
+  'darkslategrey':        Float32Array.of(0.184, 0.310, 0.310, 1.000),
+  'darkturquoise':        Float32Array.of(0.000, 0.808, 0.820, 1.000),
+  'darkviolet':           Float32Array.of(0.580, 0.000, 0.827, 1.000),
+  'deeppink':             Float32Array.of(1.000, 0.078, 0.576, 1.000),
+  'deepskyblue':          Float32Array.of(0.000, 0.749, 1.000, 1.000),
+  'dimgray':              Float32Array.of(0.412, 0.412, 0.412, 1.000),
+  'dimgrey':              Float32Array.of(0.412, 0.412, 0.412, 1.000),
+  'dodgerblue':           Float32Array.of(0.118, 0.565, 1.000, 1.000),
+  'firebrick':            Float32Array.of(0.698, 0.133, 0.133, 1.000),
+  'floralwhite':          Float32Array.of(1.000, 0.980, 0.941, 1.000),
+  'forestgreen':          Float32Array.of(0.133, 0.545, 0.133, 1.000),
+  'fuchsia':              Float32Array.of(1.000, 0.000, 1.000, 1.000),
+  'gainsboro':            Float32Array.of(0.863, 0.863, 0.863, 1.000),
+  'ghostwhite':           Float32Array.of(0.973, 0.973, 1.000, 1.000),
+  'gold':                 Float32Array.of(1.000, 0.843, 0.000, 1.000),
+  'goldenrod':            Float32Array.of(0.855, 0.647, 0.125, 1.000),
+  'gray':                 Float32Array.of(0.502, 0.502, 0.502, 1.000),
+  'green':                Float32Array.of(0.000, 0.502, 0.000, 1.000),
+  'greenyellow':          Float32Array.of(0.678, 1.000, 0.184, 1.000),
+  'grey':                 Float32Array.of(0.502, 0.502, 0.502, 1.000),
+  'honeydew':             Float32Array.of(0.941, 1.000, 0.941, 1.000),
+  'hotpink':              Float32Array.of(1.000, 0.412, 0.706, 1.000),
+  'indianred':            Float32Array.of(0.804, 0.361, 0.361, 1.000),
+  'indigo':               Float32Array.of(0.294, 0.000, 0.510, 1.000),
+  'ivory':                Float32Array.of(1.000, 1.000, 0.941, 1.000),
+  'khaki':                Float32Array.of(0.941, 0.902, 0.549, 1.000),
+  'lavender':             Float32Array.of(0.902, 0.902, 0.980, 1.000),
+  'lavenderblush':        Float32Array.of(1.000, 0.941, 0.961, 1.000),
+  'lawngreen':            Float32Array.of(0.486, 0.988, 0.000, 1.000),
+  'lemonchiffon':         Float32Array.of(1.000, 0.980, 0.804, 1.000),
+  'lightblue':            Float32Array.of(0.678, 0.847, 0.902, 1.000),
+  'lightcoral':           Float32Array.of(0.941, 0.502, 0.502, 1.000),
+  'lightcyan':            Float32Array.of(0.878, 1.000, 1.000, 1.000),
+  'lightgoldenrodyellow': Float32Array.of(0.980, 0.980, 0.824, 1.000),
+  'lightgray':            Float32Array.of(0.827, 0.827, 0.827, 1.000),
+  'lightgreen':           Float32Array.of(0.565, 0.933, 0.565, 1.000),
+  'lightgrey':            Float32Array.of(0.827, 0.827, 0.827, 1.000),
+  'lightpink':            Float32Array.of(1.000, 0.714, 0.757, 1.000),
+  'lightsalmon':          Float32Array.of(1.000, 0.627, 0.478, 1.000),
+  'lightseagreen':        Float32Array.of(0.125, 0.698, 0.667, 1.000),
+  'lightskyblue':         Float32Array.of(0.529, 0.808, 0.980, 1.000),
+  'lightslategray':       Float32Array.of(0.467, 0.533, 0.600, 1.000),
+  'lightslategrey':       Float32Array.of(0.467, 0.533, 0.600, 1.000),
+  'lightsteelblue':       Float32Array.of(0.690, 0.769, 0.871, 1.000),
+  'lightyellow':          Float32Array.of(1.000, 1.000, 0.878, 1.000),
+  'lime':                 Float32Array.of(0.000, 1.000, 0.000, 1.000),
+  'limegreen':            Float32Array.of(0.196, 0.804, 0.196, 1.000),
+  'linen':                Float32Array.of(0.980, 0.941, 0.902, 1.000),
+  'magenta':              Float32Array.of(1.000, 0.000, 1.000, 1.000),
+  'maroon':               Float32Array.of(0.502, 0.000, 0.000, 1.000),
+  'mediumaquamarine':     Float32Array.of(0.400, 0.804, 0.667, 1.000),
+  'mediumblue':           Float32Array.of(0.000, 0.000, 0.804, 1.000),
+  'mediumorchid':         Float32Array.of(0.729, 0.333, 0.827, 1.000),
+  'mediumpurple':         Float32Array.of(0.576, 0.439, 0.859, 1.000),
+  'mediumseagreen':       Float32Array.of(0.235, 0.702, 0.443, 1.000),
+  'mediumslateblue':      Float32Array.of(0.482, 0.408, 0.933, 1.000),
+  'mediumspringgreen':    Float32Array.of(0.000, 0.980, 0.604, 1.000),
+  'mediumturquoise':      Float32Array.of(0.282, 0.820, 0.800, 1.000),
+  'mediumvioletred':      Float32Array.of(0.780, 0.082, 0.522, 1.000),
+  'midnightblue':         Float32Array.of(0.098, 0.098, 0.439, 1.000),
+  'mintcream':            Float32Array.of(0.961, 1.000, 0.980, 1.000),
+  'mistyrose':            Float32Array.of(1.000, 0.894, 0.882, 1.000),
+  'moccasin':             Float32Array.of(1.000, 0.894, 0.710, 1.000),
+  'navajowhite':          Float32Array.of(1.000, 0.871, 0.678, 1.000),
+  'navy':                 Float32Array.of(0.000, 0.000, 0.502, 1.000),
+  'oldlace':              Float32Array.of(0.992, 0.961, 0.902, 1.000),
+  'olive':                Float32Array.of(0.502, 0.502, 0.000, 1.000),
+  'olivedrab':            Float32Array.of(0.420, 0.557, 0.137, 1.000),
+  'orange':               Float32Array.of(1.000, 0.647, 0.000, 1.000),
+  'orangered':            Float32Array.of(1.000, 0.271, 0.000, 1.000),
+  'orchid':               Float32Array.of(0.855, 0.439, 0.839, 1.000),
+  'palegoldenrod':        Float32Array.of(0.933, 0.910, 0.667, 1.000),
+  'palegreen':            Float32Array.of(0.596, 0.984, 0.596, 1.000),
+  'paleturquoise':        Float32Array.of(0.686, 0.933, 0.933, 1.000),
+  'palevioletred':        Float32Array.of(0.859, 0.439, 0.576, 1.000),
+  'papayawhip':           Float32Array.of(1.000, 0.937, 0.835, 1.000),
+  'peachpuff':            Float32Array.of(1.000, 0.855, 0.725, 1.000),
+  'peru':                 Float32Array.of(0.804, 0.522, 0.247, 1.000),
+  'pink':                 Float32Array.of(1.000, 0.753, 0.796, 1.000),
+  'plum':                 Float32Array.of(0.867, 0.627, 0.867, 1.000),
+  'powderblue':           Float32Array.of(0.690, 0.878, 0.902, 1.000),
+  'purple':               Float32Array.of(0.502, 0.000, 0.502, 1.000),
+  'rebeccapurple':        Float32Array.of(0.400, 0.200, 0.600, 1.000),
+  'red':                  Float32Array.of(1.000, 0.000, 0.000, 1.000),
+  'rosybrown':            Float32Array.of(0.737, 0.561, 0.561, 1.000),
+  'royalblue':            Float32Array.of(0.255, 0.412, 0.882, 1.000),
+  'saddlebrown':          Float32Array.of(0.545, 0.271, 0.075, 1.000),
+  'salmon':               Float32Array.of(0.980, 0.502, 0.447, 1.000),
+  'sandybrown':           Float32Array.of(0.957, 0.643, 0.376, 1.000),
+  'seagreen':             Float32Array.of(0.180, 0.545, 0.341, 1.000),
+  'seashell':             Float32Array.of(1.000, 0.961, 0.933, 1.000),
+  'sienna':               Float32Array.of(0.627, 0.322, 0.176, 1.000),
+  'silver':               Float32Array.of(0.753, 0.753, 0.753, 1.000),
+  'skyblue':              Float32Array.of(0.529, 0.808, 0.922, 1.000),
+  'slateblue':            Float32Array.of(0.416, 0.353, 0.804, 1.000),
+  'slategray':            Float32Array.of(0.439, 0.502, 0.565, 1.000),
+  'slategrey':            Float32Array.of(0.439, 0.502, 0.565, 1.000),
+  'snow':                 Float32Array.of(1.000, 0.980, 0.980, 1.000),
+  'springgreen':          Float32Array.of(0.000, 1.000, 0.498, 1.000),
+  'steelblue':            Float32Array.of(0.275, 0.510, 0.706, 1.000),
+  'tan':                  Float32Array.of(0.824, 0.706, 0.549, 1.000),
+  'teal':                 Float32Array.of(0.000, 0.502, 0.502, 1.000),
+  'thistle':              Float32Array.of(0.847, 0.749, 0.847, 1.000),
+  'tomato':               Float32Array.of(1.000, 0.388, 0.278, 1.000),
+  'transparent':          Float32Array.of(0.000, 0.000, 0.000, 0.000),
+  'turquoise':            Float32Array.of(0.251, 0.878, 0.816, 1.000),
+  'violet':               Float32Array.of(0.933, 0.510, 0.933, 1.000),
+  'wheat':                Float32Array.of(0.961, 0.871, 0.702, 1.000),
+  'white':                Float32Array.of(1.000, 1.000, 1.000, 1.000),
+  'whitesmoke':           Float32Array.of(0.961, 0.961, 0.961, 1.000),
+  'yellow':               Float32Array.of(1.000, 1.000, 0.000, 1.000),
+  'yellowgreen':          Float32Array.of(0.604, 0.804, 0.196, 1.000),
+}
+
 function colorToString(skcolor) {
   // https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color
   var components = CanvasKit.getColorComponents(skcolor);
diff --git a/modules/canvaskit/interface.js b/modules/canvaskit/interface.js
index 495fb9d..40c5416 100644
--- a/modules/canvaskit/interface.js
+++ b/modules/canvaskit/interface.js
@@ -861,8 +861,7 @@
   // atlas is an SkImage, e.g. from CanvasKit.MakeImageFromEncoded
   // srcRects and dstXforms should be CanvasKit.SkRectBuilder and CanvasKit.RSXFormBuilder
   // or just arrays of floats in groups of 4.
-  // colors, if provided, should be a CanvasKit.SkColorBuilder or array of SkColor
-  // (from CanvasKit.Color)
+  // colors, if provided, should be a CanvasKit.SkColorBuilder or array of Canvaskit.SimpleColor4f
   CanvasKit.SkCanvas.prototype.drawAtlas = function(atlas, srcRects, dstXforms, paint,
                                        /*optional*/ blendMode, colors) {
     if (!atlas || !paint || !srcRects || !dstXforms) {
@@ -871,6 +870,7 @@
     }
     if (srcRects.length !== dstXforms.length || (colors && colors.length !== dstXforms.length)) {
       SkDebug('Doing nothing since input arrays length mismatches');
+      return;
     }
     if (!blendMode) {
       blendMode = CanvasKit.BlendMode.SrcOver;
@@ -895,6 +895,13 @@
       if (colors.build) {
         colorPtr = colors.build();
       } else {
+        if (!isCanvasKitColor(colors[0])) {
+          SkDebug('DrawAtlas color argument expected to be CanvasKit.SkRectBuilder or array of ' +
+            'Canvaskit.SimpleColor4f, but got '+colors);
+          return;
+        }
+        // convert here
+        colors = colors.map(toUint32Color);
         colorPtr = copy1dArray(colors, CanvasKit.HEAPU32);
       }
     }
@@ -1063,7 +1070,7 @@
   }
 
   CanvasKit.SkShader.MakeLinearGradient = function(start, end, colors, pos, mode, localMatrix, flags) {
-    var colorPtr = copy1dArray(colors, CanvasKit.HEAPU32);
+    var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
     var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
     flags = flags || 0;
 
@@ -1085,7 +1092,7 @@
   }
 
   CanvasKit.SkShader.MakeRadialGradient = function(center, radius, colors, pos, mode, localMatrix, flags) {
-    var colorPtr = copy1dArray(colors, CanvasKit.HEAPU32);
+    var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
     var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
     flags = flags || 0;
 
@@ -1107,7 +1114,7 @@
   }
 
   CanvasKit.SkShader.MakeSweepGradient = function(cx, cy, colors, pos, mode, localMatrix, flags, startAngle, endAngle) {
-    var colorPtr = copy1dArray(colors, CanvasKit.HEAPU32);
+    var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
     var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
     flags = flags || 0;
     startAngle = startAngle || 0;
@@ -1135,7 +1142,7 @@
 
   CanvasKit.SkShader.MakeTwoPointConicalGradient = function(start, startRadius, end, endRadius,
                                                          colors, pos, mode, localMatrix, flags) {
-    var colorPtr = copy1dArray(colors, CanvasKit.HEAPU32);
+    var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
     var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
     flags = flags || 0;
 
@@ -1257,6 +1264,7 @@
   return CanvasKit._MakeImage(info, pptr, pixels.length, width * bytesPerPixel);
 }
 
+// colors is an array of SimpleColor4f
 CanvasKit.MakeSkVertices = function(mode, positions, textureCoordinates, colors,
                                     indices, isVolatile) {
   // Default isVolitile to true if not set
@@ -1282,7 +1290,8 @@
     copy2dArray(textureCoordinates, CanvasKit.HEAPF32, builder.texCoords());
   }
   if (builder.colors()) {
-    copy1dArray(colors,             CanvasKit.HEAPU32, builder.colors());
+    // Convert from canvaskit 4f colors to 32 bit uint colors which builder supports.
+    copy1dArray(colors.map(toUint32Color), CanvasKit.HEAPU32, builder.colors());
   }
   if (builder.indices()) {
     copy1dArray(indices,            CanvasKit.HEAPU16, builder.indices());
diff --git a/modules/canvaskit/paragraph.js b/modules/canvaskit/paragraph.js
index 84d808e..d50edfd 100644
--- a/modules/canvaskit/paragraph.js
+++ b/modules/canvaskit/paragraph.js
@@ -65,11 +65,11 @@
 
     CanvasKit.TextStyle = function(s) {
        // Use [''] to tell closure not to minify the names
-      s['backgroundColor'] = s['backgroundColor'] || 0;
-      // Can't check for falsey as 0 is "white".
-      if (s['color'] === undefined) {
+      if (!isCanvasKitColor(s['color'])) {
         s['color'] = CanvasKit.BLACK;
       }
+      s['foregroundColor'] = s['foregroundColor'] || CanvasKit.TRANSPARENT;
+      s['backgroundColor'] = s['backgroundColor'] || CanvasKit.TRANSPARENT;
       s['decoration'] = s['decoration'] || 0;
       s['decorationThickness'] = s['decorationThickness'] || 0;
       s['fontSize'] = s['fontSize'] || 0;
@@ -83,7 +83,6 @@
         SkDebug("no font families provided, text may draw wrong or not at all")
       }
       s['fontStyle'] = fontStyle(s['fontStyle']);
-      s['foregroundColor'] = s['foregroundColor'] || 0;
       return s;
     }
 
diff --git a/modules/canvaskit/paragraph_bindings.cpp b/modules/canvaskit/paragraph_bindings.cpp
index 13f8c8e..7dc057a 100644
--- a/modules/canvaskit/paragraph_bindings.cpp
+++ b/modules/canvaskit/paragraph_bindings.cpp
@@ -21,7 +21,7 @@
 
 #include <emscripten.h>
 #include <emscripten/bind.h>
-#include "modules/canvaskit/WasmAliases.h"
+#include "modules/canvaskit/WasmCommon.h"
 
 using namespace emscripten;
 
@@ -34,13 +34,13 @@
 };
 
 struct SimpleTextStyle {
-    SkColor backgroundColor;
-    SkColor color;
+    SimpleColor4f color;
+    SimpleColor4f foregroundColor;
+    SimpleColor4f backgroundColor;
     uint8_t decoration;
     SkScalar decorationThickness;
     SkScalar fontSize;
     SimpleFontStyle fontStyle;
-    SkColor foregroundColor;
 
     uintptr_t /* const char** */ fontFamilies;
     int numFontFamilies;
@@ -48,20 +48,23 @@
 
 para::TextStyle toTextStyle(const SimpleTextStyle& s) {
     para::TextStyle ts;
-    if (s.color != 0) {
-        ts.setColor(s.color);
+
+    // textstyle.color doesn't support a 4f color, however the foreground and background fields below do.
+    ts.setColor(s.color.toSkColor());
+
+    // Emscripten will not allow a value_object to have an unset field, however
+    // It is functionally important that these paints be unset when no value was provided.
+    // paragraph.js defaults these colors to transparent in that case and we use that signal here.
+    if (s.foregroundColor.a > 0) {
+        SkPaint p1;
+        p1.setColor4f(s.foregroundColor.toSkColor4f());
+        ts.setForegroundColor(p1);
     }
 
-    if (s.foregroundColor != 0) {
-        SkPaint p;
-        p.setColor(s.foregroundColor);
-        ts.setForegroundColor(p);
-    }
-
-    if (s.backgroundColor != 0) {
-        SkPaint p;
-        p.setColor(s.backgroundColor);
-        ts.setBackgroundColor(p);
+    if (s.backgroundColor.a > 0) {
+        SkPaint p2;
+        p2.setColor4f(s.backgroundColor.toSkColor4f());
+        ts.setBackgroundColor(p2);
     }
 
     if (s.fontSize != 0) {
@@ -272,14 +275,14 @@
         .field("textStyle",         &SimpleParagraphStyle::textStyle);
 
     value_object<SimpleTextStyle>("TextStyle")
-        .field("backgroundColor",     &SimpleTextStyle::backgroundColor)
         .field("color",               &SimpleTextStyle::color)
+        .field("foregroundColor",     &SimpleTextStyle::foregroundColor)
+        .field("backgroundColor",     &SimpleTextStyle::backgroundColor)
         .field("decoration",          &SimpleTextStyle::decoration)
         .field("decorationThickness", &SimpleTextStyle::decorationThickness)
         .field("_fontFamilies",       &SimpleTextStyle::fontFamilies)
         .field("fontSize",            &SimpleTextStyle::fontSize)
         .field("fontStyle",           &SimpleTextStyle::fontStyle)
-        .field("foregroundColor",     &SimpleTextStyle::foregroundColor)
         .field("_numFontFamilies",    &SimpleTextStyle::numFontFamilies);
 
     // The U stands for unsigned - we can't bind a generic/template object, so we have to specify it
diff --git a/modules/canvaskit/particles_bindings.cpp b/modules/canvaskit/particles_bindings.cpp
index aed39b4..be18932 100644
--- a/modules/canvaskit/particles_bindings.cpp
+++ b/modules/canvaskit/particles_bindings.cpp
@@ -15,7 +15,7 @@
 
 #include <string>
 
-#include "modules/canvaskit/WasmAliases.h"
+#include "modules/canvaskit/WasmCommon.h"
 
 #include <emscripten.h>
 #include <emscripten/bind.h>
diff --git a/modules/canvaskit/skottie_bindings.cpp b/modules/canvaskit/skottie_bindings.cpp
index a7e44ae..a22a8b0 100644
--- a/modules/canvaskit/skottie_bindings.cpp
+++ b/modules/canvaskit/skottie_bindings.cpp
@@ -17,7 +17,7 @@
 
 #include <emscripten.h>
 #include <emscripten/bind.h>
-#include "modules/canvaskit/WasmAliases.h"
+#include "modules/canvaskit/WasmCommon.h"
 
 #if SK_INCLUDE_MANAGED_SKOTTIE
 #include "modules/skottie/include/SkottieProperty.h"
@@ -220,7 +220,9 @@
         .function("render"    , select_overload<void(SkCanvas*) const>(&ManagedAnimation::render), allow_raw_pointers())
         .function("render"    , select_overload<void(SkCanvas*, const SkRect&) const>
                                     (&ManagedAnimation::render), allow_raw_pointers())
-        .function("setColor"  , &ManagedAnimation::setColor)
+        .function("setColor"  , optional_override([](ManagedAnimation& self, const std::string& key, SimpleColor4f c) {
+            self.setColor(key, c.toSkColor());
+        }))
         .function("setOpacity", &ManagedAnimation::setOpacity)
         .function("getMarkers", &ManagedAnimation::getMarkers)
         .function("getColorProps"  , &ManagedAnimation::getColorProps)
diff --git a/modules/canvaskit/tests/canvas2d.spec.js b/modules/canvaskit/tests/canvas2d.spec.js
index db1ab23..6a6bbf3 100644
--- a/modules/canvaskit/tests/canvas2d.spec.js
+++ b/modules/canvaskit/tests/canvas2d.spec.js
@@ -14,6 +14,14 @@
         container.innerHTML = '';
     });
 
+    function expectColorCloseTo(a, b) {
+        expect(a.length).toEqual(4);
+        expect(b.length).toEqual(4);
+        for (let i=0; i<4; i++){
+            expect(a[i]).toBeCloseTo(b[i], 3);
+        }
+    }
+
     describe('color strings', function() {
         function hex(s) {
             return parseInt(s, 16);
@@ -22,13 +30,13 @@
         it('parses hex color strings', function(done) {
             LoadCanvasKit.then(catchException(done, () => {
                 const parseColor = CanvasKit.parseColorString;
-                expect(parseColor('#FED')).toEqual(
+                expectColorCloseTo(parseColor('#FED'), 
                     CanvasKit.Color(hex('FF'), hex('EE'), hex('DD'), 1));
-                expect(parseColor('#FEDC')).toEqual(
+                expectColorCloseTo(parseColor('#FEDC'),
                     CanvasKit.Color(hex('FF'), hex('EE'), hex('DD'), hex('CC')/255));
-                expect(parseColor('#fed')).toEqual(
+                expectColorCloseTo(parseColor('#fed'),
                     CanvasKit.Color(hex('FF'), hex('EE'), hex('DD'), 1));
-                expect(parseColor('#fedc')).toEqual(
+                expectColorCloseTo(parseColor('#fedc'),
                     CanvasKit.Color(hex('FF'), hex('EE'), hex('DD'), hex('CC')/255));
                 done();
             }));
@@ -36,19 +44,19 @@
         it('parses rgba color strings', function(done) {
             LoadCanvasKit.then(catchException(done, () => {
                 const parseColor = CanvasKit.parseColorString;
-                expect(parseColor('rgba(117, 33, 64, 0.75)')).toEqual(
+                expectColorCloseTo(parseColor('rgba(117, 33, 64, 0.75)'),
                     CanvasKit.Color(117, 33, 64, 0.75));
-                expect(parseColor('rgb(117, 33, 64, 0.75)')).toEqual(
+                expectColorCloseTo(parseColor('rgb(117, 33, 64, 0.75)'),
                     CanvasKit.Color(117, 33, 64, 0.75));
-                expect(parseColor('rgba(117,33,64)')).toEqual(
+                expectColorCloseTo(parseColor('rgba(117,33,64)'),
                     CanvasKit.Color(117, 33, 64, 1.0));
-                expect(parseColor('rgb(117,33, 64)')).toEqual(
+                expectColorCloseTo(parseColor('rgb(117,33, 64)'),
                     CanvasKit.Color(117, 33, 64, 1.0));
-                expect(parseColor('rgb(117,33, 64, 32%)')).toEqual(
+                expectColorCloseTo(parseColor('rgb(117,33, 64, 32%)'),
                     CanvasKit.Color(117, 33, 64, 0.32));
-                expect(parseColor('rgb(117,33, 64, 0.001)')).toEqual(
+                expectColorCloseTo(parseColor('rgb(117,33, 64, 0.001)'),
                     CanvasKit.Color(117, 33, 64, 0.001));
-                expect(parseColor('rgb(117,33,64,0)')).toEqual(
+                expectColorCloseTo(parseColor('rgb(117,33,64,0)'),
                     CanvasKit.Color(117, 33, 64, 0.0));
                 done();
             }));
@@ -58,11 +66,11 @@
                 // Keep this one as the _testing version, because we don't include the large
                 // color map by default.
                 const parseColor = CanvasKit._testing.parseColor;
-                expect(parseColor('grey')).toEqual(
+                expectColorCloseTo(parseColor('grey'),
                     CanvasKit.Color(128, 128, 128, 1.0));
-                expect(parseColor('blanchedalmond')).toEqual(
+                expectColorCloseTo(parseColor('blanchedalmond'),
                     CanvasKit.Color(255, 235, 205, 1.0));
-                expect(parseColor('transparent')).toEqual(
+                expectColorCloseTo(parseColor('transparent'),
                     CanvasKit.Color(0, 0, 0, 0));
                 done();
             }));
@@ -75,7 +83,7 @@
                 expect(colorToString(CanvasKit.Color(102, 51, 153, 1.0))).toEqual('#663399');
 
                 expect(colorToString(CanvasKit.Color(255, 235, 205, 0.5))).toEqual(
-                                               'rgba(255, 235, 205, 0.50196078)');
+                                               'rgba(255, 235, 205, 0.50000000)');
 
                 done();
             }));
@@ -111,7 +119,7 @@
                 for (let tc of testCases) {
                     // Print out the test case if the two don't match.
                     expect(multiplyByAlpha(tc.inColor, tc.inAlpha))
-                          .toBe(tc.outColor, JSON.stringify(tc));
+                          .toEqual(tc.outColor, JSON.stringify(tc));
                 }
 
                 done();
diff --git a/modules/canvaskit/tests/core.spec.js b/modules/canvaskit/tests/core.spec.js
index cc3c36d..f48684c 100644
--- a/modules/canvaskit/tests/core.spec.js
+++ b/modules/canvaskit/tests/core.spec.js
@@ -47,6 +47,15 @@
         }));
     });
 
+    function uIntColorToCanvasKitColor(c) {
+        return CanvasKit.Color(
+         (c >> 16) & 0xFF,
+         (c >>  8) & 0xFF,
+         (c >>  0) & 0xFF,
+        ((c >> 24) & 0xFF) / 255
+      );
+    }
+
     it('can compute tonal colors', function(done) {
         LoadCanvasKit.then(catchException(done, () => {
             const input = {
@@ -54,14 +63,12 @@
                 spot: CanvasKit.RED,
             };
             const out = CanvasKit.computeTonalColors(input);
-
-            expect(out.ambient).toEqual(CanvasKit.Color(0,0,0,1));
-
-            const [r,g,b,a] = CanvasKit.getColorComponents(out.spot);
-            expect(r).toEqual(44);
-            expect(g).toEqual(0);
-            expect(b).toEqual(0);
-            expect(a).toBeCloseTo(0.969, 2);
+            expect(new Float32Array(out.ambient)).toEqual(CanvasKit.BLACK);
+            const expectedSpot = [0.173, 0, 0, 0.969];
+            expect(out.spot[0]).toBeCloseTo(expectedSpot[0], 3);
+            expect(out.spot[1]).toBeCloseTo(expectedSpot[1], 3);
+            expect(out.spot[2]).toBeCloseTo(expectedSpot[2], 3);
+            expect(out.spot[3]).toBeCloseTo(expectedSpot[3], 3);
             done();
         }));
     });
@@ -221,7 +228,7 @@
             const shader = CanvasKit.SkShader.MakeSweepGradient(
                 100,
                 100,
-                [0xFF00FF00, 0xFF0000FF],
+                [CanvasKit.GREEN, CanvasKit.BLUE],
                 [0.0, 1.0],
                 CanvasKit.TileMode.Clamp,
             );
@@ -731,8 +738,8 @@
 
     it('exports consts correctly', function(done) {
         LoadCanvasKit.then(catchException(done, () => {
-            expect(CanvasKit.TRANSPARENT).toEqual(0);
-            expect(CanvasKit.RED).toEqual(4294901760);
+            expect(CanvasKit.TRANSPARENT).toEqual(Float32Array.of(0, 0, 0, 0));
+            expect(CanvasKit.RED).toEqual(Float32Array.of(1, 0, 0, 1));
 
             expect(CanvasKit.QUAD_VERB).toEqual(2);
             expect(CanvasKit.CONIC_VERB).toEqual(3);
@@ -744,4 +751,15 @@
         }));
     });
 
+    it('can set and get a 4f color on a paint', function(done) {
+        LoadCanvasKit.then(catchException(done, () => {
+
+            const paint = new CanvasKit.SkPaint();
+            paint.setColor(CanvasKit.Color4f(3.3, 2.2, 1.1, 0.5));
+            expect(paint.getColor()).toEqual(Float32Array.of(3.3, 2.2, 1.1, 0.5));
+
+            done();
+        }));
+    });
+
 });