1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2023 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorType.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixmap.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSamplingOptions.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkShader.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkGainmapInfo.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkGainmapShader.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
26*c8dee2aaSAndroid Build Coastguard Worker
approx_equal(const SkColor4f & a,const SkColor4f & b)27*c8dee2aaSAndroid Build Coastguard Worker static bool approx_equal(const SkColor4f& a, const SkColor4f& b) {
28*c8dee2aaSAndroid Build Coastguard Worker constexpr float kEpsilon = 1e-3f;
29*c8dee2aaSAndroid Build Coastguard Worker return std::abs(a.fR - b.fR) < kEpsilon && std::abs(a.fG - b.fG) < kEpsilon &&
30*c8dee2aaSAndroid Build Coastguard Worker std::abs(a.fB - b.fB) < kEpsilon && std::abs(a.fA - b.fA) < kEpsilon;
31*c8dee2aaSAndroid Build Coastguard Worker }
32*c8dee2aaSAndroid Build Coastguard Worker
33*c8dee2aaSAndroid Build Coastguard Worker // 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*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> make_1x1_image(
35*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkColorSpace> imageColorSpace,
36*c8dee2aaSAndroid Build Coastguard Worker SkAlphaType imageAlphaType,
37*c8dee2aaSAndroid Build Coastguard Worker SkColor4f imageColor,
38*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkColorSpace> imageColorColorSpace = SkColorSpace::MakeSRGBLinear()) {
39*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo bmInfo =
40*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo::Make(1, 1, kRGBA_F32_SkColorType, imageAlphaType, imageColorSpace);
41*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bm;
42*c8dee2aaSAndroid Build Coastguard Worker bm.allocPixels(bmInfo);
43*c8dee2aaSAndroid Build Coastguard Worker
44*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo writePixelsInfo = SkImageInfo::Make(
45*c8dee2aaSAndroid Build Coastguard Worker 1, 1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType, imageColorColorSpace);
46*c8dee2aaSAndroid Build Coastguard Worker SkPixmap writePixelsPixmap(writePixelsInfo, &imageColor, writePixelsInfo.minRowBytes());
47*c8dee2aaSAndroid Build Coastguard Worker bm.writePixels(writePixelsPixmap, 0, 0);
48*c8dee2aaSAndroid Build Coastguard Worker return SkImages::RasterFromBitmap(bm);
49*c8dee2aaSAndroid Build Coastguard Worker }
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker // Return gainmap info that will scale 1 up to the specified hdrRatioMax.
simple_gainmap_info(float hdrRatioMax)52*c8dee2aaSAndroid Build Coastguard Worker static SkGainmapInfo simple_gainmap_info(float hdrRatioMax) {
53*c8dee2aaSAndroid Build Coastguard Worker SkGainmapInfo gainmapInfo;
54*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fDisplayRatioSdr = 1.f;
55*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fDisplayRatioHdr = hdrRatioMax;
56*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fEpsilonSdr = {0.f, 0.f, 0.f, 1.f};
57*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fEpsilonHdr = {0.f, 0.f, 0.f, 1.f};
58*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fGainmapRatioMin = {1.f, 1.f, 1.f, 1.f};
59*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fGainmapRatioMax = {hdrRatioMax, hdrRatioMax, hdrRatioMax, 1.f};
60*c8dee2aaSAndroid Build Coastguard Worker return gainmapInfo;
61*c8dee2aaSAndroid Build Coastguard Worker }
62*c8dee2aaSAndroid Build Coastguard Worker
63*c8dee2aaSAndroid Build Coastguard Worker // Draw using a gainmap to a canvas with the specified HDR to SDR ratio and the specified color
64*c8dee2aaSAndroid Build Coastguard Worker // 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*c8dee2aaSAndroid Build Coastguard Worker static SkColor4f draw_1x1_gainmap(sk_sp<SkImage> baseImage,
66*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> gainmapImage,
67*c8dee2aaSAndroid Build Coastguard Worker const SkGainmapInfo& gainmapInfo,
68*c8dee2aaSAndroid Build Coastguard Worker float dstRatio,
69*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkColorSpace> dstColorSpace = SkColorSpace::MakeSRGB()) {
70*c8dee2aaSAndroid Build Coastguard Worker constexpr auto kRect = SkRect::MakeWH(1.f, 1.f);
71*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo canvasInfo =
72*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo::Make(1, 1, kRGBA_F32_SkColorType, kPremul_SkAlphaType, dstColorSpace);
73*c8dee2aaSAndroid Build Coastguard Worker SkBitmap canvasBitmap;
74*c8dee2aaSAndroid Build Coastguard Worker canvasBitmap.allocPixels(canvasInfo);
75*c8dee2aaSAndroid Build Coastguard Worker canvasBitmap.eraseColor(SK_ColorTRANSPARENT);
76*c8dee2aaSAndroid Build Coastguard Worker
77*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> shader = SkGainmapShader::Make(baseImage,
78*c8dee2aaSAndroid Build Coastguard Worker kRect,
79*c8dee2aaSAndroid Build Coastguard Worker SkSamplingOptions(),
80*c8dee2aaSAndroid Build Coastguard Worker gainmapImage,
81*c8dee2aaSAndroid Build Coastguard Worker kRect,
82*c8dee2aaSAndroid Build Coastguard Worker SkSamplingOptions(),
83*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo,
84*c8dee2aaSAndroid Build Coastguard Worker kRect,
85*c8dee2aaSAndroid Build Coastguard Worker dstRatio,
86*c8dee2aaSAndroid Build Coastguard Worker dstColorSpace);
87*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
88*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(shader);
89*c8dee2aaSAndroid Build Coastguard Worker SkCanvas canvas(canvasBitmap);
90*c8dee2aaSAndroid Build Coastguard Worker canvas.drawRect(kRect, paint);
91*c8dee2aaSAndroid Build Coastguard Worker
92*c8dee2aaSAndroid Build Coastguard Worker SkColor4f result = {0.f, 0.f, 0.f, 0.f};
93*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo readPixelsInfo = SkImageInfo::Make(
94*c8dee2aaSAndroid Build Coastguard Worker 1, 1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType, SkColorSpace::MakeSRGBLinear());
95*c8dee2aaSAndroid Build Coastguard Worker canvas.readPixels(readPixelsInfo, &result, sizeof(result), 0, 0);
96*c8dee2aaSAndroid Build Coastguard Worker return result;
97*c8dee2aaSAndroid Build Coastguard Worker }
98*c8dee2aaSAndroid Build Coastguard Worker
99*c8dee2aaSAndroid Build Coastguard Worker // Verify that the gainmap shader correctly applies the base, gainmap, and destination rectangles.
DEF_TEST(GainmapShader_rects,r)100*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(GainmapShader_rects, r) {
101*c8dee2aaSAndroid Build Coastguard Worker SkColor4f sdrColors[5][2] = {
102*c8dee2aaSAndroid Build Coastguard Worker {{-1.f, -1.f, -1.f, 1.0f}, {-1.f, -1.f, -1.f, 1.0f}},
103*c8dee2aaSAndroid Build Coastguard Worker {{1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 0.5f, 1.0f}},
104*c8dee2aaSAndroid Build Coastguard Worker {{1.0f, 0.5f, 1.0f, 1.0f}, {1.0f, 0.5f, 0.5f, 1.0f}},
105*c8dee2aaSAndroid Build Coastguard Worker {{0.5f, 1.0f, 1.0f, 1.0f}, {0.5f, 1.0f, 0.5f, 1.0f}},
106*c8dee2aaSAndroid Build Coastguard Worker {{0.5f, 0.5f, 1.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}},
107*c8dee2aaSAndroid Build Coastguard Worker };
108*c8dee2aaSAndroid Build Coastguard Worker SkPixmap sdrPixmap(SkImageInfo::Make(2, 5, kRGBA_F32_SkColorType, kOpaque_SkAlphaType),
109*c8dee2aaSAndroid Build Coastguard Worker sdrColors,
110*c8dee2aaSAndroid Build Coastguard Worker 2 * sizeof(SkColor4f));
111*c8dee2aaSAndroid Build Coastguard Worker auto sdrImage = SkImages::RasterFromPixmap(sdrPixmap, nullptr, nullptr);
112*c8dee2aaSAndroid Build Coastguard Worker const auto sdrImageRect = SkRect::MakeXYWH(0.f, 1.f, 2.f, 4.f);
113*c8dee2aaSAndroid Build Coastguard Worker
114*c8dee2aaSAndroid Build Coastguard Worker // The top pixel indicates to gain only red, and the bottom pixel indicates to gain everything
115*c8dee2aaSAndroid Build Coastguard Worker // except red.
116*c8dee2aaSAndroid Build Coastguard Worker SkColor4f gainmapColors[2][2] = {
117*c8dee2aaSAndroid Build Coastguard Worker {{-1.f, -1.f, -1.f, 1.f}, {1.0f, 0.0f, 0.0f, 1.f}},
118*c8dee2aaSAndroid Build Coastguard Worker {{-1.f, -1.f, -1.f, 1.f}, {0.0f, 1.0f, 1.0f, 1.f}},
119*c8dee2aaSAndroid Build Coastguard Worker };
120*c8dee2aaSAndroid Build Coastguard Worker SkPixmap gainmapPixmap(SkImageInfo::Make(2, 2, kRGBA_F32_SkColorType, kOpaque_SkAlphaType),
121*c8dee2aaSAndroid Build Coastguard Worker gainmapColors,
122*c8dee2aaSAndroid Build Coastguard Worker 2 * sizeof(SkColor4f));
123*c8dee2aaSAndroid Build Coastguard Worker auto gainmapImage = SkImages::RasterFromPixmap(gainmapPixmap, nullptr, nullptr);
124*c8dee2aaSAndroid Build Coastguard Worker const auto gainmapImageRect = SkRect::MakeXYWH(1.f, 0.f, 1.f, 2.f);
125*c8dee2aaSAndroid Build Coastguard Worker SkGainmapInfo gainmapInfo = simple_gainmap_info(2.f);
126*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fEpsilonHdr[0] = 0.1f;
127*c8dee2aaSAndroid Build Coastguard Worker
128*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo canvasInfo = SkImageInfo::Make(
129*c8dee2aaSAndroid Build Coastguard Worker 4, 6, kRGBA_F32_SkColorType, kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
130*c8dee2aaSAndroid Build Coastguard Worker SkBitmap canvasBitmap;
131*c8dee2aaSAndroid Build Coastguard Worker canvasBitmap.allocPixels(canvasInfo);
132*c8dee2aaSAndroid Build Coastguard Worker canvasBitmap.eraseColor(SK_ColorTRANSPARENT);
133*c8dee2aaSAndroid Build Coastguard Worker const auto canvasRect = SkRect::MakeXYWH(1.f, 1.f, 2.f, 4.f);
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> shader = SkGainmapShader::Make(sdrImage,
136*c8dee2aaSAndroid Build Coastguard Worker sdrImageRect,
137*c8dee2aaSAndroid Build Coastguard Worker SkSamplingOptions(),
138*c8dee2aaSAndroid Build Coastguard Worker gainmapImage,
139*c8dee2aaSAndroid Build Coastguard Worker gainmapImageRect,
140*c8dee2aaSAndroid Build Coastguard Worker SkSamplingOptions(),
141*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo,
142*c8dee2aaSAndroid Build Coastguard Worker canvasRect,
143*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fDisplayRatioHdr,
144*c8dee2aaSAndroid Build Coastguard Worker canvasInfo.refColorSpace());
145*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
146*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(shader);
147*c8dee2aaSAndroid Build Coastguard Worker SkCanvas canvas(canvasBitmap);
148*c8dee2aaSAndroid Build Coastguard Worker canvas.drawRect(canvasRect, paint);
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker // Compute and compare the expected colors.
151*c8dee2aaSAndroid Build Coastguard Worker // This is linearToSRGB(srgbToLinear(1.0)*2.0) = linearToSRGB(2.0).
152*c8dee2aaSAndroid Build Coastguard Worker constexpr float k10G = 1.353256028586302f;
153*c8dee2aaSAndroid Build Coastguard Worker // This is linearToSRGB(srgbToLinear(0.5)*2.0)
154*c8dee2aaSAndroid Build Coastguard Worker constexpr float k05G = 0.6858361015012847f;
155*c8dee2aaSAndroid Build Coastguard Worker // The 'R' component also has a fEpsilonHdr set.
156*c8dee2aaSAndroid Build Coastguard Worker // This is linearToSRGB(srgbToLinear(1.0)*2.0-0.1) = linearToSRGB(1.9).
157*c8dee2aaSAndroid Build Coastguard Worker constexpr float kR10G = 1.3234778541409058f;
158*c8dee2aaSAndroid Build Coastguard Worker // This is linearToSRGB(srgbToLinear(0.5)-0.1)
159*c8dee2aaSAndroid Build Coastguard Worker // The gain map is 0.f (no gain), but there are still affectd by the offset.
160*c8dee2aaSAndroid Build Coastguard Worker constexpr float kR05G = 0.371934685412575f;
161*c8dee2aaSAndroid Build Coastguard Worker SkColor4f expectedColors[4][2] = {
162*c8dee2aaSAndroid Build Coastguard Worker {{kR10G, 1.0f, 1.0f, 1.0f}, {kR10G, 1.0f, 0.5f, 1.0f}},
163*c8dee2aaSAndroid Build Coastguard Worker {{kR10G, 0.5f, 1.0f, 1.0f}, {kR10G, 0.5f, 0.5f, 1.0f}},
164*c8dee2aaSAndroid Build Coastguard Worker {{kR05G, k10G, k10G, 1.0f}, {kR05G, k10G, k05G, 1.0f}},
165*c8dee2aaSAndroid Build Coastguard Worker {{kR05G, k05G, k10G, 1.0f}, {kR05G, k05G, k05G, 1.0f}},
166*c8dee2aaSAndroid Build Coastguard Worker };
167*c8dee2aaSAndroid Build Coastguard Worker for (int y = 0; y < 4; ++y) {
168*c8dee2aaSAndroid Build Coastguard Worker for (int x = 0; x < 2; ++x) {
169*c8dee2aaSAndroid Build Coastguard Worker const auto color = canvasBitmap.getColor4f(x + 1, y + 1);
170*c8dee2aaSAndroid Build Coastguard Worker const auto& expected = expectedColors[y][x];
171*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r,
172*c8dee2aaSAndroid Build Coastguard Worker approx_equal(color, expected),
173*c8dee2aaSAndroid Build Coastguard Worker "color (%.3f %.3f %.3f %.3f) does not match expected color (%.3f %.3f "
174*c8dee2aaSAndroid Build Coastguard Worker "%.3f %.3f) at "
175*c8dee2aaSAndroid Build Coastguard Worker "pixel (%d, %d)",
176*c8dee2aaSAndroid Build Coastguard Worker color.fR,
177*c8dee2aaSAndroid Build Coastguard Worker color.fG,
178*c8dee2aaSAndroid Build Coastguard Worker color.fB,
179*c8dee2aaSAndroid Build Coastguard Worker color.fA,
180*c8dee2aaSAndroid Build Coastguard Worker expected.fR,
181*c8dee2aaSAndroid Build Coastguard Worker expected.fG,
182*c8dee2aaSAndroid Build Coastguard Worker expected.fB,
183*c8dee2aaSAndroid Build Coastguard Worker expected.fA,
184*c8dee2aaSAndroid Build Coastguard Worker x,
185*c8dee2aaSAndroid Build Coastguard Worker y);
186*c8dee2aaSAndroid Build Coastguard Worker }
187*c8dee2aaSAndroid Build Coastguard Worker }
188*c8dee2aaSAndroid Build Coastguard Worker }
189*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(GainmapShader_baseImageIsHdr,r)190*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(GainmapShader_baseImageIsHdr, r) {
191*c8dee2aaSAndroid Build Coastguard Worker SkColor4f hdrColors[4][2] = {
192*c8dee2aaSAndroid Build Coastguard Worker {{1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 0.5f, 1.0f}},
193*c8dee2aaSAndroid Build Coastguard Worker {{1.0f, 0.5f, 1.0f, 1.0f}, {1.0f, 0.5f, 0.5f, 1.0f}},
194*c8dee2aaSAndroid Build Coastguard Worker {{0.5f, 1.0f, 1.0f, 1.0f}, {0.5f, 1.0f, 0.5f, 1.0f}},
195*c8dee2aaSAndroid Build Coastguard Worker {{0.5f, 0.5f, 1.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}},
196*c8dee2aaSAndroid Build Coastguard Worker };
197*c8dee2aaSAndroid Build Coastguard Worker SkPixmap hdrPixmap(SkImageInfo::Make(2, 4, kRGBA_F32_SkColorType, kOpaque_SkAlphaType),
198*c8dee2aaSAndroid Build Coastguard Worker hdrColors,
199*c8dee2aaSAndroid Build Coastguard Worker 2 * sizeof(SkColor4f));
200*c8dee2aaSAndroid Build Coastguard Worker auto hdrImage = SkImages::RasterFromPixmap(hdrPixmap, nullptr, nullptr);
201*c8dee2aaSAndroid Build Coastguard Worker const auto hdrImageRect = SkRect::MakeXYWH(0.f, 0.f, 2.f, 4.f);
202*c8dee2aaSAndroid Build Coastguard Worker
203*c8dee2aaSAndroid Build Coastguard Worker // The top pixel indicates to gain only red, and the bottom pixel indicates to gain everything
204*c8dee2aaSAndroid Build Coastguard Worker // except red.
205*c8dee2aaSAndroid Build Coastguard Worker SkColor4f gainmapColors[2][1] = {
206*c8dee2aaSAndroid Build Coastguard Worker {{1.0f, 0.0f, 0.0f, 1.f}},
207*c8dee2aaSAndroid Build Coastguard Worker {{0.0f, 1.0f, 1.0f, 1.f}},
208*c8dee2aaSAndroid Build Coastguard Worker };
209*c8dee2aaSAndroid Build Coastguard Worker SkPixmap gainmapPixmap(SkImageInfo::Make(1, 2, kRGBA_F32_SkColorType, kOpaque_SkAlphaType),
210*c8dee2aaSAndroid Build Coastguard Worker gainmapColors,
211*c8dee2aaSAndroid Build Coastguard Worker 1 * sizeof(SkColor4f));
212*c8dee2aaSAndroid Build Coastguard Worker auto gainmapImage = SkImages::RasterFromPixmap(gainmapPixmap, nullptr, nullptr);
213*c8dee2aaSAndroid Build Coastguard Worker const auto gainmapImageRect = SkRect::MakeXYWH(0.f, 0.f, 1.f, 2.f);
214*c8dee2aaSAndroid Build Coastguard Worker SkGainmapInfo gainmapInfo = simple_gainmap_info(2.f);
215*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fBaseImageType = SkGainmapInfo::BaseImageType::kHDR;
216*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fEpsilonSdr[0] = 0.1f;
217*c8dee2aaSAndroid Build Coastguard Worker
218*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo canvasInfo = SkImageInfo::Make(
219*c8dee2aaSAndroid Build Coastguard Worker 2, 4, kRGBA_F32_SkColorType, kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
220*c8dee2aaSAndroid Build Coastguard Worker SkBitmap canvasBitmap;
221*c8dee2aaSAndroid Build Coastguard Worker canvasBitmap.allocPixels(canvasInfo);
222*c8dee2aaSAndroid Build Coastguard Worker canvasBitmap.eraseColor(SK_ColorTRANSPARENT);
223*c8dee2aaSAndroid Build Coastguard Worker const auto canvasRect = SkRect::MakeXYWH(0.f, 0.f, 2.f, 4.f);
224*c8dee2aaSAndroid Build Coastguard Worker
225*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> shader = SkGainmapShader::Make(hdrImage,
226*c8dee2aaSAndroid Build Coastguard Worker hdrImageRect,
227*c8dee2aaSAndroid Build Coastguard Worker SkSamplingOptions(),
228*c8dee2aaSAndroid Build Coastguard Worker gainmapImage,
229*c8dee2aaSAndroid Build Coastguard Worker gainmapImageRect,
230*c8dee2aaSAndroid Build Coastguard Worker SkSamplingOptions(),
231*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo,
232*c8dee2aaSAndroid Build Coastguard Worker canvasRect,
233*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fDisplayRatioSdr,
234*c8dee2aaSAndroid Build Coastguard Worker canvasInfo.refColorSpace());
235*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
236*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(shader);
237*c8dee2aaSAndroid Build Coastguard Worker SkCanvas canvas(canvasBitmap);
238*c8dee2aaSAndroid Build Coastguard Worker canvas.drawRect(canvasRect, paint);
239*c8dee2aaSAndroid Build Coastguard Worker
240*c8dee2aaSAndroid Build Coastguard Worker // Compute and compare the expected colors.
241*c8dee2aaSAndroid Build Coastguard Worker // This is linearToSRGB(srgbToLinear(1.0)*0.5) = linearToSRGB(0.5).
242*c8dee2aaSAndroid Build Coastguard Worker constexpr float k10G = 0.7353569830524495f;
243*c8dee2aaSAndroid Build Coastguard Worker // This is linearToSRGB(srgbToLinear(0.5)*0.5)
244*c8dee2aaSAndroid Build Coastguard Worker constexpr float k05G = 0.3607802138332792f;
245*c8dee2aaSAndroid Build Coastguard Worker // The 'R' component also has a fEpsilonSdr set.
246*c8dee2aaSAndroid Build Coastguard Worker // This is linearToSRGB(srgbToLinear(1.0)*0.5-0.1) = linearToSRGB(0.4).
247*c8dee2aaSAndroid Build Coastguard Worker constexpr float kR10G = 0.6651850846308363f;
248*c8dee2aaSAndroid Build Coastguard Worker // This is linearToSRGB(srgbToLinear(0.5)-0.1)
249*c8dee2aaSAndroid Build Coastguard Worker // The gain map is 0.f (no gain), but there are still affectd by the offset.
250*c8dee2aaSAndroid Build Coastguard Worker constexpr float kR05G = 0.371934685412575f;
251*c8dee2aaSAndroid Build Coastguard Worker SkColor4f expectedColors[4][2] = {
252*c8dee2aaSAndroid Build Coastguard Worker {{kR10G, 1.0f, 1.0f, 1.0f}, {kR10G, 1.0f, 0.5f, 1.0f}},
253*c8dee2aaSAndroid Build Coastguard Worker {{kR10G, 0.5f, 1.0f, 1.0f}, {kR10G, 0.5f, 0.5f, 1.0f}},
254*c8dee2aaSAndroid Build Coastguard Worker {{kR05G, k10G, k10G, 1.0f}, {kR05G, k10G, k05G, 1.0f}},
255*c8dee2aaSAndroid Build Coastguard Worker {{kR05G, k05G, k10G, 1.0f}, {kR05G, k05G, k05G, 1.0f}},
256*c8dee2aaSAndroid Build Coastguard Worker };
257*c8dee2aaSAndroid Build Coastguard Worker for (int y = 0; y < 4; ++y) {
258*c8dee2aaSAndroid Build Coastguard Worker for (int x = 0; x < 2; ++x) {
259*c8dee2aaSAndroid Build Coastguard Worker const auto color = canvasBitmap.getColor4f(x, y);
260*c8dee2aaSAndroid Build Coastguard Worker const auto& expected = expectedColors[y][x];
261*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r,
262*c8dee2aaSAndroid Build Coastguard Worker approx_equal(color, expected),
263*c8dee2aaSAndroid Build Coastguard Worker "color (%.3f %.3f %.3f %.3f) does not match expected color (%.3f %.3f "
264*c8dee2aaSAndroid Build Coastguard Worker "%.3f %.3f) at "
265*c8dee2aaSAndroid Build Coastguard Worker "pixel (%d, %d)",
266*c8dee2aaSAndroid Build Coastguard Worker color.fR,
267*c8dee2aaSAndroid Build Coastguard Worker color.fG,
268*c8dee2aaSAndroid Build Coastguard Worker color.fB,
269*c8dee2aaSAndroid Build Coastguard Worker color.fA,
270*c8dee2aaSAndroid Build Coastguard Worker expected.fR,
271*c8dee2aaSAndroid Build Coastguard Worker expected.fG,
272*c8dee2aaSAndroid Build Coastguard Worker expected.fB,
273*c8dee2aaSAndroid Build Coastguard Worker expected.fA,
274*c8dee2aaSAndroid Build Coastguard Worker x,
275*c8dee2aaSAndroid Build Coastguard Worker y);
276*c8dee2aaSAndroid Build Coastguard Worker }
277*c8dee2aaSAndroid Build Coastguard Worker }
278*c8dee2aaSAndroid Build Coastguard Worker }
279*c8dee2aaSAndroid Build Coastguard Worker
280*c8dee2aaSAndroid Build Coastguard Worker // Verify that the gainmap shader isn't affected by the color spaces of the base, gainmap, or
281*c8dee2aaSAndroid Build Coastguard Worker // destination. But the fGainmapMathColorSpace is taken into account.
DEF_TEST(GainmapShader_colorSpace,r)282*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(GainmapShader_colorSpace, r) {
283*c8dee2aaSAndroid Build Coastguard Worker auto sdrColorSpace =
284*c8dee2aaSAndroid Build Coastguard Worker SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kSRGB)->makeColorSpin();
285*c8dee2aaSAndroid Build Coastguard Worker auto gainmapColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, SkNamedGamut::kRec2020);
286*c8dee2aaSAndroid Build Coastguard Worker auto dstColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, SkNamedGamut::kDisplayP3);
287*c8dee2aaSAndroid Build Coastguard Worker
288*c8dee2aaSAndroid Build Coastguard Worker constexpr SkColor4f kSdrColor = {0.25f, 0.5f, 1.f, 1.f};
289*c8dee2aaSAndroid Build Coastguard Worker constexpr SkColor4f kGainmapColor = {
290*c8dee2aaSAndroid Build Coastguard Worker 0.0f, // The sRGB G channel will have a exp2(0.0)=1.000 gain.
291*c8dee2aaSAndroid Build Coastguard Worker 0.5f, // The sRGB B channel will have a exp2(0.5)=1.414 gain.
292*c8dee2aaSAndroid Build Coastguard Worker 1.0f, // The sRGB R channel will have a exp2(1.0)=2.000 gain.
293*c8dee2aaSAndroid Build Coastguard Worker 1.f};
294*c8dee2aaSAndroid Build Coastguard Worker constexpr SkColor4f kExpectedColor = {0.5f, 0.5f, 1.414f, 1.f};
295*c8dee2aaSAndroid Build Coastguard Worker
296*c8dee2aaSAndroid Build Coastguard Worker auto sdrImage = make_1x1_image(sdrColorSpace, kOpaque_SkAlphaType, kSdrColor);
297*c8dee2aaSAndroid Build Coastguard Worker auto gainmapImage = make_1x1_image(
298*c8dee2aaSAndroid Build Coastguard Worker gainmapColorSpace, kOpaque_SkAlphaType, kGainmapColor, gainmapColorSpace);
299*c8dee2aaSAndroid Build Coastguard Worker SkGainmapInfo gainmapInfo = simple_gainmap_info(2.f);
300*c8dee2aaSAndroid Build Coastguard Worker
301*c8dee2aaSAndroid Build Coastguard Worker auto color = draw_1x1_gainmap(
302*c8dee2aaSAndroid Build Coastguard Worker sdrImage, gainmapImage, gainmapInfo, gainmapInfo.fDisplayRatioHdr, dstColorSpace);
303*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, approx_equal(color, kExpectedColor));
304*c8dee2aaSAndroid Build Coastguard Worker
305*c8dee2aaSAndroid Build Coastguard Worker // Setting fGainmapMathColorSpace to the base image's color space does not change the result.
306*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fGainmapMathColorSpace = sdrColorSpace;
307*c8dee2aaSAndroid Build Coastguard Worker color = draw_1x1_gainmap(
308*c8dee2aaSAndroid Build Coastguard Worker sdrImage, gainmapImage, gainmapInfo, gainmapInfo.fDisplayRatioHdr, dstColorSpace);
309*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, approx_equal(color, kExpectedColor));
310*c8dee2aaSAndroid Build Coastguard Worker
311*c8dee2aaSAndroid Build Coastguard Worker // Setting fGainmapMathColorSpace ot a different color space does change the result.
312*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fGainmapMathColorSpace =
313*c8dee2aaSAndroid Build Coastguard Worker SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, SkNamedGamut::kRec2020);
314*c8dee2aaSAndroid Build Coastguard Worker color = draw_1x1_gainmap(
315*c8dee2aaSAndroid Build Coastguard Worker sdrImage, gainmapImage, gainmapInfo, gainmapInfo.fDisplayRatioHdr, dstColorSpace);
316*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !approx_equal(color, kExpectedColor));
317*c8dee2aaSAndroid Build Coastguard Worker }
318*c8dee2aaSAndroid Build Coastguard Worker
319*c8dee2aaSAndroid Build Coastguard Worker // Verify that a fully applied Apple gainmap maps the specification.
DEF_TEST(GainmapShader_apple,r)320*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(GainmapShader_apple, r) {
321*c8dee2aaSAndroid Build Coastguard Worker constexpr SkColor4f kSdrColor = {0.25f, 0.5f, 1.f, 1.f};
322*c8dee2aaSAndroid Build Coastguard Worker const SkColor4f kGainmapColor = {0.0f, // The R channel will have a linear value of 0.0.
323*c8dee2aaSAndroid Build Coastguard Worker 0.702250f, // The G channel will have a linear value of 0.5.
324*c8dee2aaSAndroid Build Coastguard Worker 1.0f, // The B channel will have a linear value 0f 1.0.
325*c8dee2aaSAndroid Build Coastguard Worker 1.f};
326*c8dee2aaSAndroid Build Coastguard Worker
327*c8dee2aaSAndroid Build Coastguard Worker // Set the HDR headroom to 5.0.
328*c8dee2aaSAndroid Build Coastguard Worker const float kH = 5.f;
329*c8dee2aaSAndroid Build Coastguard Worker
330*c8dee2aaSAndroid Build Coastguard Worker const SkColor4f kExpectedColor = {0.25f * (1 + (kH - 1) * 0.0f), // 0.25,
331*c8dee2aaSAndroid Build Coastguard Worker 0.50f * (1 + (kH - 1) * 0.5f), // 0.5,
332*c8dee2aaSAndroid Build Coastguard Worker 1.00f * (1 + (kH - 1) * 1.0f), // 5.0,
333*c8dee2aaSAndroid Build Coastguard Worker 1.f};
334*c8dee2aaSAndroid Build Coastguard Worker
335*c8dee2aaSAndroid Build Coastguard Worker auto sdrImage = make_1x1_image(SkColorSpace::MakeSRGB(), kOpaque_SkAlphaType, kSdrColor);
336*c8dee2aaSAndroid Build Coastguard Worker auto gainmapImage = make_1x1_image(nullptr, kOpaque_SkAlphaType, kGainmapColor);
337*c8dee2aaSAndroid Build Coastguard Worker SkGainmapInfo gainmapInfo = simple_gainmap_info(kH);
338*c8dee2aaSAndroid Build Coastguard Worker gainmapInfo.fType = SkGainmapInfo::Type::kApple;
339*c8dee2aaSAndroid Build Coastguard Worker
340*c8dee2aaSAndroid Build Coastguard Worker auto color = draw_1x1_gainmap(
341*c8dee2aaSAndroid Build Coastguard Worker sdrImage, gainmapImage, gainmapInfo, kH, SkColorSpace::MakeSRGBLinear());
342*c8dee2aaSAndroid Build Coastguard Worker
343*c8dee2aaSAndroid Build Coastguard Worker // Note that (0.250, 1.548, 5.000) is *not* an acceptable answer here (even though it's sort-of
344*c8dee2aaSAndroid Build Coastguard Worker // close). That number is the result of not using the the Apple-compatible math.
345*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, approx_equal(color, kExpectedColor));
346*c8dee2aaSAndroid Build Coastguard Worker }
347