1 2--- 3title: "Skia Color Management" 4linkTitle: "Skia Color Management" 5 6--- 7 8 9What we mean by color management 10-------------------------------- 11 12All the color spaces Skia works with describe themselves by how to transform 13colors from that color space to a common "connection" color space called XYZ 14D50. And we can infer from that same description how to transform from that 15XYZ D50 space back to the original color space. XYZ D50 is a color space 16represented in three dimensions like RGB, but the XYZ parts are not RGB-like at 17all, rather a linear remix of those channels. Y is closest to what you'd think 18of as brightness, but X and Z are a little more abstract. It's kind of like 19YUV if you're familiar with that. The "D50" part refers to the whitepoint of 20this space, around 5000 Kelvin. 21 22All color managed drawing is divided into six parts, three steps connecting the 23source colors to that XYZ D50 space, then three symmetric steps connecting back 24from XYZ D50 to the destination color space. Some of these steps can 25annihilate with each other into no-ops, sometimes all the way to the entire 26process amounting to a no-op when the source space and destination space are 27the same. Here are the steps: 28 29Color management steps 30---------------------- 31 321. unpremultiply if the source color is premultiplied -- alpha is not involved 33 in color management, and we need to divide it out if it's multiplied in 342. linearize the source color using the source color space's transfer function 353. convert those unpremultiplied, linear source colors to XYZ D50 gamut by 36 multiplying by a 3x3 matrix 374. convert those XYZ D50 colors to the destination gamut by multiplying by a 3x3 matrix 385. encode that color using the inverse of the destination color space's transfer function 396. premultiply by alpha if the destination is premultiplied 40 41If you poke around in our code the clearest place to see this logic is in a 42type called SkColorSpaceXformSteps. You'll see it as 5 steps there: we always 43merge the innermost two operations into a single 3x3 matrix multiply. 44 45Optimizations 46------------- 47 48Whenever we're about to do some drawing we look at which of those steps we 49really need to do. Any step that's a fundamental no-op we skip: 50 51 * skip 1 if the source is already unpremultiplied 52 * skip 2 if the source is already linearly encoded 53 * skip 3 and 4 if that single concatenated matrix is identity (i.e. the 54 source and destination color spaces have the same gamut) 55 * skip 5 if the destination wants linear encoding 56 * skip 6 if the destination wants to be unpremultiplied 57 58We can reason from those basic skips into some more advanced optimizations: 59 60 * if we've skipped 3 and 4 already, we can skip 2 and 5 any time the transfer 61 functions are the same -- sending colors through a given transfer function 62 and its own inverse is a no-op 63 * if we've skipped all of 2-5, we can skip 1 and 6 if we were going to do 64 both --- no sense in unpremultiplying just to re-premultiply. 65 * opaque colors can be treated as either unpremultiplied or premultiplied, 66 whichever lets us skip more steps. 67 68All this comes together to an impressive "nothing to do" most of the time. If 69you're drawing opaque colors in a given color space to a destination tagged 70with that same color space, we'll notice we can skip all six steps. Sometimes 71fewer steps are needed, sometimes more. In general if you need to do a gamut 72conversion, you should generally expect all the middle steps to be active. 73Steps 2 and 5 are by far the most expensive to compute. 74 75nullptr SkColorSpace defaults 76----------------------------- 77 78Now how do nullptr SkColorSpace defaults work into all of this? We preface all 79that logic I've just mentioned above with this little snippet: 80 81 if (srcCS == nullptr) { srcCS = sRGB; } 82 if (dstCS == nullptr) { dstCS = srcCS; } 83 84(Order matters there.) The gist is, we assume any untagged sources are sRGB. 85And if you leave your surface untagged, we act as if your destination fluidly 86matches whatever source you're trying to draw into it, which skips at least 87steps 2-5 as listed above, maintaining an unmanaged color mode of drawing 88compatible with how retro Skia used to work before we introduce color 89management. It's not very principled, but it's handy in practice to keep 90around. 91 92