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