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