• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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