[canvaskit] expose ComputeTonalColors

Change-Id: Ia4f0eec8e162ad273e856c04ca98c7e2cb48935e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/240693
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/modules/canvaskit/CHANGELOG.md b/modules/canvaskit/CHANGELOG.md
index 31b3eef..203448f 100644
--- a/modules/canvaskit/CHANGELOG.md
+++ b/modules/canvaskit/CHANGELOG.md
@@ -11,6 +11,7 @@
  - `SkPath.offset()`, `SkPath.drawOval`
  - `SkRRect` support (`SkCanvas.drawRRect`, `SkCanvas.drawDRRect`, `CanvasKit.RRectXY`).
    Advanced users can specify the 8 individual radii, if needed.
+ - `CanvasKit.computeTonalColors()`, which returns an SkColor.
 
 ### Changed
  - `MakeSkVertices` uses a builder to save a copy.
diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp
index 7e7d881..37f1059 100644
--- a/modules/canvaskit/canvaskit_bindings.cpp
+++ b/modules/canvaskit/canvaskit_bindings.cpp
@@ -577,6 +577,18 @@
     return rr;
 }
 
+struct TonalColors {
+    SkColor ambientColor;
+    SkColor spotColor;
+};
+
+TonalColors computeTonalColors(const TonalColors& in) {
+    TonalColors out;
+    SkShadowUtils::ComputeTonalColors(in.ambientColor, in.spotColor,
+        &out.ambientColor, &out.spotColor);
+    return out;
+}
+
 // These objects have private destructors / delete methods - I don't think
 // we need to do anything other than tell emscripten to do nothing.
 namespace emscripten {
@@ -624,6 +636,7 @@
 
     constant("gpu", true);
 #endif
+    function("computeTonalColors", &computeTonalColors);
     function("_decodeImage", optional_override([](uintptr_t /* uint8_t*  */ iptr,
                                                   size_t length)->sk_sp<SkImage> {
         uint8_t* imgData = reinterpret_cast<uint8_t*>(iptr);
@@ -1336,6 +1349,10 @@
         .field("fRight",  &SkIRect::fRight)
         .field("fBottom", &SkIRect::fBottom);
 
+    value_object<TonalColors>("TonalColors")
+        .field("ambient", &TonalColors::ambientColor)
+        .field("spot",    &TonalColors::spotColor);
+
     value_object<SimpleImageInfo>("SkImageInfo")
         .field("width",     &SimpleImageInfo::width)
         .field("height",    &SimpleImageInfo::height)
diff --git a/modules/canvaskit/externs.js b/modules/canvaskit/externs.js
index 0ef36e7..07bfe1d 100644
--- a/modules/canvaskit/externs.js
+++ b/modules/canvaskit/externs.js
@@ -57,6 +57,8 @@
 	/** @return {RadialCanvasGradient} */
 	MakeTwoPointConicalGradientShader: function() {},
 	MakeWebGLCanvasSurface: function() {},
+	/** @return {TonalColors} */
+	computeTonalColors: function() {},
 	currentContext: function() {},
 	getColorComponents: function() {},
 	getSkDataBytes: function() {},
diff --git a/modules/canvaskit/tests/core.spec.js b/modules/canvaskit/tests/core.spec.js
index 7656d31..1c7043d 100644
--- a/modules/canvaskit/tests/core.spec.js
+++ b/modules/canvaskit/tests/core.spec.js
@@ -47,4 +47,23 @@
         }));
     });
 
+    it('can compute tonal colors', function(done) {
+        LoadCanvasKit.then(catchException(done, () => {
+            const input = {
+                ambient: CanvasKit.BLUE,
+                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);
+            done();
+        }));
+    })
+
 });