Use context2D to convert CSS4 colors to display-p3#2145
Conversation
* memoize (with some decent limit) for faster renders * clear rect to guard against semi-transparent color leaks
|
OK now it's fast in both examples (p3 and CSS4 + sRGB). |
|
One thing that doesn't work here is the serialization to disk made by our unit tests; you can see the two new SVGs appear black (in this PR's preview on github, as well as on dick for me). I think it's okay for now, but it means that node-canvas doesn't support CSS4 nor display-p3? Needs a bit more investigation. |
|
After investigation:
|
| return {r, g, b}; | ||
| }; | ||
| let p; | ||
| return colorSpace === "srgb" ? (c) => (isNaN((p = rgb(c)).opacity) ? canvasConverter(c) : p) : canvasConverter; |
There was a problem hiding this comment.
I don’t like switching to the canvasConverter in the sRGB case when the opacity is NaN… I would prefer to always use the simple rgbConvert in the sRGB case (the current behavior), and allow the user to opt-in to the canvasConverter implementation if desired (to have the browser handle color conversion), and make the canvasConverter implementation the default in the non-sRGB case. We could also expose these converter implementations similar to how we expose interpolatorBarycentric etc. This would also allow you to force “direct” (or “vivid”) conversion from RGB bytes to P3 XYZ if desired, though perhaps ill-advised.
There was a problem hiding this comment.
This converter adds support both for (a) CSS4 color strings in the sRGB space, and (b) display-p3. (The NaN check is only for additional speed in the sRGB case where d3.rgb would be faster, so we try that first, then switch to canvas as a fall-back; but it has an additional cost for all invalid color strings.)
I can see how we could want (a) to be opt-in, so that by default we only use d3.rgb, for simplicity—I'm not sure if it's better for the user but at least it would not be slower in the case where many colors are invalid strings.
I feel that the fact that we are converting the color string through canvas is an implementation detail, so I'd be wary about exposing this under the name "canvas". Maybe colorConverter: "css4" would be a better name?
And imo using this converter with sRGB should still try to parse with d3.rgb first, for speed.
PS: Re-reading the code I see that it doesn't support rgba colors, as it drops the a part. This feels wrong. (Edit: FIXED)
There was a problem hiding this comment.
No, I think given the complexity it is inherently a leaky abstraction and the user can decide whether to use our limited color parser or (at the expense of performance) the canvas-based one.
|
I want to rethink that. If we implemented a css4 color string parser, we wouldn't have to juggle with that complexity and speed hit and incompatibility with SSR. There is a reference implementation in https://colorjs.io/ (https://github.com/color-js/color.js). The library is too big to include as is, but I can extract the formulas into a smaller file, either to extend d3-color, or tailored for our purposes. |
Since we rely on the browser to parse CSS4, and to create a canvas, we can rely on it to also convert these colors to whatever color space it supports.
Maybe this is a hack—or maybe it's the only correct way.
If it's the correct way, maybe we can apply this technique to also support CSS4 colors in the sRGB color space, instead of relying on d3.rgb.