1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkColorSpacePriv.h"
9 #include "SkColorSpaceXformSteps.h"
10 #include "Test.h"
11 
DEF_TEST(SkColorSpaceXformSteps,r)12 DEF_TEST(SkColorSpaceXformSteps, r) {
13     auto srgb   = SkColorSpace::MakeSRGB(),
14          adobe  = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB),
15          srgb22 = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kSRGB),
16          srgb1  = srgb ->makeLinearGamma(),
17          adobe1 = adobe->makeLinearGamma();
18 
19     auto premul =   kPremul_SkAlphaType,
20          opaque =   kOpaque_SkAlphaType,
21        unpremul = kUnpremul_SkAlphaType;
22 
23     struct {
24         sk_sp<SkColorSpace> src, dst;
25         SkAlphaType         srcAT, dstAT;
26 
27         bool unpremul;
28         bool linearize;
29         bool gamut_transform;
30         bool encode;
31         bool premul;
32 
33     } tests[] = {
34         // The general case is converting between two color spaces with different gamuts
35         // and different transfer functions.  There's no optimization possible here.
36         { adobe, srgb, premul, premul,
37             true,  // src is encoded as f(s)*a,a, so we unpremul to f(s),a before linearizing.
38             true,  // linearize to s,a
39             true,  // transform s to dst gamut, s'
40             true,  // encode with dst transfer function, g(s'), a
41             true,  // premul to g(s')*a, a
42         },
43         // All the same going the other direction.
44         { srgb, adobe, premul, premul,  true,true,true,true,true },
45 
46         // If the src alpha type is unpremul, we'll not need that initial unpremul step.
47         { adobe, srgb, unpremul, premul, false,true,true,true,true },
48         { srgb, adobe, unpremul, premul, false,true,true,true,true },
49 
50         // If opaque, we need neither the initial unpremul, nor the premul later.
51         { adobe, srgb, opaque, premul, false,true,true,true,false },
52         { srgb, adobe, opaque, premul, false,true,true,true,false },
53 
54 
55         // Now let's go between sRGB and sRGB with a 2.2 gamma, the gamut staying the same.
56         { srgb, srgb22, premul, premul,
57             true,  // we need to linearize, so we need to unpremul
58             true,  // we need to encode to 2.2 gamma, so we need to get linear
59             false, // no need to change gamut
60             true,  // linear -> gamma 2.2
61             true,  // premul going into the blend
62         },
63         // Same sort of logic in the other direction.
64         { srgb22, srgb, premul, premul,  true,true,false,true,true },
65 
66         // As in the general case, when we change the alpha type unpremul and premul steps drop out.
67         { srgb, srgb22, unpremul, premul, false,true,false,true,true },
68         { srgb22, srgb, unpremul, premul, false,true,false,true,true },
69         { srgb, srgb22,   opaque, premul, false,true,false,true,false },
70         { srgb22, srgb,   opaque, premul, false,true,false,true,false },
71 
72         // Let's look at the special case of completely matching color spaces.
73         // We should be ready to go into the blend without any fuss.
74         { srgb, srgb,   premul, premul, false,false,false,false,false },
75         { srgb, srgb, unpremul, premul, false,false,false,false,true },
76         { srgb, srgb,   opaque, premul, false,false,false,false,false },
77 
78         // We can drop out the linearize step when the source is already linear.
79         { srgb1, adobe,   premul, premul, true,false,true,true,true },
80         { srgb1,  srgb,   premul, premul, true,false,false,true,true },
81         // And we can drop the encode step when the destination is linear.
82         { adobe, srgb1,   premul, premul, true,true,true,false,true },
83         {  srgb, srgb1,   premul, premul, true,true,false,false,true },
84 
85         // Here's an interesting case where only gamut transform is needed.
86         { adobe1, srgb1,   premul, premul, false,false,true,false,false },
87         { adobe1, srgb1,   opaque, premul, false,false,true,false,false },
88         { adobe1, srgb1, unpremul, premul, false,false,true,false, true },
89 
90         // Just finishing up with something to produce each other possible output.
91         // Nothing terribly interesting in these eight.
92         { srgb,  srgb1,   opaque, premul, false, true,false,false,false },
93         { srgb,  srgb1, unpremul, premul, false, true,false,false, true },
94         { srgb, adobe1,   opaque, premul, false, true, true,false,false },
95         { srgb, adobe1, unpremul, premul, false, true, true,false, true },
96         { srgb1,  srgb,   opaque, premul, false,false,false, true,false },
97         { srgb1,  srgb, unpremul, premul, false,false,false, true, true },
98         { srgb1, adobe,   opaque, premul, false,false, true, true,false },
99         { srgb1, adobe, unpremul, premul, false,false, true, true, true },
100 
101         // Now test non-premul outputs.
102         { srgb , srgb  , premul, unpremul, true,false,false,false,false },
103         { srgb , srgb1 , premul, unpremul, true, true,false,false,false },
104         { srgb1, adobe1, premul, unpremul, true,false, true,false,false },
105         { srgb , adobe1, premul, unpremul, true, true, true,false,false },
106         { srgb1, srgb  , premul, unpremul, true,false,false, true,false },
107         { srgb , srgb22, premul, unpremul, true, true,false, true,false },
108         { srgb1, adobe , premul, unpremul, true,false, true, true,false },
109         { srgb , adobe , premul, unpremul, true, true, true, true,false },
110 
111         // Opaque outputs are treated as the same alpha type as the source input.
112         // TODO: we'd really like to have a good way of explaining why we think this is useful.
113         { srgb , srgb  , premul, opaque, false,false,false,false,false },
114         { srgb , srgb1 , premul, opaque,  true, true,false,false, true },
115         { srgb1, adobe1, premul, opaque, false,false, true,false,false },
116         { srgb , adobe1, premul, opaque,  true, true, true,false, true },
117         { srgb1, srgb  , premul, opaque,  true,false,false, true, true },
118         { srgb , srgb22, premul, opaque,  true, true,false, true, true },
119         { srgb1, adobe , premul, opaque,  true,false, true, true, true },
120         { srgb , adobe , premul, opaque,  true, true, true, true, true },
121 
122         { srgb , srgb  , unpremul, opaque, false,false,false,false,false },
123         { srgb , srgb1 , unpremul, opaque, false, true,false,false,false },
124         { srgb1, adobe1, unpremul, opaque, false,false, true,false,false },
125         { srgb , adobe1, unpremul, opaque, false, true, true,false,false },
126         { srgb1, srgb  , unpremul, opaque, false,false,false, true,false },
127         { srgb , srgb22, unpremul, opaque, false, true,false, true,false },
128         { srgb1, adobe , unpremul, opaque, false,false, true, true,false },
129         { srgb , adobe , unpremul, opaque, false, true, true, true,false },
130     };
131 
132     uint32_t tested = 0x00000000;
133     for (auto t : tests) {
134         SkColorSpaceXformSteps steps{t.src.get(), t.srcAT,
135                                      t.dst.get(), t.dstAT};
136         REPORTER_ASSERT(r, steps.flags.unpremul        == t.unpremul);
137         REPORTER_ASSERT(r, steps.flags.linearize       == t.linearize);
138         REPORTER_ASSERT(r, steps.flags.gamut_transform == t.gamut_transform);
139         REPORTER_ASSERT(r, steps.flags.encode          == t.encode);
140         REPORTER_ASSERT(r, steps.flags.premul          == t.premul);
141 
142         uint32_t bits = (uint32_t)t.unpremul        << 0
143                       | (uint32_t)t.linearize       << 1
144                       | (uint32_t)t.gamut_transform << 2
145                       | (uint32_t)t.encode          << 3
146                       | (uint32_t)t.premul          << 4;
147         tested |= (1<<bits);
148     }
149 
150     // We'll check our test cases cover all 2^5 == 32 possible outputs.
151     for (uint32_t t = 0; t < 32; t++) {
152         if (tested & (1<<t)) {
153             continue;
154         }
155 
156         // There are a couple impossible outputs, so consider those bits tested.
157         //
158         // Unpremul then premul should be optimized away to a noop, so 0b10001 isn't possible.
159         // A gamut transform in the middle is fine too, so 0b10101 isn't possible either.
160         if (t == 0b10001 || t == 0b10101) {
161             continue;
162         }
163 
164         ERRORF(r, "{ xxx, yyy, at, %s,%s,%s,%s,%s }, not covered",
165                 (t& 1) ? " true" : "false",
166                 (t& 2) ? " true" : "false",
167                 (t& 4) ? " true" : "false",
168                 (t& 8) ? " true" : "false",
169                 (t&16) ? " true" : "false");
170     }
171 }
172