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