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

Define color space for interpolation interface for canvas gradients #8296

Open
mysteryDate opened this issue Sep 19, 2022 · 4 comments
Open

Comments

@mysteryDate
Copy link
Contributor

mysteryDate commented Sep 19, 2022

CSS Color 4 defines an interface for CSS gradients to interpolate within user defined color spaces. For example:

linear-gradient(in lab, red, blue);
radial-gradient(circle at center in hsl longer hue, rgb(255, 0, 0), lab(0, 0, 0));

On top of that, the CSS spec states that OKLab should be the default color space for interpolations when no color space is defined by the user, except possibly for "gradients with non-legacy sRGB color formats (hex colors, named colors, rgb(), hsl() or hwb() and the equivalent alpha-including forms)". It seems logical that this should also be the case for canvas gradients.

Two pieces of information need to be consumed by CanvasRenderingContext2D.create(Linear|Radial|Conic)Gradient():

  • The color interpolation space, which is one of ["lab", "lch", "oklab", "oklch", "srgb-linear", "srgb", "xyz-d65", "xyz-d50", "hsl", "hwb"]
  • The hue interpolation method, which is one of ["shorter", "longer", "increasing", "decreasing", "specified"]

Of course, we don't have to use the same strings as CSS, but it feels really evil to do otherwise. Using createLinearGradient as an example (with ctx as an instance of CanvasRenderingContext2D), here are three possibilities that I can think of off the top of my head:

  1. Final, positional arguments upon creation:
ctx.createLinearGradient(0, 0, 1, 1, "lch", "longer");

I don't love this. The position feels arbitrary. In CSS the color spaces are prefixed, but we couldn't do that here without breaking compatibility. createRadialGradient also already has six positional arguments, adding more feels like bad design.

  1. A gradientSettings-esque object. Akin to the final argument to getContext:
ctx.createLinearGradient(0, 0, 1, 1, {colorInterpolationSpace: "lch", hueInterpolationMethod: "longer"});

My favorite. Leaves the possibility to add more attributes in the future. Though it's not totally clear what the attributes should be named. colorInterpolationSpace or just colorSpace?

  1. Attributes on the object that can be modified:
const grad = ctx.createLinearGradient(0, 0, 1, 1);
grad.colorSpace; // "sRGB"
grad.colorSpace = "OKLab";

Seems like it might be kind of a mess. Would the user expect a gradient on screen to change when these attributes are changed? That would certainly add difficulty to implementation. One positive is that it follows the addColorStop pattern. Regardless, I think it would be nice to have these attributes visible to developers, even if they're not modifiable after creation.

Personally, I vote for (2). I'm happy to start writing the spec if we can get consensus here.

@fserb
Copy link
Contributor

fserb commented Sep 19, 2022

I think we could drop the interpolation on the object names, for concision and we should have properties exposed too (even if they are read-only).

I'm not sure why allowing after-construction change would be an issue, given that gradients are already mutable after assignment (with addStop()). We should have (2) and (3) combined.

Another alternative would be to accept both an Object (like your proposal) or a string containing both information ("lch longer", "lab") as the last parameter, if folks find having an object too verbose. But this is probably an overkill and we should stick to the object for initialization + property.

Tagging @whatwg/canvas folks.

@kdashg
Copy link

kdashg commented Sep 19, 2022

It depends if we want to encourage authors to modify after creation. I think implementation-wise, if we support addStop, adding colorspace mutation support should be easy, so I think this is an API design question.

Do "we" have a philosophy here with regards to strongly discouraging mutation?

@mysteryDate
Copy link
Contributor Author

Do "we" have a philosophy here with regards to strongly discouraging mutation?

Not as far as I'm concerned! Mutating things is fun!

I'm not sure why allowing after-construction change would be an issue, given that gradients are already mutable after assignment (with addStop()). We should have (2) and (3) combined.

Yeah, I probably overstated this. I was thinking about what a mess mutation was for filters.

@Kaiido
Copy link
Member

Kaiido commented Sep 20, 2022

Regarding the mutability of CanvasGradient objects, I want to clarify that modifying it through addColorStop() is currently the only way to define a non transparent CanvasGradient object. I don't think mutability is a problem here.

For the API design, there is also #8214 which will probably extend these methods, it would be great to coordinate the efforts on this.
Personally I'd also vote for the option bag + properties idea (2 & 3), though adding properties may open the question "why not expose the color-stops?".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

4 participants