xref: /aosp_15_r20/external/skia/tests/NdkEncodeTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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