• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 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 <cmath>
9 #include "include/core/SkAlphaType.h"
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkColorType.h"
15 #include "include/core/SkImage.h"
16 #include "include/core/SkImageInfo.h"
17 #include "include/core/SkPaint.h"
18 #include "include/core/SkPixmap.h"
19 #include "include/core/SkRect.h"
20 #include "include/core/SkRefCnt.h"
21 #include "include/core/SkSamplingOptions.h"
22 #include "include/core/SkShader.h"
23 #include "include/private/SkGainmapInfo.h"
24 #include "include/private/SkGainmapShader.h"
25 #include "tests/Test.h"
26 
approx_equal(const SkColor4f & a,const SkColor4f & b)27 static bool approx_equal(const SkColor4f& a, const SkColor4f& b) {
28     constexpr float kEpsilon = 1e-3f;
29     return std::abs(a.fR - b.fR) < kEpsilon && std::abs(a.fG - b.fG) < kEpsilon &&
30            std::abs(a.fB - b.fB) < kEpsilon && std::abs(a.fA - b.fA) < kEpsilon;
31 }
32 
33 // Create a 1x1 image with a specified color.
make_1x1_image(sk_sp<SkColorSpace> imageColorSpace,SkAlphaType imageAlphaType,SkColor4f imageColor,sk_sp<SkColorSpace> imageColorColorSpace=SkColorSpace::MakeSRGBLinear ())34 static sk_sp<SkImage> make_1x1_image(
35         sk_sp<SkColorSpace> imageColorSpace,
36         SkAlphaType imageAlphaType,
37         SkColor4f imageColor,
38         sk_sp<SkColorSpace> imageColorColorSpace = SkColorSpace::MakeSRGBLinear()) {
39     SkImageInfo bmInfo =
40             SkImageInfo::Make(1, 1, kRGBA_F32_SkColorType, imageAlphaType, imageColorSpace);
41     SkBitmap bm;
42     bm.allocPixels(bmInfo);
43 
44     SkImageInfo writePixelsInfo = SkImageInfo::Make(
45             1, 1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType, imageColorColorSpace);
46     SkPixmap writePixelsPixmap(writePixelsInfo, &imageColor, writePixelsInfo.minRowBytes());
47     bm.writePixels(writePixelsPixmap, 0, 0);
48     return SkImage::MakeFromBitmap(bm);
49 }
50 
51 // Return gainmap info that will scale 1 up to the specified hdrRatioMax.
simple_gainmap_info(float hdrRatioMax)52 static SkGainmapInfo simple_gainmap_info(float hdrRatioMax) {
53     SkGainmapInfo gainmapInfo;
54     gainmapInfo.fDisplayRatioSdr = 1.f;
55     gainmapInfo.fDisplayRatioHdr = hdrRatioMax;
56     gainmapInfo.fEpsilonSdr = {0.f, 0.f, 0.f, 1.f};
57     gainmapInfo.fEpsilonHdr = {0.f, 0.f, 0.f, 1.f};
58     gainmapInfo.fGainmapRatioMin = {1.f, 1.f, 1.f, 1.f};
59     gainmapInfo.fGainmapRatioMax = {hdrRatioMax, hdrRatioMax, hdrRatioMax, 1.f};
60     return gainmapInfo;
61 }
62 
63 // Draw using a gainmap to a canvas with the specified HDR to SDR ratio and the specified color
64 // space. Return the result as unpremultiplied sRGB linear.
draw_1x1_gainmap(sk_sp<SkImage> baseImage,sk_sp<SkImage> gainmapImage,const SkGainmapInfo & gainmapInfo,float dstRatio,sk_sp<SkColorSpace> dstColorSpace=SkColorSpace::MakeSRGB ())65 static SkColor4f draw_1x1_gainmap(sk_sp<SkImage> baseImage,
66                                   sk_sp<SkImage> gainmapImage,
67                                   const SkGainmapInfo& gainmapInfo,
68                                   float dstRatio,
69                                   sk_sp<SkColorSpace> dstColorSpace = SkColorSpace::MakeSRGB()) {
70     constexpr auto kRect = SkRect::MakeWH(1.f, 1.f);
71     SkImageInfo canvasInfo =
72             SkImageInfo::Make(1, 1, kRGBA_F32_SkColorType, kPremul_SkAlphaType, dstColorSpace);
73     SkBitmap canvasBitmap;
74     canvasBitmap.allocPixels(canvasInfo);
75     canvasBitmap.eraseColor(SK_ColorTRANSPARENT);
76 
77     sk_sp<SkShader> shader = SkGainmapShader::Make(baseImage,
78                                                    kRect,
79                                                    SkSamplingOptions(),
80                                                    gainmapImage,
81                                                    kRect,
82                                                    SkSamplingOptions(),
83                                                    gainmapInfo,
84                                                    kRect,
85                                                    dstRatio,
86                                                    dstColorSpace);
87     SkPaint paint;
88     paint.setShader(shader);
89     SkCanvas canvas(canvasBitmap);
90     canvas.drawRect(kRect, paint);
91 
92     SkColor4f result = {0.f, 0.f, 0.f, 0.f};
93     SkImageInfo readPixelsInfo = SkImageInfo::Make(
94             1, 1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType, SkColorSpace::MakeSRGBLinear());
95     canvas.readPixels(readPixelsInfo, &result, sizeof(result), 0, 0);
96     return result;
97 }
98 
99 // Verify that the gainmap shader correctly applies the base, gainmap, and destination rectangles.
DEF_TEST(GainmapShader_rects,r)100 DEF_TEST(GainmapShader_rects, r) {
101     SkColor4f sdrColors[5][2] = {
102             {{-1.f, -1.f, -1.f, 1.0f}, {-1.f, -1.f, -1.f, 1.0f}},
103             {{1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 0.5f, 1.0f}},
104             {{1.0f, 0.5f, 1.0f, 1.0f}, {1.0f, 0.5f, 0.5f, 1.0f}},
105             {{0.5f, 1.0f, 1.0f, 1.0f}, {0.5f, 1.0f, 0.5f, 1.0f}},
106             {{0.5f, 0.5f, 1.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}},
107     };
108     SkPixmap sdrPixmap(SkImageInfo::Make(2, 5, kRGBA_F32_SkColorType, kOpaque_SkAlphaType),
109                        sdrColors,
110                        2 * sizeof(SkColor4f));
111     auto sdrImage = SkImage::MakeFromRaster(sdrPixmap, nullptr, nullptr);
112     const auto sdrImageRect = SkRect::MakeXYWH(0.f, 1.f, 2.f, 4.f);
113 
114     // The top pixel indicates to gain only red, and the bottom pixel indicates to gain everything
115     // except red.
116     SkColor4f gainmapColors[2][2] = {
117             {{-1.f, -1.f, -1.f, 1.f}, {1.0f, 0.0f, 0.0f, 1.f}},
118             {{-1.f, -1.f, -1.f, 1.f}, {0.0f, 1.0f, 1.0f, 1.f}},
119     };
120     SkPixmap gainmapPixmap(SkImageInfo::Make(2, 2, kRGBA_F32_SkColorType, kOpaque_SkAlphaType),
121                            gainmapColors,
122                            2 * sizeof(SkColor4f));
123     auto gainmapImage = SkImage::MakeFromRaster(gainmapPixmap, nullptr, nullptr);
124     const auto gainmapImageRect = SkRect::MakeXYWH(1.f, 0.f, 1.f, 2.f);
125     const SkGainmapInfo gainmapInfo = simple_gainmap_info(2.f);
126 
127     SkImageInfo canvasInfo = SkImageInfo::Make(
128             4, 6, kRGBA_F32_SkColorType, kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
129     SkBitmap canvasBitmap;
130     canvasBitmap.allocPixels(canvasInfo);
131     canvasBitmap.eraseColor(SK_ColorTRANSPARENT);
132     const auto canvasRect = SkRect::MakeXYWH(1.f, 1.f, 2.f, 4.f);
133 
134     sk_sp<SkShader> shader = SkGainmapShader::Make(sdrImage,
135                                                    sdrImageRect,
136                                                    SkSamplingOptions(),
137                                                    gainmapImage,
138                                                    gainmapImageRect,
139                                                    SkSamplingOptions(),
140                                                    gainmapInfo,
141                                                    canvasRect,
142                                                    gainmapInfo.fDisplayRatioHdr,
143                                                    canvasInfo.refColorSpace());
144     SkPaint paint;
145     paint.setShader(shader);
146     SkCanvas canvas(canvasBitmap);
147     canvas.drawRect(canvasRect, paint);
148 
149     // Compute and compare the expected colors.
150     constexpr float k10G = 1.353256028586302f;   // This is linearToSRGB(2.0).
151     constexpr float k05G = 0.6858361015012847f;  // This is linearToSRGB(srgbToLinear(0.5)*2.0)
152     SkColor4f expectedColors[4][2] = {
153             {{k10G, 1.0f, 1.0f, 1.0f}, {k10G, 1.0f, 0.5f, 1.0f}},
154             {{k10G, 0.5f, 1.0f, 1.0f}, {k10G, 0.5f, 0.5f, 1.0f}},
155             {{0.5f, k10G, k10G, 1.0f}, {0.5f, k10G, k05G, 1.0f}},
156             {{0.5f, k05G, k10G, 1.0f}, {0.5f, k05G, k05G, 1.0f}},
157     };
158     for (int y = 0; y < 4; ++y) {
159         for (int x = 0; x < 2; ++x) {
160             auto color = canvasBitmap.getColor4f(x + 1, y + 1);
161             REPORTER_ASSERT(r, approx_equal(color, expectedColors[y][x]));
162         }
163     }
164 }
165 
166 // Verify that the gainmap shader isn't affected by the color spaces of the base, gainmap, or
167 // destination.
DEF_TEST(GainmapShader_colorSpace,r)168 DEF_TEST(GainmapShader_colorSpace, r) {
169     auto sdrColorSpace =
170             SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kSRGB)->makeColorSpin();
171     auto gainmapColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, SkNamedGamut::kRec2020);
172     auto dstColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, SkNamedGamut::kDisplayP3);
173 
174     constexpr SkColor4f kSdrColor = {0.25f, 0.5f, 1.f, 1.f};
175     constexpr SkColor4f kGainmapColor = {
176             0.0f,  // The sRGB G channel will have a exp2(0.0)=1.000 gain.
177             0.5f,  // The sRGB B channel will have a exp2(0.5)=0.707 gain.
178             1.0f,  // The sRGB R channel will have a exp2(1.0)=2.000 gain.
179             1.f};
180     constexpr SkColor4f kExpectedColor = {0.5f, 0.5f, 1.414f, 1.f};
181 
182     auto sdrImage = make_1x1_image(sdrColorSpace, kOpaque_SkAlphaType, kSdrColor);
183     auto gainmapImage = make_1x1_image(
184             gainmapColorSpace, kOpaque_SkAlphaType, kGainmapColor, gainmapColorSpace);
185     SkGainmapInfo gainmapInfo = simple_gainmap_info(2.f);
186 
187     auto color = draw_1x1_gainmap(
188             sdrImage, gainmapImage, gainmapInfo, gainmapInfo.fDisplayRatioHdr, dstColorSpace);
189     REPORTER_ASSERT(r, approx_equal(color, kExpectedColor));
190 }
191