Wide color gamut support and working example.

Color space arguments accepted at surface creation, paint, gradient, and other call sites.
Works correctly only when chrome happens to be rendering itself in the same color space
the canvaskit user has chosen, there's not yet end to end color management of
canvases supported in browsers.

readPixels not yet working due to possible chrome bug.

Change-Id: I3dea5b16c60a3871cd2a54f86716f4a438a90135
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/289733
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 3ef0180..7766718 100644
--- a/modules/canvaskit/CHANGELOG.md
+++ b/modules/canvaskit/CHANGELOG.md
@@ -6,12 +6,31 @@
 
 ## [Unreleased]
 
+### Added
+ - Support for wide-gamut color spaces DisplayP3 and AdobeRGB. However, correct representation on a
+   WCG monitor requires that the browser is rendering everything to the DisplayP3 or AdobeRGB
+   profile, since there is not yet any way to indicate to the browser that a canvas element has a
+   non-sRGB color space. See color support example in extra.html. Only supported for WebGL2 backed
+   surfaces.
+ - Added `SkSurface.reportBackendType` which returns either 'CPU' or 'GPU'.
+ - Added `SkSurface.imageInfo` which returns an ImageInfo object describing the size and color
+   properties of the surface. colorSpace is added to ImageInfo everywhere it is used.
+
 ### Changed
-  - We now compile/ship with Emscripten v1.39.16.
+ - We now compile/ship with Emscripten v1.39.16.
+ - `CanvasKit.MakeCanvasSurface` accepts a new enum specifying one of the three color space and
+   pixel format combinations supported by CanvasKit. 
+ - all `_Make*Shader` functions now accept a color space argument at the end. leaving it off or
+   passing null makes it behave as it did before, defaulting to sRGB
+ - `SkPaint.setColor` accepts a new color space argument, defaulting to sRGB.
 
 ### Breaking
  - `CanvasKitInit(...)` now directly returns a Promise. As such, `CanvasKitInit(...).ready()`
    has been removed.
+ - `CanvasKit.MakeCanvasSurface` no longer accepts width/height arguments to override those on
+   the canvas element. Use the canvas element's width/height attributes to dictate the size of
+   the drawing area, and use CSS width/height to set the size it will appear on the page
+   (it is rescaled after drawing when css sizing applies).
 
 ## [0.15.0] - 2020-05-14
 
diff --git a/modules/canvaskit/canvaskit/example.html b/modules/canvaskit/canvaskit/example.html
index c10b5d0..7aa1dcb 100644
--- a/modules/canvaskit/canvaskit/example.html
+++ b/modules/canvaskit/canvaskit/example.html
@@ -1125,6 +1125,7 @@
       height: 50,
       alphaType: CanvasKit.AlphaType.Premul,
       colorType: CanvasKit.ColorType.RGBA_8888,
+      colorSpace: CanvasKit.SkColorSpace.SRGB,
     });
 
     if (!subSurface) {
diff --git a/modules/canvaskit/canvaskit/extra.html b/modules/canvaskit/canvaskit/extra.html
index 1d1882d..f932231 100644
--- a/modules/canvaskit/canvaskit/extra.html
+++ b/modules/canvaskit/canvaskit/extra.html
@@ -35,8 +35,9 @@
 <h2> 3D perspective transformations </h2>
 <canvas id=camera3d width=500 height=500></canvas>
 
-<h2> Use of offscreen surfaces </h2>
-<canvas id=surfaces width=500 height=500></canvas>
+<h2> Support for extended color spaces </h2>
+<a href="chrome://flags/#force-color-profile">Force P3 profile</a>
+<canvas id=colorsupport width=300 height=300></canvas>
 
 <script type="text/javascript" src="/node_modules/canvaskit/bin/canvaskit.js"></script>
 
@@ -79,7 +80,7 @@
 
     SkpExample(CanvasKit, skpData);
 
-    SurfaceAPI1(CanvasKit);
+    ColorSupport(CanvasKit);
   });
 
   fetch(cdn + 'lego_loader.json').then((resp) => {
@@ -240,76 +241,6 @@
    "Bindings": []
 };
 
-  function SurfaceAPI1(CanvasKit) {
-    const surface = CanvasKit.MakeCanvasSurface('surfaces');
-    if (!surface) {
-      console.error('Could not make surface');
-      return;
-    }
-    console.log('SurfaceAPI1 top surface type = '+surface.reportBackendType() );
-    const context = CanvasKit.currentContext();
-    const canvas = surface.getCanvas();
-
-    //create a subsurface as a temporary workspace.
-    const subSurface = surface.makeSurface({
-      width: 50,
-      height: 50,
-      alphaType: CanvasKit.AlphaType.Premul,
-      colorType: CanvasKit.ColorType.RGBA_8888,
-    });
-
-    if (!subSurface) {
-      console.error('Could not make subsurface');
-      return;
-    }
-    console.log('SurfaceAPI1 subSurface type = '+subSurface.reportBackendType() );
-
-    // draw a small "scene"
-    const paint = new CanvasKit.SkPaint();
-    paint.setColor(CanvasKit.Color(139, 228, 135, 0.95)); // greenish
-    paint.setStyle(CanvasKit.PaintStyle.Fill);
-    paint.setAntiAlias(true);
-
-    const subCanvas = subSurface.getCanvas();
-    subCanvas.clear(CanvasKit.BLACK);
-    subCanvas.drawRect(CanvasKit.LTRBRect(5, 15, 45, 40), paint);
-
-    paint.setColor(CanvasKit.Color(214, 93, 244)); // purplish
-    for (let i = 0; i < 10; i++) {
-      const x = Math.random() * 50;
-      const y = Math.random() * 50;
-
-      subCanvas.drawOval(CanvasKit.XYWHRect(x, y, 6, 6), paint);
-    }
-
-    // Snap it off as an SkImage - this image will be in the form the
-    // parent surface prefers (e.g. Texture for GPU / Raster for CPU).
-    const img = subSurface.makeImageSnapshot();
-
-    // clean up the temporary surface
-    subSurface.delete();
-    paint.delete();
-
-    // Make it repeat a bunch with a shader
-    const pattern = img.makeShader(CanvasKit.TileMode.Repeat, CanvasKit.TileMode.Mirror);
-    const patternPaint = new CanvasKit.SkPaint();
-    patternPaint.setShader(pattern);
-
-    let i = 0;
-
-    function drawFrame() {
-      i++;
-      CanvasKit.setCurrentContext(context);
-      canvas.clear(CanvasKit.WHITE);
-
-      canvas.drawOval(CanvasKit.LTRBRect(i % 60, i % 60, 300 - (i% 60), 300 - (i % 60)), patternPaint);
-      surface.flush();
-      window.requestAnimationFrame(drawFrame);
-    }
-    window.requestAnimationFrame(drawFrame);
-
-  }
-
   function ParagraphAPI1(CanvasKit, fontData) {
     if (!CanvasKit || !fontData) {
       return;
@@ -890,4 +821,30 @@
 
     surface.requestAnimationFrame(drawFrame);
   }
+
+  function ColorSupport(CanvasKit) {
+    const surface = CanvasKit.MakeCanvasSurface('colorsupport', CanvasKit.SkColorSpace.ADOBE_RGB);
+    if (!surface) {
+      console.error('Could not make surface');
+      return;
+    }
+    const canvas = surface.getCanvas();
+
+    // If the surface is correctly initialized with a higher bit depth color type,
+    // And chrome is compositing it into a buffer with the P3 color space,
+    // then the inner round rect should be distinct and less saturated than the full red background.
+    // Even if the monitor it is viewed on cannot accurately represent that color space.
+
+    let red = CanvasKit.Color4f(1, 0, 0, 1);
+    let paint = new CanvasKit.SkPaint();
+    paint.setColor(red, CanvasKit.SkColorSpace.ADOBE_RGB);
+    canvas.drawPaint(paint);
+    paint.setColor(red, CanvasKit.SkColorSpace.DISPLAY_P3);
+    canvas.drawRoundRect(CanvasKit.LTRBRect(50, 50, 250, 250), 30, 30, paint);
+    paint.setColor(red, CanvasKit.SkColorSpace.SRGB);
+    canvas.drawRoundRect(CanvasKit.LTRBRect(100, 100, 200, 200), 30, 30, paint);
+
+    surface.flush();
+    surface.delete();
+  }
 </script>
diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp
index 5dfcf4d..32f344d 100644
--- a/modules/canvaskit/canvaskit_bindings.cpp
+++ b/modules/canvaskit/canvaskit_bindings.cpp
@@ -12,6 +12,7 @@
 #include "include/core/SkCanvas.h"
 #include "include/core/SkColor.h"
 #include "include/core/SkColorFilter.h"
+#include "include/core/SkColorSpace.h"
 #include "include/core/SkData.h"
 #include "include/core/SkDrawable.h"
 #include "include/core/SkEncodedImageFormat.h"
@@ -109,13 +110,29 @@
     int height;
     SkColorType colorType;
     SkAlphaType alphaType;
-    // TODO color spaces?
+    sk_sp<SkColorSpace> colorSpace;
 };
 
 SkImageInfo toSkImageInfo(const SimpleImageInfo& sii) {
-    return SkImageInfo::Make(sii.width, sii.height, sii.colorType, sii.alphaType);
+    return SkImageInfo::Make(sii.width, sii.height, sii.colorType, sii.alphaType, sii.colorSpace);
 }
 
+// Set the pixel format based on the colortype.
+// These degrees of freedom are removed from canvaskit only to keep the interface simpler.
+struct ColorSettings {
+    ColorSettings(sk_sp<SkColorSpace> colorSpace) {
+        if (colorSpace == nullptr || colorSpace->isSRGB()) {
+            colorType = kRGBA_8888_SkColorType;
+            pixFormat = GL_RGBA8;
+        } else {
+            colorType = kRGBA_F16_SkColorType;
+            pixFormat = GL_RGBA16F;
+        }
+    };
+    SkColorType colorType;
+    GrGLenum pixFormat;
+};
+
 #ifdef SK_GL
 sk_sp<GrContext> MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)
 {
@@ -131,31 +148,27 @@
     return grContext;
 }
 
-sk_sp<SkSurface> MakeOnScreenGLSurface(sk_sp<GrContext> grContext, int width, int height) {
+sk_sp<SkSurface> MakeOnScreenGLSurface(sk_sp<GrContext> grContext, int width, int height,
+    sk_sp<SkColorSpace> colorSpace) {
     glClearColor(0, 0, 0, 0);
     glClearStencil(0);
     glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 
-
     // Wrap the frame buffer object attached to the screen in a Skia render
     // target so Skia can render to it
     GrGLint buffer;
     glGetIntegerv(GL_FRAMEBUFFER_BINDING, &buffer);
     GrGLFramebufferInfo info;
     info.fFBOID = (GrGLuint) buffer;
-    SkColorType colorType;
 
     GrGLint stencil;
     glGetIntegerv(GL_STENCIL_BITS, &stencil);
 
-    info.fFormat = GL_RGBA8;
-    colorType = kRGBA_8888_SkColorType;
-
+    const auto colorSettings = ColorSettings(colorSpace);
+    info.fFormat = colorSettings.pixFormat;
     GrBackendRenderTarget target(width, height, 0, stencil, info);
-
     sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(grContext.get(), target,
-                                                                    kBottomLeft_GrSurfaceOrigin,
-                                                                    colorType, nullptr, nullptr));
+        kBottomLeft_GrSurfaceOrigin, colorSettings.colorType, colorSpace, nullptr));
     return surface;
 }
 
@@ -758,16 +771,17 @@
         return SkImage::MakeRasterData(info, pixelData, rowBytes);
     }), allow_raw_pointers());
     function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
-                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr,
+                                uintptr_t /* SkScalar*  */ pPtr,
                                 int count, SkTileMode mode, uint32_t flags,
-                                uintptr_t /* SkScalar*  */ mPtr)->sk_sp<SkShader> {
+                                uintptr_t /* SkScalar*  */ mPtr,
+                                sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
         SkPoint points[] = { start, end };
         // See comment above for uintptr_t explanation
         const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*>(cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
         OptionalMatrix localMatrix(mPtr);
-        // TODO(nifong): do not assume color space. Support and test wide gamut color gradients
-        return SkGradientShader::MakeLinear(points, colors, SkColorSpace::MakeSRGB(), positions, count,
+        return SkGradientShader::MakeLinear(points, colors, colorSpace, positions, count,
                                                 mode, flags, &localMatrix);
     }), allow_raw_pointers());
 #ifdef SK_SERIALIZE_SKP
@@ -781,42 +795,48 @@
     }), allow_raw_pointers());
 #endif
     function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
-                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr,
+                                uintptr_t /* SkScalar*  */ pPtr,
                                 int count, SkTileMode mode, uint32_t flags,
-                                uintptr_t /* SkScalar*  */ mPtr)->sk_sp<SkShader> {
+                                uintptr_t /* SkScalar*  */ mPtr,
+                                sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
         // See comment above for uintptr_t explanation
         const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*>(cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
         OptionalMatrix localMatrix(mPtr);
-        return SkGradientShader::MakeRadial(center, radius, colors, SkColorSpace::MakeSRGB(), positions, count,
+        return SkGradientShader::MakeRadial(center, radius, colors, colorSpace, positions, count,
                                             mode, flags, &localMatrix);
     }), allow_raw_pointers());
     function("_MakeSweepGradientShader", optional_override([](SkScalar cx, SkScalar cy,
-                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr,
+                                uintptr_t /* SkScalar*  */ pPtr,
                                 int count, SkTileMode mode,
                                 SkScalar startAngle, SkScalar endAngle,
                                 uint32_t flags,
-                                uintptr_t /* SkScalar*  */ mPtr)->sk_sp<SkShader> {
+                                uintptr_t /* SkScalar*  */ mPtr,
+                                sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
         // See comment above for uintptr_t explanation
         const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*>(cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
         OptionalMatrix localMatrix(mPtr);
-        return SkGradientShader::MakeSweep(cx, cy, colors, SkColorSpace::MakeSRGB(), positions, count,
+        return SkGradientShader::MakeSweep(cx, cy, colors, colorSpace, positions, count,
                                            mode, startAngle, endAngle, flags,
                                            &localMatrix);
     }), allow_raw_pointers());
     function("_MakeTwoPointConicalGradientShader", optional_override([](
                                 SkPoint start, SkScalar startRadius,
                                 SkPoint end, SkScalar endRadius,
-                                uintptr_t /* SkColor4f*  */ cPtr, uintptr_t /* SkScalar*  */ pPtr,
+                                uintptr_t /* SkColor4f*  */ cPtr,
+                                uintptr_t /* SkScalar*  */ pPtr,
                                 int count, SkTileMode mode, uint32_t flags,
-                                uintptr_t /* SkScalar*  */ mPtr)->sk_sp<SkShader> {
+                                uintptr_t /* SkScalar*  */ mPtr,
+                                sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
         // See comment above for uintptr_t explanation
         const SkColor4f*  colors  = reinterpret_cast<const SkColor4f*> (cPtr);
         const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
         OptionalMatrix localMatrix(mPtr);
         return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
-                                                     colors, SkColorSpace::MakeSRGB(), positions, count, mode,
+                                                     colors, colorSpace, positions, count, mode,
                                                      flags, &localMatrix);
     }), allow_raw_pointers());
 
@@ -1257,8 +1277,9 @@
         .function("setAntiAlias", &SkPaint::setAntiAlias)
         .function("setAlphaf", &SkPaint::setAlphaf)
         .function("setBlendMode", &SkPaint::setBlendMode)
-        .function("_setColor", optional_override([](SkPaint& self, uintptr_t /* float* */ cPtr) {
-            self.setColor(ptrToSkColor4f(cPtr));
+        .function("_setColor", optional_override([](SkPaint& self, uintptr_t /* float* */ cPtr,
+                sk_sp<SkColorSpace> colorSpace) {
+            self.setColor(ptrToSkColor4f(cPtr), colorSpace.get());
         }))
         .function("setColorFilter", &SkPaint::setColorFilter)
         .function("setFilterQuality", &SkPaint::setFilterQuality)
@@ -1272,6 +1293,21 @@
         .function("setStrokeWidth", &SkPaint::setStrokeWidth)
         .function("setStyle", &SkPaint::setStyle);
 
+    class_<SkColorSpace>("SkColorSpace")
+        .smart_ptr<sk_sp<SkColorSpace>>("sk_sp<SkColorSpace>")
+        .class_function("Equals", optional_override([](sk_sp<SkColorSpace> a, sk_sp<SkColorSpace> b)->bool {
+            return SkColorSpace::Equals(a.get(), b.get());
+        }))
+        // These are private because they are to be called once in interface.js to
+        // avoid clients having to delete the returned objects.
+        .class_function("_MakeSRGB", &SkColorSpace::MakeSRGB)
+        .class_function("_MakeDisplayP3", optional_override([]()->sk_sp<SkColorSpace> {
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
+        }))
+        .class_function("_MakeAdobeRGB", optional_override([]()->sk_sp<SkColorSpace> {
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB);
+        }));
+
     class_<SkPathEffect>("SkPathEffect")
         .smart_ptr<sk_sp<SkPathEffect>>("sk_sp<SkPathEffect>")
         .class_function("MakeCorner", &SkCornerPathEffect::Make)
@@ -1405,8 +1441,8 @@
         .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",
-            optional_override([](uintptr_t /* float* */ cPtr)->sk_sp<SkShader> {
-                return SkShaders::Color(ptrToSkColor4f(cPtr), SkColorSpace::MakeSRGB());
+            optional_override([](uintptr_t /* float* */ cPtr, sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
+                return SkShaders::Color(ptrToSkColor4f(cPtr), colorSpace);
             })
         )
         .class_function("Lerp", select_overload<sk_sp<SkShader>(float, sk_sp<SkShader>, sk_sp<SkShader>)>(&SkShaders::Lerp));
@@ -1457,6 +1493,10 @@
         .smart_ptr<sk_sp<SkSurface>>("sk_sp<SkSurface>")
         .function("_flush", select_overload<void()>(&SkSurface::flushAndSubmit))
         .function("getCanvas", &SkSurface::getCanvas, allow_raw_pointers())
+        .function("imageInfo", optional_override([](SkSurface& self)->SimpleImageInfo {
+            const auto& ii = self.imageInfo();
+            return {ii.width(), ii.height(), ii.colorType(), ii.alphaType(), ii.refColorSpace()};
+        }))
         .function("height", &SkSurface::height)
         .function("makeImageSnapshot", select_overload<sk_sp<SkImage>()>(&SkSurface::makeImageSnapshot))
         .function("makeImageSnapshot", select_overload<sk_sp<SkImage>(const SkIRect& bounds)>(&SkSurface::makeImageSnapshot))
@@ -1654,7 +1694,6 @@
         .value("TrianglesStrip",  SkVertices::VertexMode::kTriangleStrip_VertexMode)
         .value("TriangleFan",     SkVertices::VertexMode::kTriangleFan_VertexMode);
 
-
     // A value object is much simpler than a class - it is returned as a JS
     // object and does not require delete().
     // https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#value-types
@@ -1690,10 +1729,11 @@
         .field("fBottom", &SkIRect::fBottom);
 
     value_object<SimpleImageInfo>("SkImageInfo")
-        .field("width",     &SimpleImageInfo::width)
-        .field("height",    &SimpleImageInfo::height)
-        .field("colorType", &SimpleImageInfo::colorType)
-        .field("alphaType", &SimpleImageInfo::alphaType);
+        .field("width",      &SimpleImageInfo::width)
+        .field("height",     &SimpleImageInfo::height)
+        .field("colorType",  &SimpleImageInfo::colorType)
+        .field("alphaType",  &SimpleImageInfo::alphaType)
+        .field("colorSpace", &SimpleImageInfo::colorSpace);
 
     // SkPoints can be represented by [x, y]
     value_array<SkPoint>("SkPoint")
diff --git a/modules/canvaskit/cpu.js b/modules/canvaskit/cpu.js
index d2e543c..88b57e1 100644
--- a/modules/canvaskit/cpu.js
+++ b/modules/canvaskit/cpu.js
@@ -26,6 +26,10 @@
       CanvasKit.MakeCanvasSurface = CanvasKit.MakeSWCanvasSurface;
     }
 
+    // Note that color spaces are currently not supported in CPU surfaces. due to the limitation
+    // canvas.getContext('2d').putImageData imposes a limitatin of using an RGBA_8888 color type.
+    // TODO(nifong): support WGC color spaces while still using an RGBA_8888 color type when
+    // on a cpu backend.
     CanvasKit.MakeSurface = function(width, height) {
       /* @dict */
       var imageInfo = {
@@ -35,6 +39,7 @@
         // Since we are sending these pixels directly into the HTML canvas,
         // (and those pixels are un-premultiplied, i.e. straight r,g,b,a)
         'alphaType': CanvasKit.AlphaType.Unpremul,
+        'colorSpace': CanvasKit.SkColorSpace.SRGB,
       }
       var pixelLen = width * height * 4; // it's 8888, so 4 bytes per pixel
       // Allocate the buffer of pixels to be drawn into.
diff --git a/modules/canvaskit/externs.js b/modules/canvaskit/externs.js
index ed2a4a6..6add3d4 100644
--- a/modules/canvaskit/externs.js
+++ b/modules/canvaskit/externs.js
@@ -259,6 +259,17 @@
     scaled: function() {},
   },
 
+  SkColorSpace: {
+    Equals: function() {},
+    SRGB: {},
+    DISPLAY_P3: {},
+    ADOBE_RGB: {},
+    // private API (from C++ bindings)
+    _MakeSRGB: function() {},
+    _MakeDisplayP3: function() {},
+    _MakeAdobeRGB: function() {},
+  },
+
   SkContourMeasureIter: {
     next: function() {},
   },
@@ -520,6 +531,7 @@
     // public API (from C++ bindings)
     /** @return {CanvasKit.SkCanvas} */
     getCanvas: function() {},
+    imageInfo: function() {},
     /** @return {CanvasKit.SkImage} */
     makeImageSnapshot: function() {},
     makeSurface: function() {},
diff --git a/modules/canvaskit/gpu.js b/modules/canvaskit/gpu.js
index 10c5038..f4043cc 100644
--- a/modules/canvaskit/gpu.js
+++ b/modules/canvaskit/gpu.js
@@ -41,19 +41,22 @@
         return CanvasKit.currentContext() || 0;
       };
 
-      // arg can be of types:
+      // idOrElement can be of types:
       //  - String - in which case it is interpreted as an id of a
       //          canvas element.
       //  - HTMLCanvasElement - in which the provided canvas element will
       //          be used directly.
-      // Width and height can be provided to override those on the canvas
-      // element, or specify a height for when a context is provided.
-      CanvasKit.MakeWebGLCanvasSurface = function(arg, width, height) {
-        var canvas = arg;
+      // colorSpace - sk_sp<SkColorSpace> - one of the supported color spaces:
+      //          CanvasKit.SkColorSpace.SRGB
+      //          CanvasKit.SkColorSpace.DISPLAY_P3
+      //          CanvasKit.SkColorSpace.ADOBE_RGB
+      CanvasKit.MakeWebGLCanvasSurface = function(idOrElement, colorSpace) {
+        colorSpace = colorSpace || null;
+        var canvas = idOrElement;
         if (canvas.tagName !== 'CANVAS') {
-          canvas = document.getElementById(arg);
+          canvas = document.getElementById(idOrElement);
           if (!canvas) {
-            throw 'Canvas with id ' + arg + ' was not found';
+            throw 'Canvas with id ' + idOrElement + ' was not found';
           }
         }
 
@@ -64,10 +67,6 @@
           throw 'failed to create webgl context: err ' + ctx;
         }
 
-        if (!canvas && (!width || !height)) {
-          throw 'height and width must be provided with context';
-        }
-
         var grcontext = this.MakeGrContext(ctx);
 
         if (grcontext) {
@@ -76,12 +75,10 @@
           grcontext.setResourceCacheLimitBytes(RESOURCE_CACHE_BYTES);
         }
 
-
-        // Maybe better to use clientWidth/height.  See:
-        // https://webglfundamentals.org/webgl/lessons/webgl-anti-patterns.html
-        var surface = this.MakeOnScreenGLSurface(grcontext,
-                                                 width  || canvas.width,
-                                                 height || canvas.height);
+        // Note that canvas.width/height here is used because it gives the size of the buffer we're
+        // rendering into. This may not be the same size the element is displayed on the page, which
+        // constrolled by css, and available in canvas.clientWidth/height.
+        var surface = this.MakeOnScreenGLSurface(grcontext, canvas.width, canvas.height, colorSpace);
         if (!surface) {
           SkDebug('falling back from GPU implementation to a SW based one');
           // we need to throw away the old canvas (which was locked to
diff --git a/modules/canvaskit/htmlcanvas/canvas2dcontext.js b/modules/canvaskit/htmlcanvas/canvas2dcontext.js
index f4fc8dd..8ebecce 100644
--- a/modules/canvaskit/htmlcanvas/canvas2dcontext.js
+++ b/modules/canvaskit/htmlcanvas/canvas2dcontext.js
@@ -848,7 +848,8 @@
     }
     var img = CanvasKit.MakeImage(imageData.data, imageData.width, imageData.height,
                                   CanvasKit.AlphaType.Unpremul,
-                                  CanvasKit.ColorType.RGBA_8888);
+                                  CanvasKit.ColorType.RGBA_8888,
+                                  CanvasKit.SkColorSpace.SRGB);
     var src = CanvasKit.XYWHRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
     var dst = CanvasKit.XYWHRect(x+dirtyX, y+dirtyY, dirtyWidth, dirtyHeight);
     var inverted = CanvasKit.SkMatrix.invert(this._currentTransform);
diff --git a/modules/canvaskit/interface.js b/modules/canvaskit/interface.js
index 9159f90..31fc6b5 100644
--- a/modules/canvaskit/interface.js
+++ b/modules/canvaskit/interface.js
@@ -8,6 +8,12 @@
 CanvasKit.onRuntimeInitialized = function() {
   // All calls to 'this' need to go in externs.js so closure doesn't minify them away.
 
+  // Create single copies of all three supported color spaces
+  // These are sk_sp<SkColorSpace>
+  CanvasKit.SkColorSpace.SRGB = CanvasKit.SkColorSpace._MakeSRGB();
+  CanvasKit.SkColorSpace.DISPLAY_P3 = CanvasKit.SkColorSpace._MakeDisplayP3();
+  CanvasKit.SkColorSpace.ADOBE_RGB = CanvasKit.SkColorSpace._MakeAdobeRGB();
+
   // Add some helpers for matrices. This is ported from SkMatrix.cpp
   // to save complexity and overhead of going back and forth between
   // C++ and JS layers.
@@ -1007,11 +1013,16 @@
 
   // returns Uint8Array
   CanvasKit.SkCanvas.prototype.readPixels = function(x, y, w, h, alphaType,
-                                                     colorType, dstRowBytes) {
+                                                     colorType, colorSpace, dstRowBytes) {
     // supply defaults (which are compatible with HTMLCanvas's getImageData)
     alphaType = alphaType || CanvasKit.AlphaType.Unpremul;
     colorType = colorType || CanvasKit.ColorType.RGBA_8888;
-    dstRowBytes = dstRowBytes || (4 * w);
+    colorSpace = colorSpace || CanvasKit.SkColorSpace.SRGB;
+    var pixBytes = 4;
+    if (colorType === CanvasKit.ColorType.RGBA_F16) {
+      pixBytes = 8;
+    }
+    dstRowBytes = dstRowBytes || (pixBytes * w);
 
     var len = h * dstRowBytes
     var pptr = CanvasKit._malloc(len);
@@ -1020,6 +1031,7 @@
       'height': h,
       'colorType': colorType,
       'alphaType': alphaType,
+      'colorSpace': colorSpace,
     }, pptr, dstRowBytes, x, y);
     if (!ok) {
       CanvasKit._free(pptr);
@@ -1036,7 +1048,7 @@
   // pixels is a TypedArray. No matter the input size, it will be treated as
   // a Uint8Array (essentially, a byte array).
   CanvasKit.SkCanvas.prototype.writePixels = function(pixels, srcWidth, srcHeight,
-                                                      destX, destY, alphaType, colorType) {
+                                                      destX, destY, alphaType, colorType, colorSpace) {
     if (pixels.byteLength % (srcWidth * srcHeight)) {
       throw 'pixels length must be a multiple of the srcWidth * srcHeight';
     }
@@ -1044,6 +1056,7 @@
     // supply defaults (which are compatible with HTMLCanvas's putImageData)
     alphaType = alphaType || CanvasKit.AlphaType.Unpremul;
     colorType = colorType || CanvasKit.ColorType.RGBA_8888;
+    colorSpace = colorSpace || CanvasKit.SkColorSpace.SRGB;
     var srcRowBytes = bytesPerPixel * srcWidth;
 
     var pptr = CanvasKit._malloc(pixels.byteLength);
@@ -1054,6 +1067,7 @@
       'height': srcHeight,
       'colorType': colorType,
       'alphaType': alphaType,
+      'colorSpace': colorSpace,
     }, pptr, srcRowBytes, destX, destY);
 
     CanvasKit._free(pptr);
@@ -1093,9 +1107,11 @@
     return copyColorFromWasm(cPtr);
   }
 
-  CanvasKit.SkPaint.prototype.setColor = function(color4f) {
+  CanvasKit.SkPaint.prototype.setColor = function(color4f, colorSpace) {
+    colorSpace = colorSpace || null; // null will be replaced with sRGB in the C++ method.
+    // emscripten wouldn't bind undefined to the sk_sp<SkColorSpace> expected here.
     var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
-    this._setColor(cPtr);
+    this._setColor(cPtr, colorSpace);
     CanvasKit._free(cPtr);
   }
 
@@ -1160,21 +1176,23 @@
     return dpe;
   }
 
-  CanvasKit.SkShader.Color = function(color4f) {
+  CanvasKit.SkShader.Color = function(color4f, colorSpace) {
+    colorSpace = colorSpace || null
     var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
-    var result = CanvasKit.SkShader._Color(cPtr);
+    var result = CanvasKit.SkShader._Color(cPtr, colorSpace);
     CanvasKit._free(cPtr);
     return result;
   }
 
-  CanvasKit.SkShader.MakeLinearGradient = function(start, end, colors, pos, mode, localMatrix, flags) {
+  CanvasKit.SkShader.MakeLinearGradient = function(start, end, colors, pos, mode, localMatrix, flags, colorSpace) {
+    colorSpace = colorSpace || null
     var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
     var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
     flags = flags || 0;
     var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
 
     var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
-                                                  colors.length, mode, flags, localMatrixPtr);
+                                                  colors.length, mode, flags, localMatrixPtr, colorSpace);
 
     CanvasKit._free(localMatrixPtr);
     CanvasKit._free(colorPtr);
@@ -1182,14 +1200,15 @@
     return lgs;
   }
 
-  CanvasKit.SkShader.MakeRadialGradient = function(center, radius, colors, pos, mode, localMatrix, flags) {
+  CanvasKit.SkShader.MakeRadialGradient = function(center, radius, colors, pos, mode, localMatrix, flags, colorSpace) {
+    colorSpace = colorSpace || null
     var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
     var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
     flags = flags || 0;
     var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
 
     var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
-                                                  colors.length, mode, flags, localMatrixPtr);
+                                                  colors.length, mode, flags, localMatrixPtr, colorSpace);
 
     CanvasKit._free(localMatrixPtr);
     CanvasKit._free(colorPtr);
@@ -1197,7 +1216,8 @@
     return rgs;
   }
 
-  CanvasKit.SkShader.MakeSweepGradient = function(cx, cy, colors, pos, mode, localMatrix, flags, startAngle, endAngle) {
+  CanvasKit.SkShader.MakeSweepGradient = function(cx, cy, colors, pos, mode, localMatrix, flags, startAngle, endAngle, colorSpace) {
+    colorSpace = colorSpace || null
     var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
     var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
     flags = flags || 0;
@@ -1208,7 +1228,7 @@
     var sgs = CanvasKit._MakeSweepGradientShader(cx, cy, colorPtr, posPtr,
                                                  colors.length, mode,
                                                  startAngle, endAngle, flags,
-                                                 localMatrixPtr);
+                                                 localMatrixPtr, colorSpace);
 
     CanvasKit._free(localMatrixPtr);
     CanvasKit._free(colorPtr);
@@ -1217,7 +1237,8 @@
   }
 
   CanvasKit.SkShader.MakeTwoPointConicalGradient = function(start, startRadius, end, endRadius,
-                                                            colors, pos, mode, localMatrix, flags) {
+                                                            colors, pos, mode, localMatrix, flags, colorSpace) {
+    colorSpace = colorSpace || null
     var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
     var posPtr =   copy1dArray(pos,    CanvasKit.HEAPF32);
     flags = flags || 0;
@@ -1225,7 +1246,7 @@
 
     var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
                           start, startRadius, end, endRadius,
-                          colorPtr, posPtr, colors.length, mode, flags, localMatrixPtr);
+                          colorPtr, posPtr, colors.length, mode, flags, localMatrixPtr, colorSpace);
 
     CanvasKit._free(localMatrixPtr);
     CanvasKit._free(colorPtr);
@@ -1335,13 +1356,14 @@
 
 // pixels must be a Uint8Array with bytes representing the pixel values
 // (e.g. each set of 4 bytes could represent RGBA values for a single pixel).
-CanvasKit.MakeImage = function(pixels, width, height, alphaType, colorType) {
+CanvasKit.MakeImage = function(pixels, width, height, alphaType, colorType, colorSpace) {
   var bytesPerPixel = pixels.length / (width * height);
   var info = {
     'width': width,
     'height': height,
     'alphaType': alphaType,
     'colorType': colorType,
+    'colorSpace': colorSpace,
   };
   var pptr = copy1dArray(pixels, CanvasKit.HEAPU8);
   // No need to _free pptr, Image takes it with SkData::MakeFromMalloc
diff --git a/modules/canvaskit/tests/core.spec.js b/modules/canvaskit/tests/core.spec.js
index 137753a..a15da3c 100644
--- a/modules/canvaskit/tests/core.spec.js
+++ b/modules/canvaskit/tests/core.spec.js
@@ -116,6 +116,7 @@
                 const imageInfo = {
                     alphaType: CanvasKit.AlphaType.Unpremul,
                     colorType: CanvasKit.ColorType.RGBA_8888,
+                    colorSpace: CanvasKit.SkColorSpace.SRGB,
                     width: img.width(),
                     height: img.height(),
                 };
@@ -172,7 +173,8 @@
               0,   0, 255, 255, // opaque blue
             255,   0, 255, 100, // transparent purple
         ]);
-        const img = CanvasKit.MakeImage(pixels, 1, 4, CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_8888);
+        const img = CanvasKit.MakeImage(pixels, 1, 4, CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_8888,
+            CanvasKit.SkColorSpace.SRGB);
         canvas.drawImage(img, 1, 1, paint);
         img.delete();
     });
@@ -305,7 +307,8 @@
             [transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
             [0, 0.65, 1.0],
             CanvasKit.TileMode.Mirror,
-            CanvasKit.SkMatrix.skewed(0.5, 0, 100, 100)
+            CanvasKit.SkMatrix.skewed(0.5, 0, 100, 100),
+            null, // color space
         );
         paint.setShader(rgsSkew);
         r = CanvasKit.LTRBRect(0, 100, 100, 200);
@@ -318,7 +321,8 @@
             [0, 0.65, 1.0],
             CanvasKit.TileMode.Mirror,
             CanvasKit.SkMatrix.skewed(0.5, 0, 100, 100),
-            1 // interpolate colors in premul
+            1, // interpolate colors in premul
+            null, // color space
         );
         paint.setShader(rgsSkewPremul);
         r = CanvasKit.LTRBRect(100, 100, 200, 200);
@@ -350,7 +354,8 @@
             [10, 110], 60, // end, radius
             [transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
             [0, 0.65, 1.0],
-            CanvasKit.TileMode.Mirror
+            CanvasKit.TileMode.Mirror,
+            null, // color space
         );
         paint.setShader(cgs);
         let r = CanvasKit.LTRBRect(0, 0, 100, 100);
@@ -365,6 +370,7 @@
             CanvasKit.TileMode.Mirror,
             null, // no local matrix
             1, // interpolate colors in premul
+            null, // color space
         );
         paint.setShader(cgsPremul);
         r = CanvasKit.LTRBRect(100, 0, 200, 100);
@@ -377,7 +383,8 @@
             [transparentGreen, CanvasKit.BLUE, CanvasKit.RED],
             [0, 0.65, 1.0],
             CanvasKit.TileMode.Mirror,
-            CanvasKit.SkMatrix.rotated(Math.PI/4, 0, 100)
+            CanvasKit.SkMatrix.rotated(Math.PI/4, 0, 100),
+            null, // color space
         );
         paint.setShader(cgs45);
         r = CanvasKit.LTRBRect(0, 100, 100, 200);
@@ -391,7 +398,8 @@
             [0, 0.65, 1.0],
             CanvasKit.TileMode.Mirror,
             CanvasKit.SkMatrix.rotated(Math.PI/4, 100, 100),
-            1 // interpolate colors in premul
+            1, // interpolate colors in premul
+            null, // color space
         );
         paint.setShader(cgs45Premul);
         r = CanvasKit.LTRBRect(100, 100, 200, 200);
@@ -585,6 +593,83 @@
         expect(paint.getColor()).toEqual(Float32Array.of(3.3, 2.2, 1.1, 0.5));
     });
 
+    describe('ColorSpace Support', () => {
+        it('Can create an SRGB 8888 surface', () => {
+            const colorSpace = CanvasKit.SkColorSpace.SRGB;
+            const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.SkColorSpace.SRGB);
+            expect(surface).toBeTruthy('Could not make surface');
+            let info = surface.imageInfo()
+            expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul);
+            expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_8888);
+            expect(CanvasKit.SkColorSpace.Equals(info.colorSpace, colorSpace))
+                .toBeTruthy("Surface not created with correct color space.");
+
+            const pixels = surface.getCanvas().readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+                CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_8888, colorSpace);
+            expect(pixels).toBeTruthy('Could not read pixels from surface');
+        });
+        it('Can create a Display P3 surface', () => {
+            const colorSpace = CanvasKit.SkColorSpace.DISPLAY_P3;
+            const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.SkColorSpace.DISPLAY_P3);
+            expect(surface).toBeTruthy('Could not make surface');
+            if (surface.reportBackendType() !== 'GPU') {
+                console.log('Not expecting color space support in cpu backed suface.');
+                return;
+            }
+            let info = surface.imageInfo()
+            expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul);
+            expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_F16);
+            expect(CanvasKit.SkColorSpace.Equals(info.colorSpace, colorSpace))
+                .toBeTruthy("Surface not created with correct color space.");
+
+            const pixels = surface.getCanvas().readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+                CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_F16, colorSpace);
+            expect(pixels).toBeTruthy('Could not read pixels from surface');
+        });
+        it('Can create an Adobe RGB surface', () => {
+            const colorSpace = CanvasKit.SkColorSpace.ADOBE_RGB;
+            const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.SkColorSpace.ADOBE_RGB);
+            expect(surface).toBeTruthy('Could not make surface');
+            if (surface.reportBackendType() !== 'GPU') {
+                console.log('Not expecting color space support in cpu backed suface.');
+                return;
+            }
+            let info = surface.imageInfo()
+            expect(info.alphaType).toEqual(CanvasKit.AlphaType.Unpremul);
+            expect(info.colorType).toEqual(CanvasKit.ColorType.RGBA_F16);
+            expect(CanvasKit.SkColorSpace.Equals(info.colorSpace, colorSpace))
+                .toBeTruthy("Surface not created with correct color space.");
+
+            const pixels = surface.getCanvas().readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+                CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_F16, colorSpace);
+            expect(pixels).toBeTruthy('Could not read pixels from surface');
+        });
+
+        it('combine draws from several color spaces', () => {
+            const surface = CanvasKit.MakeCanvasSurface('test', CanvasKit.SkColorSpace.ADOBE_RGB);
+            expect(surface).toBeTruthy('Could not make surface');
+            if (surface.reportBackendType() !== 'GPU') {
+                console.log('Not expecting color space support in cpu backed suface.');
+                return;
+            }
+            const canvas = surface.getCanvas();
+
+            let paint = new CanvasKit.SkPaint();
+            paint.setColor(CanvasKit.RED, CanvasKit.SkColorSpace.ADOBE_RGB);
+            canvas.drawPaint(paint);
+            paint.setColor(CanvasKit.RED, CanvasKit.SkColorSpace.DISPLAY_P3); // 93.7 in adobeRGB
+            canvas.drawRect(CanvasKit.LTRBRect(200, 0, 400, 600), paint);
+            paint.setColor(CanvasKit.RED, CanvasKit.SkColorSpace.SRGB); // 85.9 in adobeRGB
+            canvas.drawRect(CanvasKit.LTRBRect(400, 0, 600, 600), paint);
+
+            // this test paints three bands of red, each the maximum red that a color space supports.
+            // They are each represented by skia by some color in the Adobe RGB space of the surface,
+            // as floats between 0 and 1.
+
+            // TODO(nifong) readpixels and verify correctness after f32 readpixels bug is fixed
+        });
+    }); // end describe('ColorSpace Support')
+
     describe('DOMMatrix support', () => {
         gm('sweep_gradient_dommatrix', (canvas) => {
             const paint = new CanvasKit.SkPaint();