1 /*
2 * Copyright 2019 Google LLC
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/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkColorType.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkSurface.h"
17 #include "include/core/SkTypes.h"
18 #include "include/gpu/GpuTypes.h"
19 #include "include/gpu/GrBackendSurface.h"
20 #include "include/gpu/GrDirectContext.h"
21 #include "include/gpu/GrTypes.h"
22 #include "include/private/base/SkTo.h"
23 #include "src/core/SkAutoPixmapStorage.h"
24 #include "src/core/SkImageInfoPriv.h"
25 #include "tests/CtsEnforcement.h"
26 #include "tests/Test.h"
27 #include "tests/TestUtils.h"
28 #include "tools/ToolUtils.h"
29
30 #include <array>
31 #include <cstddef>
32 #include <cstdint>
33 #include <functional>
34 #include <initializer_list>
35
36 class SkPixmap;
37 struct GrContextOptions;
38
39 static constexpr int kSize = 32;
40
get_trans_black_expected_color(SkColorChannelFlag channels)41 static SkColor4f get_trans_black_expected_color(SkColorChannelFlag channels) {
42 float a = 0;
43 if (!(channels & kAlpha_SkColorChannelFlag)) {
44 a = 1;
45 }
46
47 return { 0, 0, 0, a };
48 }
49
get_opaque_white_expected_color(SkColorChannelFlag channels)50 static SkColor4f get_opaque_white_expected_color(SkColorChannelFlag channels) {
51 if (channels & kGray_SkColorChannelFlag) {
52 return { 1, 1, 1, 1 };
53 }
54
55 float r = 1, g = 1, b = 1;
56 if (!(channels & kRed_SkColorChannelFlag)) {
57 r = 0;
58 }
59 if (!(channels & kGreen_SkColorChannelFlag)) {
60 g = 0;
61 }
62 if (!(channels & kBlue_SkColorChannelFlag)) {
63 b = 0;
64 }
65
66 return { r, g, b, 1.0f };
67 }
68
69 struct TestCase {
70 SkColorType fColorType;
71 SkAlphaType fAlphaType;
72 SkColorChannelFlag fChannels;
73 bool fGpuCanMakeSurfaces;
74 };
75
76 static const TestCase gTests[] = {
77 { kAlpha_8_SkColorType, kPremul_SkAlphaType, kAlpha_SkColorChannelFlag, true },
78 { kA16_unorm_SkColorType, kPremul_SkAlphaType, kAlpha_SkColorChannelFlag, false},
79 { kA16_float_SkColorType, kPremul_SkAlphaType, kAlpha_SkColorChannelFlag, false},
80 { kRGB_565_SkColorType, kOpaque_SkAlphaType, kRGB_SkColorChannelFlags, true },
81 { kARGB_4444_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorChannelFlags, true },
82 { kRGBA_8888_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorChannelFlags, true },
83 { kRGB_888x_SkColorType, kOpaque_SkAlphaType, kRGB_SkColorChannelFlags, true },
84 { kBGRA_8888_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorChannelFlags, true },
85 { kRGBA_1010102_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorChannelFlags, true },
86 { kRGB_101010x_SkColorType, kOpaque_SkAlphaType, kRGB_SkColorChannelFlags, true },
87 { kGray_8_SkColorType, kOpaque_SkAlphaType, kGray_SkColorChannelFlag, true },
88 { kRGBA_F16Norm_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorChannelFlags, true },
89 { kRGBA_F16_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorChannelFlags, true },
90 { kRGBA_F32_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorChannelFlags, true },
91 { kR8G8_unorm_SkColorType, kOpaque_SkAlphaType, kRG_SkColorChannelFlags, true },
92 { kR16G16_unorm_SkColorType, kOpaque_SkAlphaType, kRG_SkColorChannelFlags, false},
93 { kR16G16_float_SkColorType, kOpaque_SkAlphaType, kRG_SkColorChannelFlags, false},
94 { kR16G16B16A16_unorm_SkColorType, kPremul_SkAlphaType, kRGBA_SkColorChannelFlags, false},
95 };
96
raster_tests(skiatest::Reporter * reporter,const TestCase & test)97 static void raster_tests(skiatest::Reporter* reporter, const TestCase& test) {
98
99 const SkImageInfo nativeII = SkImageInfo::Make(kSize, kSize, test.fColorType, test.fAlphaType);
100 const SkImageInfo f32Unpremul = SkImageInfo::Make(kSize, kSize, kRGBA_F32_SkColorType,
101 kUnpremul_SkAlphaType);
102
103 uint32_t actualChannels = SkColorTypeChannelFlags(test.fColorType);
104 REPORTER_ASSERT(reporter, test.fChannels == actualChannels);
105
106 // all colorTypes can be drawn to
107 {
108 auto s = SkSurface::MakeRaster(nativeII);
109 REPORTER_ASSERT(reporter, SkToBool(s));
110 }
111
112 // opaque formats should make transparent black become opaque
113 {
114 SkAutoPixmapStorage pm;
115 pm.alloc(nativeII);
116 pm.erase(SkColors::kTransparent);
117 SkColor actual = pm.getColor(0, 0);
118 SkColor4f expected = get_trans_black_expected_color(test.fChannels);
119 REPORTER_ASSERT(reporter, expected.toSkColor() == actual);
120 }
121
122 // unused channels should drop out
123 {
124 SkAutoPixmapStorage pm;
125 pm.alloc(nativeII);
126 pm.erase(SkColors::kWhite);
127 SkColor actual = pm.getColor(0, 0);
128 SkColor4f expected = get_opaque_white_expected_color(test.fChannels);
129 REPORTER_ASSERT(reporter, expected.toSkColor() == actual);
130 }
131
132 // Reading back from an image to the same colorType should always work
133 {
134 SkAutoPixmapStorage srcPM;
135 srcPM.alloc(nativeII);
136 srcPM.erase(SkColors::kWhite);
137 auto i = SkImage::MakeFromRaster(srcPM, nullptr, nullptr);
138 REPORTER_ASSERT(reporter, SkToBool(i));
139
140 SkAutoPixmapStorage readbackPM;
141 readbackPM.alloc(nativeII);
142 readbackPM.erase(SkColors::kTransparent);
143
144 REPORTER_ASSERT(reporter, i->readPixels(nullptr, readbackPM, 0, 0));
145
146 SkColor expected = srcPM.getColor(0, 0);
147 SkColor actual = readbackPM.getColor(0, 0);
148 REPORTER_ASSERT(reporter, expected == actual);
149 }
150
151 // Rendering to an F32 surface should always work
152 {
153 SkAutoPixmapStorage srcPM;
154 srcPM.alloc(nativeII);
155 srcPM.erase(SkColors::kWhite);
156 auto i = SkImage::MakeFromRaster(srcPM, nullptr, nullptr);
157 REPORTER_ASSERT(reporter, SkToBool(i));
158
159 auto s = SkSurface::MakeRaster(f32Unpremul);
160 REPORTER_ASSERT(reporter, SkToBool(s));
161
162 {
163 auto c = s->getCanvas();
164 c->drawImage(i, 0, 0);
165 }
166
167 SkAutoPixmapStorage readbackPM;
168 readbackPM.alloc(f32Unpremul);
169 readbackPM.erase(SkColors::kTransparent);
170
171 REPORTER_ASSERT(reporter, i->readPixels(nullptr, readbackPM, 0, 0));
172
173 SkColor expected = srcPM.getColor(0, 0);
174 SkColor actual = readbackPM.getColor(0, 0);
175 REPORTER_ASSERT(reporter, expected == actual);
176 }
177 }
178
compare_pixmaps(skiatest::Reporter * reporter,const SkPixmap & expected,const SkPixmap & actual,SkColorType ct,const char * label)179 static void compare_pixmaps(skiatest::Reporter* reporter,
180 const SkPixmap& expected, const SkPixmap& actual,
181 SkColorType ct, const char* label) {
182 const float tols[4] = {0.0f, 0.0f, 0.0f, 0};
183
184 auto error = std::function<ComparePixmapsErrorReporter>(
185 [reporter, ct, label](int x, int y, const float diffs[4]) {
186 SkASSERT(x >= 0 && y >= 0);
187 ERRORF(reporter, "%s %s - mismatch at %d, %d (%f, %f, %f %f)",
188 ToolUtils::colortype_name(ct), label, x, y,
189 diffs[0], diffs[1], diffs[2], diffs[3]);
190 });
191
192 ComparePixels(expected, actual, tols, error);
193 }
194
gpu_tests(GrDirectContext * dContext,skiatest::Reporter * reporter,const TestCase & test)195 static void gpu_tests(GrDirectContext* dContext,
196 skiatest::Reporter* reporter,
197 const TestCase& test) {
198
199 const SkImageInfo nativeII = SkImageInfo::Make(kSize, kSize, test.fColorType, test.fAlphaType);
200 const SkImageInfo f32Unpremul = SkImageInfo::Make(kSize, kSize, kRGBA_F32_SkColorType,
201 kUnpremul_SkAlphaType);
202
203 // We had better not be able to render to prohibited colorTypes
204 if (!test.fGpuCanMakeSurfaces) {
205 auto s = SkSurface::MakeRenderTarget(dContext, skgpu::Budgeted::kNo, nativeII);
206 REPORTER_ASSERT(reporter, !SkToBool(s));
207 }
208
209 if (!dContext->colorTypeSupportedAsImage(test.fColorType)) {
210 return;
211 }
212
213 SkAutoPixmapStorage nativeExpected;
214 nativeExpected.alloc(nativeII);
215 nativeExpected.erase(SkColors::kWhite);
216
217 for (bool fullInit : { false, true }) {
218 GrBackendTexture backendTex;
219
220 bool finishedBECreate = false;
221 auto markFinished = [](void* context) {
222 *(bool*)context = true;
223 };
224 if (fullInit) {
225 backendTex = dContext->createBackendTexture(nativeExpected, kTopLeft_GrSurfaceOrigin,
226 GrRenderable::kNo, GrProtected::kNo,
227 markFinished, &finishedBECreate);
228 } else {
229 backendTex = dContext->createBackendTexture(kSize,
230 kSize,
231 test.fColorType,
232 SkColors::kWhite,
233 GrMipmapped::kNo,
234 GrRenderable::kNo,
235 GrProtected::kNo,
236 markFinished,
237 &finishedBECreate);
238 }
239 REPORTER_ASSERT(reporter, backendTex.isValid());
240 dContext->submit();
241 while (backendTex.isValid() && !finishedBECreate) {
242 dContext->checkAsyncWorkCompletion();
243 }
244
245 auto img = SkImage::MakeFromTexture(dContext, backendTex, kTopLeft_GrSurfaceOrigin,
246 test.fColorType, test.fAlphaType, nullptr);
247 REPORTER_ASSERT(reporter, SkToBool(img));
248
249 {
250 SkAutoPixmapStorage nativeActual;
251 nativeActual.alloc(nativeII);
252 nativeActual.erase(SkColors::kTransparent);
253
254 if (img->readPixels(dContext, nativeActual, 0, 0)) {
255 compare_pixmaps(reporter, nativeExpected, nativeActual,
256 test.fColorType, "SkImage::readPixels to native CT");
257 }
258
259 // SkSurface::readPixels with the same colorType as the source pixels round trips
260 // (when allowed)
261 if (dContext->colorTypeSupportedAsSurface(test.fColorType)) {
262 auto s = SkSurface::MakeRenderTarget(dContext, skgpu::Budgeted::kNo, nativeII);
263 REPORTER_ASSERT(reporter, SkToBool(s));
264
265 {
266 SkCanvas* c = s->getCanvas();
267 c->drawImage(img, 0, 0);
268 }
269
270 nativeActual.erase(SkColors::kTransparent);
271 REPORTER_ASSERT(reporter, s->readPixels(nativeActual, 0, 0));
272
273 compare_pixmaps(reporter, nativeExpected, nativeActual,
274 test.fColorType, "SkSurface::readPixels to native CT");
275 }
276 }
277
278 {
279 SkAutoPixmapStorage f32Expected;
280 f32Expected.alloc(f32Unpremul);
281 f32Expected.erase(get_opaque_white_expected_color(test.fChannels));
282
283 // read back to F32 if possible
284 {
285 SkAutoPixmapStorage f32Actual;
286 f32Actual.alloc(f32Unpremul);
287 f32Actual.erase(SkColors::kTransparent);
288 if (img->readPixels(dContext, f32Actual, 0, 0)) {
289 compare_pixmaps(reporter, f32Expected, f32Actual,
290 test.fColorType, "SkImage::readPixels to F32");
291 }
292 }
293
294 // drawing a native SkImage works appropriately (as assessed by reading back from an
295 // RGBA8 surface to an F32 pixmap)
296 {
297 const SkImageInfo rgba8888Premul = SkImageInfo::Make(kSize, kSize,
298 kRGBA_8888_SkColorType,
299 kPremul_SkAlphaType);
300
301 auto s =
302 SkSurface::MakeRenderTarget(dContext, skgpu::Budgeted::kNo, rgba8888Premul);
303 REPORTER_ASSERT(reporter, SkToBool(s));
304
305 {
306 SkCanvas* c = s->getCanvas();
307 c->drawImage(img, 0, 0);
308 }
309
310 SkAutoPixmapStorage f32Actual;
311 f32Actual.alloc(f32Unpremul);
312 f32Actual.erase(SkColors::kTransparent);
313 REPORTER_ASSERT(reporter, s->readPixels(f32Actual, 0, 0));
314
315 compare_pixmaps(reporter, f32Expected, f32Actual,
316 test.fColorType, "SkSurface::drawn to RGBA8888");
317 }
318 }
319
320 img.reset();
321 dContext->flushAndSubmit();
322 dContext->deleteBackendTexture(backendTex);
323 }
324 }
325
DEF_TEST(ExtendedSkColorTypeTests_raster,reporter)326 DEF_TEST(ExtendedSkColorTypeTests_raster, reporter) {
327 for (size_t i = 0; i < std::size(gTests); ++i) {
328 raster_tests(reporter, gTests[i]);
329 }
330 }
331
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ExtendedSkColorTypeTests_gpu,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)332 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ExtendedSkColorTypeTests_gpu,
333 reporter,
334 ctxInfo,
335 CtsEnforcement::kApiLevel_T) {
336 auto context = ctxInfo.directContext();
337
338 for (size_t i = 0; i < std::size(gTests); ++i) {
339 gpu_tests(context, reporter, gTests[i]);
340 }
341 }
342