Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support the p3 color space in the raster mark #2138

Open
jwoLondon opened this issue Aug 16, 2024 · 8 comments · May be fixed by #2143
Open

Support the p3 color space in the raster mark #2138

jwoLondon opened this issue Aug 16, 2024 · 8 comments · May be fixed by #2143
Assignees
Labels
enhancement New feature or request

Comments

@jwoLondon
Copy link

jwoLondon commented Aug 16, 2024

Enhancement request: Allow the raster mark to use CSS4 colour specifications and the P3 colour space.

Marks that generate SVG currently work with modern colour specifications, meaning they can specify colours using new colour spaces such as Oklch. For example, this works correctly:

Plot.cellX([1], { fill: "oklch(52.6% 0.24 29.5deg)" }).plot()

Not only does this give access to improved perceptual colour spaces, but also allows deeply saturated colours outside the sRGB gamut but in the P3 gamut to be rendered correctly on displays that support P3 (with a graceful fallback to the nearest sRGB colour for those which don't).

However, the raster mark, which relies on Canvas, does not currently support either modern colour specification, nor the P3 colour space. The following renders a raster as black rather than the intended saturated red:

Plot.raster([1], {
  width: 1,
  height: 1,
  fill: "oklch(52.6% 0.24 29.5deg)"
}).plot()

It would be great to have support for modern colour specs in Canvas.

As far as I can see, the current implementation creates an sRGB canvas rather than a p3-friendly one. I think that would just require

getContext("2d", { colorSpace: "display-p3" });

But handling CSS 4 colour specs looks a little more tricky as the implementation seems to rely on d3.color which currently does not handle the modern comma-free colour format. I note d3 issue #87 though.

@jwoLondon jwoLondon added the enhancement New feature or request label Aug 16, 2024
@Fil
Copy link
Contributor

Fil commented Aug 16, 2024

I would love that, also for "p3" color scales.

@Fil
Copy link
Contributor

Fil commented Aug 16, 2024

Another potential problem (if I remember correctly from my last tentative), is that the canvas we create is then serialized into the SVG with

.attr("xlink:href", canvas.toDataURL())

I'm not sure if we can keep its colorSpace as we do that.

@Fil
Copy link
Contributor

Fil commented Aug 21, 2024

To close the loop, this fantastic notebook just published on the topic:

(thanks, @jwoLondon!)

@mbostock
Copy link
Member

We don’t use d3-color for parsing anymore, as of #1454 (#1851). See isColor:

plot/src/options.js

Lines 514 to 527 in e50254d

// Returns true if value is a valid CSS color string. This is intentionally lax
// because the CSS color spec keeps growing, and we don’t need to parse these
// colors—we just need to disambiguate them from column names.
// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
// https://www.w3.org/TR/css-color-5/
export function isColor(value) {
if (typeof value !== "string") return false;
value = value.toLowerCase().trim();
return (
/^#[0-9a-f]{3,8}$/.test(value) || // hex rgb, rgba, rrggbb, rrggbbaa
/^(?:url|var|rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color|color-mix)\(.*\)$/.test(value) || // <funciri>, CSS variable, color, etc.
namedColors.has(value) // currentColor, red, etc.
);
}

So I think the only thing missing here is some way of specifying the colorSpace for the raster mark’s canvas. Retitling the issue accordingly.

@mbostock mbostock changed the title Support for modern color specifications in the raster mark Support the p3 color space in the raster mark Aug 21, 2024
@mbostock mbostock self-assigned this Aug 21, 2024
@mbostock mbostock linked a pull request Aug 21, 2024 that will close this issue
@Fil
Copy link
Contributor

Fil commented Aug 22, 2024

The raster mark uses d3.rgb:

let {r, g, b} = rgb(this.fill) ?? {r: 0, g: 0, b: 0};

({r, g, b} = rgb(fi));

I've added lots of comments on #2143 which I should probably transfer here at some point.

@mbostock
Copy link
Member

Ah thanks for reminding me @Fil. I like your proposed solution. How does it perform going through canvas?

@Fil
Copy link
Contributor

Fil commented Aug 22, 2024

It sure takes a hit: on my machine rasterVaporP3 takes about 300ms, compared to rasterVapor 110ms.

Still, it's not too awful, and it becomes an optimization problem. It will be fun to research once we think it's "correct".

I feel it would be useful to apply this technique also in the srgb + CSS4 case — unless we get to a point where using fillRect is faster than editing the data matrix directly… but I doubt that there is any chance of that happening). Then we can say that css 4 colors are fully supported.

@Fil
Copy link
Contributor

Fil commented Sep 3, 2024

Another interesting post on the topic, by @clhenrick:
https://clhenrick.io/blog/color-experiments-with-oklch/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants