1 /*
2 * Copyright 2021 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/SkCanvas.h"
10 #include "include/core/SkFont.h"
11 #include "include/core/SkSurface.h"
12 #include "tools/Resources.h"
13
14 static const skcms_TransferFunction gTFs[] = {
15 SkNamedTransferFn::kSRGB,
16 SkNamedTransferFn::k2Dot2,
17 SkNamedTransferFn::kLinear,
18 SkNamedTransferFn::kRec2020,
19 SkNamedTransferFn::kPQ,
20 SkNamedTransferFn::kHLG,
21 {-3.0f, 2.0f, 2.0f, 1/0.17883277f, 0.28466892f, 0.55991073f, 3.0f }, // HLG scaled 4x
22 };
23
24 static const skcms_Matrix3x3 gGamuts[] = {
25 SkNamedGamut::kSRGB,
26 SkNamedGamut::kAdobeRGB,
27 SkNamedGamut::kDisplayP3,
28 SkNamedGamut::kRec2020,
29 SkNamedGamut::kXYZ,
30 };
31
32 static const int W = 128,
33 H = 128;
34
35 // These GMs demonstrate that our color space management is self-consistent.
36 // (Important to note, self-consistent, not necessarily correct in an objective sense.)
37 //
38 // Let's let,
39 //
40 // SkColorSpace* imgCS = img->colorSpace();
41 // SkColorSpace* dstCS = canvas->imageInfo().colorSpace();
42 //
43 // Ordinarily we'd just
44 //
45 // canvas->drawImage(img, 0,0);
46 //
47 // which would convert that img's pixels from imgCS to dstCS while drawing.
48 //
49 // But before we draw in these GMs, we convert the image to an arbitrarily different color space,
50 // letting midCS range over the cross-product gTFs × gGamuts:
51 //
52 // canvas->drawImage(img->makeColorSpace(midCS), 0,0);
53 //
54 // This converts img first from imgCS to midCS, treating midCS as a destination color space,
55 // and then draws that midCS image to the dstCS canvas, treating midCS as a source color space.
56 // This should draw a grid of images that look identical except for small precision loss.
57 //
58 // If instead of calling SkImage::makeColorSpace() we use SkCanvas::makeSurface() to create a
59 // midCS offscreen, we construct the same logical imgCS -> midCS -> dstCS transform chain while
60 // exercising different drawing code paths. Both strategies should draw roughly the same.
61
62 namespace {
63 enum Strategy { SkImage_makeColorSpace, SkCanvas_makeSurface };
64 }
65
draw_colorspace_gm(Strategy strategy,SkCanvas * canvas)66 static void draw_colorspace_gm(Strategy strategy, SkCanvas* canvas) {
67 if (!canvas->imageInfo().colorSpace()) {
68 canvas->drawString("This GM only makes sense with color-managed drawing.",
69 W,H, SkFont{}, SkPaint{});
70 return;
71 }
72
73 sk_sp<SkImage> img = GetResourceAsImage("images/mandrill_128.png");
74 if (!img) {
75 canvas->drawString("Could not load our test image!",
76 W,H, SkFont{}, SkPaint{});
77 return;
78 }
79
80 SkASSERT(img->width() == W);
81 SkASSERT(img->height() == H);
82 SkASSERT(img->colorSpace());
83
84 for (skcms_Matrix3x3 gamut : gGamuts) {
85 canvas->save();
86 for (skcms_TransferFunction tf : gTFs) {
87 sk_sp<SkColorSpace> midCS = SkColorSpace::MakeRGB(tf, gamut);
88
89 switch (strategy) {
90 case SkImage_makeColorSpace: {
91 canvas->drawImage(img->makeColorSpace(midCS), 0,0);
92 } break;
93
94 case SkCanvas_makeSurface: {
95 sk_sp<SkSurface> offscreen =
96 canvas->makeSurface(canvas->imageInfo().makeColorSpace(midCS));
97 if (!offscreen) {
98 canvas->drawString("Could not allocate offscreen surface!",
99 W,H, SkFont{}, SkPaint{});
100 return;
101 }
102 offscreen->getCanvas()->drawImage(img, 0,0);
103 canvas->drawImage(offscreen->makeImageSnapshot(), 0,0);
104 } break;
105 }
106
107 canvas->translate(W, 0);
108 }
109 canvas->restore();
110 canvas->translate(0, H);
111 }
112 }
113
DEF_SIMPLE_GM(colorspace,canvas,W * SK_ARRAY_COUNT (gTFs),H * SK_ARRAY_COUNT (gGamuts))114 DEF_SIMPLE_GM(colorspace, canvas, W*SK_ARRAY_COUNT(gTFs), H*SK_ARRAY_COUNT(gGamuts)) {
115 draw_colorspace_gm(SkImage_makeColorSpace, canvas);
116 }
117
DEF_SIMPLE_GM(colorspace2,canvas,W * SK_ARRAY_COUNT (gTFs),H * SK_ARRAY_COUNT (gGamuts))118 DEF_SIMPLE_GM(colorspace2, canvas, W*SK_ARRAY_COUNT(gTFs), H*SK_ARRAY_COUNT(gGamuts)) {
119 draw_colorspace_gm(SkCanvas_makeSurface, canvas);
120 }
121