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