• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gm.h"
9 
10 #include "SkColorSpace.h"
11 #include "SkColorSpaceXformSteps.h"
12 #include "SkDashPathEffect.h"
13 #include "SkFont.h"
14 #include "SkGradientShader.h"
15 #include "SkString.h"
16 
nearly_equal(SkColor4f x,SkColor4f y)17 static bool nearly_equal(SkColor4f x, SkColor4f y) {
18     const float K = 0.01f;
19     return fabsf(x.fR - y.fR) < K
20         && fabsf(x.fG - y.fG) < K
21         && fabsf(x.fB - y.fB) < K
22         && fabsf(x.fA - y.fA) < K;
23 }
24 
fmt(SkColor4f c)25 static SkString fmt(SkColor4f c) {
26     return SkStringPrintf("%.2g %.2g %.2g %.2g", c.fR, c.fG, c.fB, c.fA);
27 }
28 
transform(SkColor4f c,SkColorSpace * src,SkColorSpace * dst)29 static SkColor4f transform(SkColor4f c, SkColorSpace* src, SkColorSpace* dst) {
30     SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType,
31                            dst, kUnpremul_SkAlphaType).apply(c.vec());
32     return c;
33 }
34 
compare_pixel(const char * label,SkCanvas * canvas,int x,int y,SkColor4f color,SkColorSpace * cs)35 static void compare_pixel(const char* label,
36                           SkCanvas* canvas, int x, int y,
37                           SkColor4f color, SkColorSpace* cs) {
38     SkPaint paint;
39     SkFont font;
40     auto canvas_cs = canvas->imageInfo().refColorSpace();
41 
42     // I'm not really sure if this makes things easier or harder to follow,
43     // but we sniff the canvas to grab its current y-translate, so that (x,y)
44     // can be written in sort of chunk-relative terms.
45     const SkMatrix& m = canvas->getTotalMatrix();
46     SkASSERT(m.isTranslate());
47     SkScalar dy = m.getTranslateY();
48     SkASSERT(dy == (int)dy);
49     y += (int)dy;
50 
51     SkBitmap bm;
52     bm.allocPixels(SkImageInfo::Make(1,1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType, canvas_cs));
53     if (!canvas->readPixels(bm, x,y)) {
54         MarkGMGood(canvas, 140,40);
55         canvas->drawString("can't readPixels() on this canvas :(", 100,20, font, paint);
56         return;
57     }
58 
59     SkColor4f pixel;
60     memcpy(&pixel, bm.getAddr(0,0), sizeof(pixel));
61 
62     SkColor4f expected = transform(color,cs, canvas_cs.get());
63     if (canvas->imageInfo().colorType() < kRGBA_F16_SkColorType) {
64         // We can't expect normalized formats to hold values outside [0,1].
65         for (int i = 0; i < 4; ++i) {
66             expected[i] = SkTPin(expected[i], 0.0f, 1.0f);
67         }
68     }
69     if (canvas->imageInfo().colorType() == kGray_8_SkColorType) {
70         // Drawing into Gray8 is known to be maybe-totally broken.
71         // TODO: update expectation here to be {lum,lum,lum,1} if we fix Gray8.
72         expected = SkColor4f{NAN, NAN, NAN, 1};
73     }
74 
75     if (nearly_equal(pixel, expected)) {
76         MarkGMGood(canvas, 140,40);
77     } else {
78         MarkGMBad(canvas, 140,40);
79     }
80 
81     struct {
82         const char* label;
83         SkColor4f   color;
84     } lines[] = {
85         {"Pixel:"   , pixel   },
86         {"Expected:", expected},
87     };
88 
89     SkAutoCanvasRestore saveRestore(canvas, true);
90     canvas->drawString(label, 80,20, font, paint);
91     for (auto l : lines) {
92         canvas->translate(0,20);
93         canvas->drawString(l.label,               80,20, font, paint);
94         canvas->drawString(fmt(l.color).c_str(), 140,20, font, paint);
95     }
96 }
97 
98 DEF_SIMPLE_GM(p3, canvas, 450, 1300) {
99     auto p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
100     auto srgb = SkColorSpace::MakeSRGB();
101 
__anon45bf74810202(SkColor4f c) 102     auto p3_to_srgb = [&](SkColor4f c) {
103         SkPaint p;
104         p.setColor4f(c, p3.get());
105         return p.getColor4f();
106     };
107 
108     // Draw a P3 red rectangle and check the corner.
109     {
110         SkPaint paint;
111         paint.setColor4f({1,0,0,1}, p3.get());
112 
113         canvas->drawRect({10,10,70,70}, paint);
114         compare_pixel("drawRect P3 red ",
115                       canvas, 10,10,
116                       {1,0,0,1}, p3.get());
117     }
118 
119     canvas->translate(0,80);
120 
121     // Draw a P3 red bitmap, using a draw.
122     {
123         SkBitmap bm;
124         bm.allocPixels(SkImageInfo::Make(60,60, kRGBA_F16_SkColorType, kPremul_SkAlphaType, p3));
125 
126         SkPaint paint;
127         paint.setColor4f({1,0,0,1}, p3.get());
128         SkCanvas{bm}.drawPaint(paint);
129 
130         canvas->drawBitmap(bm, 10,10);
131         compare_pixel("drawBitmap P3 red, from drawPaint",
132                       canvas, 10,10,
133                       {1,0,0,1}, p3.get());
134     }
135 
136     canvas->translate(0,80);
137 
138     // Draw a P3 red bitmap, using SkBitmap::eraseColor().
139     {
140         SkBitmap bm;
141         bm.allocPixels(SkImageInfo::Make(60,60, kRGBA_F16_SkColorType, kPremul_SkAlphaType, p3));
142 
143         bm.eraseColor(0xffff0000/*in P3*/);
144 
145         canvas->drawBitmap(bm, 10,10);
146         compare_pixel("drawBitmap P3 red, from SkBitmap::eraseColor()",
147                       canvas, 10,10,
148                       {1,0,0,1}, p3.get());
149     }
150 
151     canvas->translate(0,80);
152 
153     // Draw a P3 red bitmap, using SkPixmap::erase().
154     {
155         SkBitmap bm;
156         bm.allocPixels(SkImageInfo::Make(60,60, kRGBA_F16_SkColorType, kPremul_SkAlphaType, p3));
157 
158         // At the moment only SkPixmap has an erase() that takes an SkColor4f.
159         SkPixmap pm;
160         SkAssertResult(bm.peekPixels(&pm));
161         SkAssertResult(pm.erase({1,0,0,1} /*in p3*/));
162 
163         canvas->drawBitmap(bm, 10,10);
164         compare_pixel("drawBitmap P3 red, from SkPixmap::erase",
165                       canvas, 10,10,
166                       {1,0,0,1}, p3.get());
167     }
168 
169     canvas->translate(0,80);
170 
171     // Draw a P3 red bitmap wrapped in a shader, using SkPixmap::erase().
172     {
173         SkBitmap bm;
174         bm.allocPixels(SkImageInfo::Make(60,60, kRGBA_F16_SkColorType, kPremul_SkAlphaType, p3));
175 
176         // At the moment only SkPixmap has an erase() that takes an SkColor4f.
177         SkPixmap pm;
178         SkAssertResult(bm.peekPixels(&pm));
179         SkAssertResult(pm.erase({1,0,0,1} /*in p3*/));
180 
181         SkPaint paint;
182         paint.setShader(SkShader::MakeBitmapShader(bm, SkShader::kRepeat_TileMode,
183                                                    SkShader::kRepeat_TileMode));
184 
185         canvas->drawRect({10,10,70,70}, paint);
186         compare_pixel("drawBitmapAsShader P3 red, from SkPixmap::erase",
187                       canvas, 10,10,
188                       {1,0,0,1}, p3.get());
189     }
190 
191     canvas->translate(0,80);
192 
193     // Draw a gradient from P3 red to P3 green interpolating in unpremul P3, checking the corners.
194     {
195 
196         SkPoint points[] = {{10.5,10.5}, {69.5,69.5}};
197         SkColor4f colors[] = {{1,0,0,1}, {0,1,0,1}};
198 
199         SkPaint paint;
200         paint.setShader(SkGradientShader::MakeLinear(points, colors, p3,
201                                                      nullptr, SK_ARRAY_COUNT(colors),
202                                                      SkShader::kClamp_TileMode));
203         canvas->drawRect({10,10,70,70}, paint);
204         canvas->save();
205             compare_pixel("UPM P3 gradient, P3 red",
206                           canvas, 10,10,
207                           {1,0,0,1}, p3.get());
208 
209             canvas->translate(180, 0);
210 
211             compare_pixel("UPM P3 gradient, P3 green",
212                           canvas, 69,69,
213                           {0,1,0,1}, p3.get());
214         canvas->restore();
215     }
216 
217     canvas->translate(0,80);
218 
219     // Draw a gradient from P3 red to P3 green interpolating in premul P3, checking the corners.
220     {
221 
222         SkPoint points[] = {{10.5,10.5}, {69.5,69.5}};
223         SkColor4f colors[] = {{1,0,0,1}, {0,1,0,1}};
224 
225         SkPaint paint;
226         paint.setShader(
227                 SkGradientShader::MakeLinear(points, colors, p3,
228                                              nullptr, SK_ARRAY_COUNT(colors),
229                                              SkShader::kClamp_TileMode,
230                                              SkGradientShader::kInterpolateColorsInPremul_Flag,
231                                              nullptr/*local matrix*/));
232         canvas->drawRect({10,10,70,70}, paint);
233         canvas->save();
234             compare_pixel("PM P3 gradient, P3 red",
235                           canvas, 10,10,
236                           {1,0,0,1}, p3.get());
237 
238             canvas->translate(180, 0);
239 
240             compare_pixel("PM P3 gradient, P3 green",
241                           canvas, 69,69,
242                           {0,1,0,1}, p3.get());
243         canvas->restore();
244     }
245 
246     canvas->translate(0,80);
247 
248     // Draw a gradient from P3 red to P3 green interpolating in unpremul sRGB, checking the corners.
249     {
250 
251         SkPoint points[] = {{10.5,10.5}, {69.5,69.5}};
252         SkColor4f colors[] = {p3_to_srgb({1,0,0,1}), p3_to_srgb({0,1,0,1})};
253 
254         SkPaint paint;
255         paint.setShader(SkGradientShader::MakeLinear(points, colors, srgb,
256                                                      nullptr, SK_ARRAY_COUNT(colors),
257                                                      SkShader::kClamp_TileMode));
258         canvas->drawRect({10,10,70,70}, paint);
259         canvas->save();
260             compare_pixel("UPM sRGB gradient, P3 red",
261                           canvas, 10,10,
262                           {1,0,0,1}, p3.get());
263 
264             canvas->translate(180, 0);
265 
266             compare_pixel("UPM sRGB gradient, P3 green",
267                           canvas, 69,69,
268                           {0,1,0,1}, p3.get());
269         canvas->restore();
270     }
271 
272     canvas->translate(0,80);
273 
274     // Draw a gradient from P3 red to P3 green interpolating in premul sRGB, checking the corners.
275     {
276 
277         SkPoint points[] = {{10.5,10.5}, {69.5,69.5}};
278         SkColor4f colors[] = {p3_to_srgb({1,0,0,1}), p3_to_srgb({0,1,0,1})};
279 
280         SkPaint paint;
281         paint.setShader(
282                 SkGradientShader::MakeLinear(points, colors, srgb,
283                                              nullptr, SK_ARRAY_COUNT(colors),
284                                              SkShader::kClamp_TileMode,
285                                              SkGradientShader::kInterpolateColorsInPremul_Flag,
286                                              nullptr/*local matrix*/));
287         canvas->drawRect({10,10,70,70}, paint);
288         canvas->save();
289             compare_pixel("PM P3 gradient, P3 red",
290                           canvas, 10,10,
291                           {1,0,0,1}, p3.get());
292 
293             canvas->translate(180, 0);
294 
295             compare_pixel("PM P3 gradient, P3 green",
296                           canvas, 69,69,
297                           {0,1,0,1}, p3.get());
298         canvas->restore();
299     }
300 
301     canvas->translate(0,80);
302 
303     // Leon's blue -> green -> red gradient, interpolating in premul.
304     {
305         SkPoint points[] = {{10.5,10.5}, {10.5,69.5}};
306         SkColor4f colors[] = { {0,0,1,1}, {0,1,0,1}, {1,0,0,1} };
307 
308         SkPaint paint;
309         paint.setShader(
310                 SkGradientShader::MakeLinear(points, colors, p3,
311                                              nullptr, SK_ARRAY_COUNT(colors),
312                                              SkShader::kClamp_TileMode,
313                                              SkGradientShader::kInterpolateColorsInPremul_Flag,
314                                              nullptr/*local matrix*/));
315         canvas->drawRect({10,10,70,70}, paint);
316         canvas->save();
317             compare_pixel("Leon's gradient, P3 blue",
318                           canvas, 10,10,
319                           {0,0,1,1}, p3.get());
320 
321             canvas->translate(180, 0);
322 
323             compare_pixel("Leon's gradient, P3 red",
324                           canvas, 10,69,
325                           {1,0,0,1}, p3.get());
326         canvas->restore();
327     }
328 
329     canvas->translate(0,80);
330 
331     // Draw an A8 image with a P3 red, scaled and not, as a shader or bitmap.
332     {
333         uint8_t mask[256];
334         for (int i = 0; i < 256; i++) {
335             mask[i] = 255-i;
336         }
337         SkBitmap bm;
338         bm.installPixels(SkImageInfo::MakeA8(16,16), mask, 16);
339 
340         SkPaint as_bitmap;
341         as_bitmap.setColor4f({1,0,0,1}, p3.get());
342         as_bitmap.setFilterQuality(kLow_SkFilterQuality);
343 
344         SkPaint as_shader;
345         as_shader.setColor4f({1,0,0,1}, p3.get());
346         as_shader.setFilterQuality(kLow_SkFilterQuality);
347         as_shader.setShader(SkShader::MakeBitmapShader(bm, SkShader::kClamp_TileMode
348                                                          , SkShader::kClamp_TileMode));
349 
350         canvas->drawBitmap(bm, 10,10, &as_bitmap);
351         compare_pixel("A8 sprite bitmap P3 red",
352                       canvas, 10,10,
353                       {1,0,0,1}, p3.get());
354 
355         canvas->translate(0, 80);
356 
357         canvas->save();
358             canvas->translate(10,10);
359             canvas->drawRect({0,0,16,16}, as_shader);
360         canvas->restore();
361         compare_pixel("A8 sprite shader P3 red",
362                       canvas, 10,10,
363                       {1,0,0,1}, p3.get());
364 
365         canvas->translate(0,80);
366 
367         canvas->drawBitmapRect(bm, {10,10,70,70}, &as_bitmap);
368         compare_pixel("A8 scaled bitmap P3 red",
369                       canvas, 10,10,
370                       {1,0,0,1}, p3.get());
371 
372         canvas->translate(0,80);
373 
374         canvas->save();
375             canvas->translate(10,10);
376             canvas->scale(3.75,3.75);
377             canvas->drawRect({0,0,16,16}, as_shader);
378         canvas->restore();
379         compare_pixel("A8 scaled shader P3 red",
380                       canvas, 10,10,
381                       {1,0,0,1}, p3.get());
382     }
383 
384     // TODO: draw P3 colors more ways
385 }
386 
387 DEF_SIMPLE_GM(p3_ovals, canvas, 450, 320) {
388     auto p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
389 
390     // Test cases that exercise each Op in GrOvalOpFactory.cpp
391 
392     // Draw a circle and check the center (CircleOp)
393     {
394         SkPaint paint;
395         paint.setAntiAlias(true);
396         paint.setColor4f({ 1,0,0,1 }, p3.get());
397 
398         canvas->drawCircle(40, 40, 30, paint);
399         compare_pixel("drawCircle P3 red ",
400                       canvas, 40, 40,
401                       { 1,0,0,1 }, p3.get());
402     }
403 
404     canvas->translate(0, 80);
405 
406     // Draw an oval and check the center (EllipseOp)
407     {
408         SkPaint paint;
409         paint.setAntiAlias(true);
410         paint.setColor4f({ 1,0,0,1 }, p3.get());
411 
412         canvas->drawOval({ 20,10,60,70 }, paint);
413         compare_pixel("drawOval P3 red ",
414                       canvas, 40, 40,
415                       { 1,0,0,1 }, p3.get());
416     }
417 
418     canvas->translate(0, 80);
419 
420     // Draw a butt-capped dashed circle and check the top of the stroke (ButtCappedDashedCircleOp)
421     {
422         SkPaint paint;
423         paint.setAntiAlias(true);
424         paint.setColor4f({ 1,0,0,1 }, p3.get());
425         paint.setStyle(SkPaint::kStroke_Style);
426         float intervals[] = { 70, 10 };
427         paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
428         paint.setStrokeWidth(10);
429 
430         canvas->drawCircle(40, 40, 30, paint);
431         compare_pixel("drawDashedCircle P3 red ",
432                       canvas, 40, 10,
433                       { 1,0,0,1 }, p3.get());
434     }
435 
436     canvas->translate(0, 80);
437 
438     // Draw an oval with rotation and check the center (DIEllipseOp)
439     {
440         SkPaint paint;
441         paint.setAntiAlias(true);
442         paint.setColor4f({ 1,0,0,1 }, p3.get());
443 
444         canvas->save();
445             canvas->translate(40, 40);
446             canvas->rotate(45);
447             canvas->drawOval({ -20,-30,20,30 }, paint);
448         canvas->restore();
449         compare_pixel("drawRotatedOval P3 red ",
450                       canvas, 40, 40,
451                       { 1,0,0,1 }, p3.get());
452     }
453 
454     canvas->translate(0, 80);
455 }
456