• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 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 "include/core/SkAlphaType.h"
9 #include "include/core/SkColorSpace.h"
10 #include "include/core/SkColorType.h"
11 #include "include/core/SkImageInfo.h"
12 #include "include/core/SkRefCnt.h"
13 #include "include/core/SkScalar.h"
14 #include "include/core/SkSize.h"
15 #include "include/core/SkString.h"
16 #include "include/core/SkTypes.h"
17 #include "include/gpu/GrBackendSurface.h"
18 #include "include/gpu/GrDirectContext.h"
19 #include "include/gpu/GrTypes.h"
20 #include "include/private/gpu/ganesh/GrTypesPriv.h"
21 #include "src/gpu/SkBackingFit.h"
22 #include "src/gpu/ganesh/GrCaps.h"
23 #include "src/gpu/ganesh/GrDirectContextPriv.h"
24 #include "src/gpu/ganesh/GrImageInfo.h"
25 #include "src/gpu/ganesh/GrPixmap.h"
26 #include "src/gpu/ganesh/GrShaderCaps.h"
27 #include "src/gpu/ganesh/SurfaceContext.h"
28 #include "tests/CtsEnforcement.h"
29 #include "tests/Test.h"
30 #include "tests/TestUtils.h"
31 
32 #include <algorithm>
33 #include <cmath>
34 #include <cstdint>
35 #include <initializer_list>
36 #include <memory>
37 #include <string>
38 
39 class GrRecordingContext;
40 struct GrContextOptions;
41 
42 // using anonymous namespace because these functions are used as template params.
43 namespace {
44 /** convert 0..1 srgb value to 0..1 linear */
srgb_to_linear(float srgb)45 float srgb_to_linear(float srgb) {
46     if (srgb <= 0.04045f) {
47         return srgb / 12.92f;
48     } else {
49         return powf((srgb + 0.055f) / 1.055f, 2.4f);
50     }
51 }
52 
53 /** convert 0..1 linear value to 0..1 srgb */
linear_to_srgb(float linear)54 float linear_to_srgb(float linear) {
55     if (linear <= 0.0031308) {
56         return linear * 12.92f;
57     } else {
58         return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f;
59     }
60 }
61 }  // namespace
62 
63 /** tests a conversion with an error tolerance */
check_conversion(uint32_t input,uint32_t output,float error)64 template <float (*CONVERT)(float)> static bool check_conversion(uint32_t input, uint32_t output,
65                                                                 float error) {
66     // alpha should always be exactly preserved.
67     if ((input & 0xff000000) != (output & 0xff000000)) {
68         return false;
69     }
70 
71     for (int c = 0; c < 3; ++c) {
72         uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8));
73         float lower = std::max(0.f, (float) inputComponent - error);
74         float upper = std::min(255.f, (float) inputComponent + error);
75         lower = CONVERT(lower / 255.f);
76         upper = CONVERT(upper / 255.f);
77         SkASSERT(lower >= 0.f && lower <= 255.f);
78         SkASSERT(upper >= 0.f && upper <= 255.f);
79         uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8);
80         if (outputComponent < SkScalarFloorToInt(lower * 255.f) ||
81             outputComponent > SkScalarCeilToInt(upper * 255.f)) {
82             return false;
83         }
84     }
85     return true;
86 }
87 
88 /** tests a forward and backward conversion with an error tolerance */
89 template <float (*FORWARD)(float), float (*BACKWARD)(float)>
check_double_conversion(uint32_t input,uint32_t output,float error)90 static bool check_double_conversion(uint32_t input, uint32_t output, float error) {
91     // alpha should always be exactly preserved.
92     if ((input & 0xff000000) != (output & 0xff000000)) {
93         return false;
94     }
95 
96     for (int c = 0; c < 3; ++c) {
97         uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8));
98         float lower = std::max(0.f, (float) inputComponent - error);
99         float upper = std::min(255.f, (float) inputComponent + error);
100         lower = FORWARD(lower / 255.f);
101         upper = FORWARD(upper / 255.f);
102         SkASSERT(lower >= 0.f && lower <= 255.f);
103         SkASSERT(upper >= 0.f && upper <= 255.f);
104         uint8_t upperComponent = SkScalarCeilToInt(upper * 255.f);
105         uint8_t lowerComponent = SkScalarFloorToInt(lower * 255.f);
106         lower = std::max(0.f, (float) lowerComponent - error);
107         upper = std::min(255.f, (float) upperComponent + error);
108         lower = BACKWARD(lowerComponent / 255.f);
109         upper = BACKWARD(upperComponent / 255.f);
110         SkASSERT(lower >= 0.f && lower <= 255.f);
111         SkASSERT(upper >= 0.f && upper <= 255.f);
112         upperComponent = SkScalarCeilToInt(upper * 255.f);
113         lowerComponent = SkScalarFloorToInt(lower * 255.f);
114 
115         uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8);
116         if (outputComponent < lowerComponent || outputComponent > upperComponent) {
117             return false;
118         }
119     }
120     return true;
121 }
122 
check_srgb_to_linear_conversion(uint32_t srgb,uint32_t linear,float error)123 static bool check_srgb_to_linear_conversion(uint32_t srgb, uint32_t linear, float error) {
124     return check_conversion<srgb_to_linear>(srgb, linear, error);
125 }
126 
check_linear_to_srgb_conversion(uint32_t linear,uint32_t srgb,float error)127 static bool check_linear_to_srgb_conversion(uint32_t linear, uint32_t srgb, float error) {
128     return check_conversion<linear_to_srgb>(linear, srgb, error);
129 }
130 
check_linear_to_srgb_to_linear_conversion(uint32_t input,uint32_t output,float error)131 static bool check_linear_to_srgb_to_linear_conversion(uint32_t input, uint32_t output, float error) {
132     return check_double_conversion<linear_to_srgb, srgb_to_linear>(input, output, error);
133 }
134 
check_srgb_to_linear_to_srgb_conversion(uint32_t input,uint32_t output,float error)135 static bool check_srgb_to_linear_to_srgb_conversion(uint32_t input, uint32_t output, float error) {
136     return check_double_conversion<srgb_to_linear, linear_to_srgb>(input, output, error);
137 }
138 
check_no_conversion(uint32_t input,uint32_t output,float error)139 static bool check_no_conversion(uint32_t input, uint32_t output, float error) {
140     // This is a bit of a hack to check identity transformations that may lose precision.
141     return check_srgb_to_linear_to_srgb_conversion(input, output, error);
142 }
143 
144 typedef bool (*CheckFn) (uint32_t orig, uint32_t actual, float error);
145 
read_and_check_pixels(skiatest::Reporter * reporter,GrDirectContext * dContext,skgpu::v1::SurfaceContext * sc,uint32_t * origData,const SkImageInfo & dstInfo,CheckFn checker,float error,const char * subtestName)146 void read_and_check_pixels(skiatest::Reporter* reporter,
147                            GrDirectContext* dContext,
148                            skgpu::v1::SurfaceContext* sc,
149                            uint32_t* origData,
150                            const SkImageInfo& dstInfo, CheckFn checker, float error,
151                            const char* subtestName) {
152     auto [w, h] = dstInfo.dimensions();
153     GrPixmap readPM = GrPixmap::Allocate(dstInfo);
154     memset(readPM.addr(), 0, sizeof(uint32_t)*w*h);
155 
156     if (!sc->readPixels(dContext, readPM, {0, 0})) {
157         ERRORF(reporter, "Could not read pixels for %s.", subtestName);
158         return;
159     }
160 
161     for (int j = 0; j < h; ++j) {
162         for (int i = 0; i < w; ++i) {
163             uint32_t orig = origData[j * w + i];
164             uint32_t read = static_cast<uint32_t*>(readPM.addr())[j * w + i];
165 
166             if (!checker(orig, read, error)) {
167                 ERRORF(reporter, "Original 0x%08x, read back as 0x%08x in %s at %d, %d).", orig,
168                        read, subtestName, i, j);
169                 return;
170             }
171         }
172     }
173 }
174 
175 namespace {
176 enum class Encoding {
177     kUntagged,
178     kLinear,
179     kSRGB,
180 };
181 }  // namespace
182 
encoding_as_color_space(Encoding encoding)183 static sk_sp<SkColorSpace> encoding_as_color_space(Encoding encoding) {
184     switch (encoding) {
185         case Encoding::kUntagged: return nullptr;
186         case Encoding::kLinear:   return SkColorSpace::MakeSRGBLinear();
187         case Encoding::kSRGB:     return SkColorSpace::MakeSRGB();
188     }
189     return nullptr;
190 }
191 
encoding_as_str(Encoding encoding)192 static const char* encoding_as_str(Encoding encoding) {
193     switch (encoding) {
194         case Encoding::kUntagged: return "untagged";
195         case Encoding::kLinear:   return "linear";
196         case Encoding::kSRGB:     return "sRGB";
197     }
198     return nullptr;
199 }
200 
201 static constexpr int kW = 255;
202 static constexpr int kH = 255;
203 
make_data()204 static std::unique_ptr<uint32_t[]> make_data() {
205     std::unique_ptr<uint32_t[]> data(new uint32_t[kW * kH]);
206     for (int j = 0; j < kH; ++j) {
207         for (int i = 0; i < kW; ++i) {
208             data[j * kW + i] = (0xFF << 24) | (i << 16) | (i << 8) | i;
209         }
210     }
211     return data;
212 }
213 
make_surface_context(Encoding contextEncoding,GrRecordingContext * rContext,skiatest::Reporter * reporter)214 static std::unique_ptr<skgpu::v1::SurfaceContext> make_surface_context(
215         Encoding contextEncoding,
216         GrRecordingContext* rContext,
217         skiatest::Reporter* reporter) {
218     GrImageInfo info(GrColorType::kRGBA_8888,
219                      kPremul_SkAlphaType,
220                      encoding_as_color_space(contextEncoding),
221                      kW, kH);
222 
223     auto sc = CreateSurfaceContext(rContext,
224                                    info,
225                                    SkBackingFit::kExact,
226                                    kBottomLeft_GrSurfaceOrigin,
227                                    GrRenderable::kYes);
228     if (!sc) {
229         ERRORF(reporter, "Could not create %s surface context.", encoding_as_str(contextEncoding));
230     }
231     return sc;
232 }
233 
test_write_read(Encoding contextEncoding,Encoding writeEncoding,Encoding readEncoding,float error,CheckFn check,GrDirectContext * dContext,skiatest::Reporter * reporter)234 static void test_write_read(Encoding contextEncoding, Encoding writeEncoding, Encoding readEncoding,
235                             float error, CheckFn check, GrDirectContext* dContext,
236                             skiatest::Reporter* reporter) {
237     auto surfaceContext = make_surface_context(contextEncoding, dContext, reporter);
238     if (!surfaceContext) {
239         return;
240     }
241     auto writeII = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
242                                      encoding_as_color_space(writeEncoding));
243     auto data = make_data();
244     GrCPixmap dataPM(writeII, data.get(), kW*sizeof(uint32_t));
245     if (!surfaceContext->writePixels(dContext, dataPM, {0, 0})) {
246         ERRORF(reporter, "Could not write %s to %s surface context.",
247                encoding_as_str(writeEncoding), encoding_as_str(contextEncoding));
248         return;
249     }
250 
251     auto readII = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
252                                     encoding_as_color_space(readEncoding));
253     SkString testName;
254     testName.printf("write %s data to a %s context and read as %s.", encoding_as_str(writeEncoding),
255                     encoding_as_str(contextEncoding), encoding_as_str(readEncoding));
256     read_and_check_pixels(reporter, dContext, surfaceContext.get(), data.get(), readII, check,
257                           error, testName.c_str());
258 }
259 
260 // Test all combinations of writePixels/readPixels where the surface context/write source/read dst
261 // are sRGB, linear, or untagged RGBA_8888.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SRGBReadWritePixels,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)262 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SRGBReadWritePixels,
263                                        reporter,
264                                        ctxInfo,
265                                        CtsEnforcement::kApiLevel_T) {
266     auto context = ctxInfo.directContext();
267     if (!context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888_SRGB,
268                                                          GrRenderable::kNo).isValid()) {
269         return;
270     }
271     // We allow more error on GPUs with lower precision shader variables.
272     float error = context->priv().caps()->shaderCaps()->fHalfIs32Bits ? 0.5f : 1.2f;
273     // For the all-sRGB case, we allow a small error only for devices that have
274     // precision variation because the sRGB data gets converted to linear and back in
275     // the shader.
276     float smallError = context->priv().caps()->shaderCaps()->fHalfIs32Bits ? 0.0f : 1.f;
277 
278     ///////////////////////////////////////////////////////////////////////////////////////////////
279     // Write sRGB data to a sRGB context - no conversion on the write.
280 
281     // back to sRGB - no conversion.
282     test_write_read(Encoding::kSRGB, Encoding::kSRGB, Encoding::kSRGB, smallError,
283                     check_no_conversion, context, reporter);
284     // Reading back to untagged should be a pass through with no conversion.
285     test_write_read(Encoding::kSRGB, Encoding::kSRGB, Encoding::kUntagged, error,
286                     check_no_conversion, context, reporter);
287 
288     // Converts back to linear
289     test_write_read(Encoding::kSRGB, Encoding::kSRGB, Encoding::kLinear, error,
290                     check_srgb_to_linear_conversion, context, reporter);
291 
292     // Untagged source data should be interpreted as sRGB.
293     test_write_read(Encoding::kSRGB, Encoding::kUntagged, Encoding::kSRGB, smallError,
294                     check_no_conversion, context, reporter);
295 
296     ///////////////////////////////////////////////////////////////////////////////////////////////
297     // Write linear data to a sRGB context. It gets converted to sRGB on write. The reads
298     // are all the same as the above cases where the original data was untagged.
299     test_write_read(Encoding::kSRGB, Encoding::kLinear, Encoding::kSRGB, error,
300                     check_linear_to_srgb_conversion, context, reporter);
301     // When the dst buffer is untagged there should be no conversion on the read.
302     test_write_read(Encoding::kSRGB, Encoding::kLinear, Encoding::kUntagged, error,
303                     check_linear_to_srgb_conversion, context, reporter);
304     test_write_read(Encoding::kSRGB, Encoding::kLinear, Encoding::kLinear, error,
305                     check_linear_to_srgb_to_linear_conversion, context, reporter);
306 
307     ///////////////////////////////////////////////////////////////////////////////////////////////
308     // Write data to an untagged context. The write does no conversion no matter what encoding the
309     // src data has.
310     for (auto writeEncoding : {Encoding::kSRGB, Encoding::kUntagged, Encoding::kLinear}) {
311         // The read from untagged to sRGB also does no conversion.
312         test_write_read(Encoding::kUntagged, writeEncoding, Encoding::kSRGB, error,
313                         check_no_conversion, context, reporter);
314         // Reading untagged back as untagged should do no conversion.
315         test_write_read(Encoding::kUntagged, writeEncoding, Encoding::kUntagged, error,
316                         check_no_conversion, context, reporter);
317         // Reading untagged back as linear does convert (context is source, so treated as sRGB),
318         // dst is tagged.
319         test_write_read(Encoding::kUntagged, writeEncoding, Encoding::kLinear, error,
320                         check_srgb_to_linear_conversion, context, reporter);
321     }
322 
323     ///////////////////////////////////////////////////////////////////////////////////////////////
324     // Write sRGB data to a linear context - converts to sRGB on the write.
325 
326     // converts back to sRGB on read.
327     test_write_read(Encoding::kLinear, Encoding::kSRGB, Encoding::kSRGB, error,
328                     check_srgb_to_linear_to_srgb_conversion, context, reporter);
329     // Reading untagged data from linear currently does no conversion.
330     test_write_read(Encoding::kLinear, Encoding::kSRGB, Encoding::kUntagged, error,
331                     check_srgb_to_linear_conversion, context, reporter);
332     // Stays linear when read.
333     test_write_read(Encoding::kLinear, Encoding::kSRGB, Encoding::kLinear, error,
334                     check_srgb_to_linear_conversion, context, reporter);
335 
336     // Untagged source data should be interpreted as sRGB.
337     test_write_read(Encoding::kLinear, Encoding::kUntagged, Encoding::kSRGB, error,
338                     check_srgb_to_linear_to_srgb_conversion, context, reporter);
339 
340     ///////////////////////////////////////////////////////////////////////////////////////////////
341     // Write linear data to a linear context. Does no conversion.
342 
343     // Reading to sRGB does a conversion.
344     test_write_read(Encoding::kLinear, Encoding::kLinear, Encoding::kSRGB, error,
345                     check_linear_to_srgb_conversion, context, reporter);
346     // Reading to untagged does no conversion.
347     test_write_read(Encoding::kLinear, Encoding::kLinear, Encoding::kUntagged, error,
348                     check_no_conversion, context, reporter);
349     // Stays linear when read.
350     test_write_read(Encoding::kLinear, Encoding::kLinear, Encoding::kLinear, error,
351                     check_no_conversion, context, reporter);
352 }
353