- From: Tab Atkins Jr. <jackalmage@gmail.com>
- Date: Fri, 8 Aug 2014 13:11:54 -0700
- To: www-style list <www-style@w3.org>
Currently, the JS color classes in the Color spec are one-to-one with the color functions - there's an RGBColor for rgb(), an HSLColor() for hsl(), etc. Some people have suggested merging some of these together, so you don't have to manually convert between them. As I mentioned on the last telcon, I have several reasons why this would be a bad idea: 1. Naming Collisions ================ Currently, the members of each class are named by a single letter, just like the function: RGBColor has r, g, b, and a members, etc. If we merged color classes together, then the "b" member of rgb() and the "b" member of hwb() would clash, as they mean different things. (The "h" from hsl() and hwb() also clash, as do the "a" members of all of the classes, but they're interpreted identically, so that doesn't matter.) The obvious fix for this is to expand the names from single-letter (from the function names) to words. That doesn't actually work, though: "black" is shared, with different meaning, by both CMYK and HWB. Those two live in different color spaces, but if we ever add HSV, which I think is reasonable, both it and HSL share "saturation" with different meaning. We could further disambiguate these on a case-by-case basis with something like "hslSaturation" and "hsvSaturation", but that's obviously terrible, both by itself and as a special-case. (And applying that to *all* the members would just be ridiculous.) In general, I don't think we can come up with a non-ridiculous scheme that will avoid these kinds of collisions. 2. We still need multiple classes, and the categorization is non-obvious ====================================================== As Florian argued in his recent mail, CMYK *really isn't* comparable to RGB and the others. Most of the color functions in CSS define colors in the sRGB colorspace, but device-cmyk() (and its analogue, the CMYKColor class) specify colors in a device-specific CMYK colorspace. You can convert trivially between colors in the same colorspace, but not between ones in different colorspaces. (The "naive conversion" algorithm defined in the spec allows you to treat device-cmyk() as if it was in the sRGB colorspace, but it's a pretty terrible conversion.) I don't think there's a reasonable way to combine things in different colorspaces into a single class; dealing with the color profiles would get really clumsy. So we probably need two color classes, at minimum, to address the current color syntaxes - one for rgb/hsl/hwb/hex, and one for cmyk. As we add more syntaxes for things in other colorspaces (CIELab, anyone?), we'll need more classes as well. However, most authors have no idea what colorspaces are, nor do they need to worry about this the vast majority of the time. I suspect that the grouping of syntaxes by colorspace will be very non-obvious and potentially confusing. 3. Constant conversions are unpredictably expensive ======================================== The current situation, with multiple classes, means that as long as you're operating on a color in a given syntax, getting/setting is trivial and cheap - it's just accessing ordinary numeric properties, with no unexpected overhead. There's an obvious cost when converting between classes, but that's easy to understand and spot; I think authors can tell quite easily that regularly converting back and forth between color classes is expensive. However, if the classes are merged, *one* syntax will be privileged as the "canonical" storage format, and getting/setting any other format will have to invoke the conversion algorithm every single time. For example, if RGB is used as the main format, setting hue means converting the resultant color back to RGB and storing it that way. This cost, while not huge, means that anyone going for high-throughput color manipulation will have to work with colors in the canonical format, even if it's not convenient for the purpose at hand. There are various ways to make this less bad, like caching read results until there's a change, or switching between canonical formats on the fly to deal with usage patterns, but they incur a decent amount of implementation complexity for something that's ultimately unnecessary. Also, conversions mean that you'll often get situations where if you set an integer and then read it right back you'll get a non-integer, due to precision loss during the conversion. For example, most integer hue angles in HSL aren't preserved when converting to/from RGB. This isn't a huge deal, as the precision loss is far too small to be noticed, or even represented internally, but it does make for a clumsier interaction with the API. 4. Allowing author extension is much more difficult ====================================== I carefully designed the current system of classes to make it very easy for authors to add new color classes that feel "first-class", like they were provided by the browser itself. There are no special abilities or non-configurable magic that authors dont' have access to. Merging classes makes this much more difficult. You can't provide your own extensions to an existing constructor, so it's impossible to make the constructor accept arguments for your own color syntax. You also can't hook into any of the possible optimizations that the UA might be applying to avoid constant conversions, so your syntax is guaranteed to be slower. (Unless you did something really complicated, like switching the built-in class with a Proxy that intercepted gets/sets, so you can implement your own caching layer.) (Actually, I'm not sure how the constructor would work in the first place. You can't distinguish between different types of dictionaries in WebIDL, and most color functions have basically identical argument number and types once you strip their units. We'd need to either have a type argument in the constructor, like `Color("rgb", 1, 0, 0)`, or just give it a trivial constructor and instead rely solely on factory functions, like `Color.fromRGB(1, 0, 0)`. Either way is rather clumsy.) Finally, you'd have to decide which class to extend, which again means you have to understand colorspaces well enough to make an informed decision. Or you can just make your color class separate, which again sets it apart from the other color classes and makes it feel non-native. ~TJ
Received on Friday, 8 August 2014 20:12:41 UTC