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