1 /*
2 * Copyright 2020 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/SkTypes.h"
9 #ifdef SK_ENABLE_NDK_IMAGES
10 #include "include/codec/SkEncodedImageFormat.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkImageGenerator.h"
15 #include "include/core/SkStream.h"
16 #include "include/encode/SkJpegEncoder.h"
17 #include "include/encode/SkPngEncoder.h"
18 #include "include/encode/SkWebpEncoder.h"
19 #include "include/private/base/SkMalloc.h"
20 #include "src/image/SkImageGeneratorPriv.h"
21 #include "tests/Test.h"
22 #include "tools/Resources.h"
23 #include "tools/ToolUtils.h"
24
25 #include <stdint.h>
26 #include <vector>
27
28 static const char* kPng = "png";
29 static const char* kJpeg = "jpeg";
30 static const char* kWebpLossless = "webp_lossless";
31 static const char* kWebpLossy = "webp_lossy";
32
33 namespace {
34 static const struct {
35 SkEncodedImageFormat format;
36 int quality;
37 const char* name;
38 } gRecs[] = {
39 { SkEncodedImageFormat::kPNG, 100, kPng},
40 { SkEncodedImageFormat::kJPEG, 100, kJpeg},
41 { SkEncodedImageFormat::kWEBP, 100, kWebpLossless},
42 { SkEncodedImageFormat::kWEBP, 80, kWebpLossy},
43 };
44 }
45
encode_ndk(const SkPixmap & pmap,SkEncodedImageFormat format,int quality)46 static sk_sp<SkData> encode_ndk(const SkPixmap& pmap, SkEncodedImageFormat format, int quality) {
47 SkDynamicMemoryWStream stream;
48 SkDynamicMemoryWStream buf;
49 switch (format) {
50 case SkEncodedImageFormat::kPNG: {
51 bool success = SkPngEncoder::Encode(&buf, pmap, {});
52 return success ? buf.detachAsData() : nullptr;
53 }
54 case SkEncodedImageFormat::kJPEG: {
55 SkJpegEncoder::Options opts;
56 opts.fQuality = quality;
57 bool success = SkJpegEncoder::Encode(&buf, pmap, opts);
58 return success ? buf.detachAsData() : nullptr;
59 }
60 case SkEncodedImageFormat::kWEBP: {
61 SkWebpEncoder::Options opts;
62 opts.fQuality = quality;
63 bool success = SkWebpEncoder::Encode(&buf, pmap, opts);
64 return success ? buf.detachAsData() : nullptr;
65 }
66 default:
67 SkUNREACHABLE;
68 }
69 }
70
DEF_TEST(NdkEncode,r)71 DEF_TEST(NdkEncode, r) {
72 for (auto ct : { kRGBA_8888_SkColorType,
73 kRGB_565_SkColorType,
74 kRGBA_F16_SkColorType }) {
75 SkBitmap bm;
76 bm.allocPixels(SkImageInfo::Make(10, 10, ct, kOpaque_SkAlphaType));
77 bm.eraseColor(SK_ColorBLUE);
78 for (const auto& rec : gRecs) {
79 auto encoded = encode_ndk(bm.pixmap(), rec.format, rec.quality);
80 if (!encoded) {
81 ERRORF(r, "Failed to encode %s to %s\n", ToolUtils::colortype_name(ct), rec.name);
82 continue;
83 }
84 auto gen = SkImageGenerators::MakeFromEncoded(std::move(encoded));
85 if (!gen) {
86 ERRORF(r, "Failed to decode from %s as %s\n", ToolUtils::colortype_name(ct),
87 rec.name);
88 continue;
89 }
90
91 if (rec.name == kPng && bm.colorType() == kRGB_565_SkColorType) {
92 REPORTER_ASSERT(r, gen->getInfo().colorType() == kRGB_565_SkColorType);
93 } else {
94 REPORTER_ASSERT(r, gen->getInfo().colorType() == kN32_SkColorType);
95 }
96
97 SkBitmap bm2;
98 bm2.allocPixels(bm.info());
99 REPORTER_ASSERT(r, gen->getPixels(bm2.pixmap()));
100
101 for (int x = 0; x < bm.width(); x++)
102 for (int y = 0; y < bm.height(); y++) {
103 SkColor orig = bm .getColor(x, y);
104 SkColor actual = bm2.getColor(x, y);
105
106 REPORTER_ASSERT(r, SkColorGetA(orig) == SkColorGetA(actual));
107 REPORTER_ASSERT(r, SkColorGetA(orig) == 0xFF);
108
109 if (rec.name == kPng || rec.name == kWebpLossless) {
110 REPORTER_ASSERT(r, orig == actual);
111 } else {
112 int diffR = std::abs((int) SkColorGetR(orig) - (int) SkColorGetR(actual));
113 int diffG = std::abs((int) SkColorGetG(orig) - (int) SkColorGetG(actual));
114 int diffB = std::abs((int) SkColorGetB(orig) - (int) SkColorGetB(actual));
115 REPORTER_ASSERT(r, diffR <= 2 && diffG <= 1 && diffB <= 1);
116 }
117 }
118 }
119 }
120 }
121
DEF_TEST(NdkEncode_unsupportedFormats,r)122 DEF_TEST(NdkEncode_unsupportedFormats, r) {
123 for (auto ct : { kRGBA_8888_SkColorType,
124 kRGB_565_SkColorType,
125 kRGBA_F16_SkColorType }) {
126 SkBitmap bm;
127 bm.allocPixels(SkImageInfo::Make(10, 10, ct, kOpaque_SkAlphaType));
128 bm.eraseColor(SK_ColorBLUE);
129 for (auto format : { SkEncodedImageFormat::kBMP,
130 SkEncodedImageFormat::kGIF,
131 SkEncodedImageFormat::kICO,
132 SkEncodedImageFormat::kWBMP,
133 SkEncodedImageFormat::kPKM,
134 SkEncodedImageFormat::kKTX,
135 SkEncodedImageFormat::kASTC,
136 SkEncodedImageFormat::kDNG,
137 SkEncodedImageFormat::kHEIF }) {
138 REPORTER_ASSERT(r, !encode_ndk(bm.pixmap(), format, 100));
139 }
140 }
141 }
142
DEF_TEST(NdkEncode_badQuality,r)143 DEF_TEST(NdkEncode_badQuality, r) {
144 for (auto ct : { kRGBA_8888_SkColorType,
145 kRGB_565_SkColorType,
146 kRGBA_F16_SkColorType }) {
147 SkBitmap bm;
148 bm.allocPixels(SkImageInfo::Make(10, 10, ct, kOpaque_SkAlphaType));
149 bm.eraseColor(SK_ColorBLUE);
150 for (auto format : { SkEncodedImageFormat::kJPEG,
151 SkEncodedImageFormat::kPNG,
152 SkEncodedImageFormat::kWEBP }) {
153 for (int quality : {-1, -100, 101, 200}) {
154 REPORTER_ASSERT(r, !encode_ndk(bm.pixmap(), format, quality));
155 }
156 }
157 }
158 }
159
DEF_TEST(NdkEncode_nullPixels,r)160 DEF_TEST(NdkEncode_nullPixels, r) {
161 for (auto info : { SkImageInfo::MakeUnknown(),
162 SkImageInfo::MakeN32Premul(10, 10),
163 SkImageInfo::MakeN32Premul(0, 0)}) {
164 SkPixmap pm(info, nullptr, info.minRowBytes());
165 for (const auto& rec : gRecs) {
166 REPORTER_ASSERT(r, !encode_ndk(pm, rec.format, rec.quality));
167 }
168 }
169 }
170
DEF_TEST(NdkEncode_badInfo,r)171 DEF_TEST(NdkEncode_badInfo, r) {
172 // Allocate an arbitrary amount of memory. These infos don't have a natural
173 // amount to allocate, and the encoder shouldn't touch the memory anyway.
174 // But this allows us to verify that the bad info fails, even when the pixel
175 // pointer is not null.
176 void* pixels = sk_malloc_throw(1024);
177 std::vector<SkPixmap> pixmaps{ SkPixmap(SkImageInfo::MakeN32Premul(-10, 10), pixels, 1000),
178 SkPixmap(SkImageInfo::MakeN32Premul(10, -10), pixels, 200),
179 SkPixmap(SkImageInfo::MakeN32Premul(10, 10), pixels, 20),
180 SkPixmap(SkImageInfo::MakeN32Premul(10, 10), pixels, 41),
181 SkPixmap(SkImageInfo::MakeN32Premul(10, 10), pixels, 0),
182 SkPixmap(SkImageInfo::MakeN32Premul( 0, 0), pixels, 40)};
183 if (sizeof(size_t) > sizeof(uint32_t)) {
184 pixmaps.emplace_back(SkImageInfo::MakeN32Premul(10, 10), pixels,
185 static_cast<size_t>(UINT32_MAX) + 1);
186 }
187 for (const auto& pm : pixmaps) {
188 for (const auto& rec : gRecs) {
189 REPORTER_ASSERT(r, !encode_ndk(pm, rec.format, rec.quality));
190 }
191 }
192 free(pixels);
193 }
194
DEF_TEST(NdkEncode_unsupportedColorTypes,r)195 DEF_TEST(NdkEncode_unsupportedColorTypes, r) {
196 for (SkColorType ct : {
197 kUnknown_SkColorType,
198 kAlpha_8_SkColorType,
199 kARGB_4444_SkColorType,
200 kRGB_888x_SkColorType,
201 kBGRA_8888_SkColorType,
202 kRGBA_1010102_SkColorType,
203 kBGRA_1010102_SkColorType,
204 kRGB_101010x_SkColorType,
205 kBGR_101010x_SkColorType,
206 kGray_8_SkColorType,
207 kRGBA_F16Norm_SkColorType,
208 kRGB_F16F16F16x_SkColorType,
209 kRGBA_F32_SkColorType,
210 kR8G8_unorm_SkColorType,
211 kA16_float_SkColorType,
212 kR16G16_float_SkColorType,
213 kA16_unorm_SkColorType,
214 kR16G16_unorm_SkColorType,
215 kR16G16B16A16_unorm_SkColorType,
216 }) {
217 auto info = SkImageInfo::Make(7, 13, ct, kOpaque_SkAlphaType, SkColorSpace::MakeSRGB());
218 SkBitmap bm;
219 bm.allocPixels(info);
220 bm.eraseColor(SK_ColorGREEN);
221 for (const auto& rec : gRecs) {
222 REPORTER_ASSERT(r, !encode_ndk(bm.pixmap(), rec.format, rec.quality));
223 }
224 if (!SkColorTypeIsAlwaysOpaque(ct)) {
225 for (auto at : { kPremul_SkAlphaType, kUnpremul_SkAlphaType}) {
226 info = info.makeAlphaType(at);
227 bm.allocPixels(info);
228 bm.eraseARGB(0x7F, 0xFF, 0xFF, 0xFF);
229 }
230 for (const auto& rec : gRecs) {
231 REPORTER_ASSERT(r, !encode_ndk(bm.pixmap(), rec.format, rec.quality));
232 }
233 }
234 }
235 }
236
DEF_TEST(NdkEncode_unsupportedAlphaTypes,r)237 DEF_TEST(NdkEncode_unsupportedAlphaTypes, r) {
238 for (auto ct : { kRGBA_8888_SkColorType,
239 kRGB_565_SkColorType,
240 kRGBA_F16_SkColorType }) {
241 for (auto at : { kUnknown_SkAlphaType, (SkAlphaType) -1}) {
242 auto info = SkImageInfo::Make(10, 10, ct, at);
243 size_t rowBytes = info.minRowBytes();
244 void* pixels = sk_malloc_throw(info.computeByteSize(rowBytes));
245 SkPixmap pm(info, pixels, rowBytes);
246 for (const auto& rec : gRecs) {
247 REPORTER_ASSERT(r, !encode_ndk(pm, rec.format, rec.quality));
248 }
249 free(pixels);
250 }
251 }
252 }
253
254 static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
255
256 static constexpr skcms_Matrix3x3 kDCIP3 = {{
257 {0.486143, 0.323835, 0.154234},
258 {0.226676, 0.710327, 0.0629966},
259 {0.000800549, 0.0432385, 0.78275},
260 }};
261
262
nearly_equal(float a,float b)263 static bool nearly_equal(float a, float b) {
264 return fabs(a - b) < .002f;
265 }
266
nearly_equal(const skcms_TransferFunction & x,const skcms_TransferFunction & y)267 static bool nearly_equal(const skcms_TransferFunction& x, const skcms_TransferFunction& y) {
268 return nearly_equal(x.g, y.g)
269 && nearly_equal(x.a, y.a)
270 && nearly_equal(x.b, y.b)
271 && nearly_equal(x.c, y.c)
272 && nearly_equal(x.d, y.d)
273 && nearly_equal(x.e, y.e)
274 && nearly_equal(x.f, y.f);
275 }
276
nearly_equal(const skcms_Matrix3x3 & a,const skcms_Matrix3x3 & b)277 static bool nearly_equal(const skcms_Matrix3x3& a, const skcms_Matrix3x3& b) {
278 for (int i = 0; i < 3; i++)
279 for (int j = 0; j < 3; j++) {
280 if (!nearly_equal(a.vals[i][j], b.vals[i][j])) return false;
281 }
282 return true;
283 }
284
nearly_equal(SkColorSpace * a,SkColorSpace * b)285 static bool nearly_equal(SkColorSpace* a, SkColorSpace* b) {
286 skcms_TransferFunction fnA, fnB;
287 skcms_Matrix3x3 gamutA, gamutB;
288 return a && b && a->isNumericalTransferFn(&fnA) && a->toXYZD50(&gamutA)
289 && b->isNumericalTransferFn(&fnB) && b->toXYZD50(&gamutB)
290 && nearly_equal(fnA, fnB) && nearly_equal(gamutA, gamutB);
291 }
292
DEF_TEST(NdkEncode_ColorSpace,r)293 DEF_TEST(NdkEncode_ColorSpace, r) {
294 const struct {
295 sk_sp<SkColorSpace> cs;
296 const char* name;
297 } colorSpaces[] = {
298 { sk_sp<SkColorSpace>(nullptr), "null" },
299 { SkColorSpace::MakeSRGB(), "srgb" },
300 { SkColorSpace::MakeSRGBLinear(), "srgb-linear"},
301 { SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, SkNamedGamut::kSRGB), "bt709" },
302 { SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, SkNamedGamut::kRec2020), "rec2020" },
303 { SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3), "p3" },
304 { SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB), "adobeRGB"},
305 { SkColorSpace::MakeRGB(k2Dot6, kDCIP3), "dci-p3" },
306 };
307 for (const auto& colorSpace : colorSpaces) {
308 for (auto ct : { kRGBA_8888_SkColorType, kRGB_565_SkColorType, kRGBA_F16_SkColorType }) {
309 SkBitmap bm;
310 bm.allocPixels(SkImageInfo::Make(10, 10, ct, kOpaque_SkAlphaType, colorSpace.cs));
311 bm.eraseColor(SK_ColorRED);
312
313 for (const auto& rec : gRecs) {
314 auto encoded = encode_ndk(bm.pixmap(), rec.format, rec.quality);
315 REPORTER_ASSERT(r, encoded);
316 auto gen = SkImageGenerators::MakeFromEncoded(std::move(encoded));
317 REPORTER_ASSERT(r, gen);
318
319 auto expected = colorSpace.cs ? colorSpace.cs : SkColorSpace::MakeSRGB();
320 auto* actual = gen->getInfo().colorSpace();
321 if (!nearly_equal(actual, expected.get())) {
322 const char* name = "unknown";
323 for (auto named : colorSpaces) {
324 if (nearly_equal(actual, named.cs.get())) {
325 name = named.name;
326 break;
327 }
328 }
329
330 ERRORF(r, "Mismatch: expected: %s\tactual:%s", colorSpace.name, name);
331 }
332 }
333 }
334 }
335 }
336
DEF_TEST(NdkEncode_unsupportedColorSpace,r)337 DEF_TEST(NdkEncode_unsupportedColorSpace, r) {
338 std::vector<sk_sp<SkColorSpace>> unsupportedCs;
339 for (auto gamut : { SkNamedGamut::kSRGB, SkNamedGamut::kAdobeRGB, SkNamedGamut::kDisplayP3,
340 SkNamedGamut::kRec2020, SkNamedGamut::kXYZ }) {
341 unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut));
342 unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut));
343 unsupportedCs.push_back(SkColorSpace::MakeRGB(k2Dot6, gamut));
344 }
345
346 for (auto gamut : { SkNamedGamut::kSRGB, SkNamedGamut::kDisplayP3,
347 SkNamedGamut::kRec2020, SkNamedGamut::kXYZ }) {
348 unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, gamut));
349 }
350
351 for (auto gamut : { SkNamedGamut::kAdobeRGB, SkNamedGamut::kDisplayP3,
352 SkNamedGamut::kXYZ }) {
353 unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut));
354 }
355
356 for (auto gamut : { SkNamedGamut::kAdobeRGB, SkNamedGamut::kDisplayP3,
357 SkNamedGamut::kRec2020, SkNamedGamut::kXYZ }) {
358 unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut));
359 }
360
361 for (auto gamut : { SkNamedGamut::kAdobeRGB,
362 SkNamedGamut::kRec2020, SkNamedGamut::kXYZ }) {
363 unsupportedCs.push_back(SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut));
364 }
365
366 for (auto fn : { SkNamedTransferFn::kSRGB, SkNamedTransferFn::k2Dot2,
367 SkNamedTransferFn::kLinear, SkNamedTransferFn::kRec2020 }) {
368 unsupportedCs.push_back(SkColorSpace::MakeRGB(fn, kDCIP3));
369 }
370
371 for (auto unsupported : unsupportedCs) {
372 for (auto ct : { kRGBA_8888_SkColorType, kRGB_565_SkColorType, kRGBA_F16_SkColorType }) {
373 SkBitmap bm;
374 bm.allocPixels(SkImageInfo::Make(10, 10, ct, kOpaque_SkAlphaType, unsupported));
375 bm.eraseColor(SK_ColorBLUE);
376
377 for (const auto& rec : gRecs) {
378 REPORTER_ASSERT(r, !encode_ndk(bm.pixmap(), rec.format, rec.quality));
379 }
380 }
381 }
382 }
383
384 #endif // SK_ENABLE_NDK_IMAGES
385