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