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