xref: /aosp_15_r20/external/skia/gm/colorspace.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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