apng.cc (37785B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 #include "lib/extras/dec/apng.h" 7 8 // Parts of this code are taken from apngdis, which has the following license: 9 /* APNG Disassembler 2.8 10 * 11 * Deconstructs APNG files into individual frames. 12 * 13 * http://apngdis.sourceforge.net 14 * 15 * Copyright (c) 2010-2015 Max Stepin 16 * maxst at users.sourceforge.net 17 * 18 * zlib license 19 * ------------ 20 * 21 * This software is provided 'as-is', without any express or implied 22 * warranty. In no event will the authors be held liable for any damages 23 * arising from the use of this software. 24 * 25 * Permission is granted to anyone to use this software for any purpose, 26 * including commercial applications, and to alter it and redistribute it 27 * freely, subject to the following restrictions: 28 * 29 * 1. The origin of this software must not be misrepresented; you must not 30 * claim that you wrote the original software. If you use this software 31 * in a product, an acknowledgment in the product documentation would be 32 * appreciated but is not required. 33 * 2. Altered source versions must be plainly marked as such, and must not be 34 * misrepresented as being the original software. 35 * 3. This notice may not be removed or altered from any source distribution. 36 * 37 */ 38 39 #include <jxl/codestream_header.h> 40 #include <jxl/encode.h> 41 #include <string.h> 42 43 #include <string> 44 #include <utility> 45 #include <vector> 46 47 #include "lib/extras/size_constraints.h" 48 #include "lib/jxl/base/byte_order.h" 49 #include "lib/jxl/base/common.h" 50 #include "lib/jxl/base/compiler_specific.h" 51 #include "lib/jxl/base/printf_macros.h" 52 #include "lib/jxl/base/scope_guard.h" 53 #include "lib/jxl/sanitizers.h" 54 #if JPEGXL_ENABLE_APNG 55 #include "png.h" /* original (unpatched) libpng is ok */ 56 #endif 57 58 namespace jxl { 59 namespace extras { 60 61 #if JPEGXL_ENABLE_APNG 62 namespace { 63 64 constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69, 65 0x66, 0x00, 0x00}; 66 67 /* hIST chunk tail is not proccesed properly; skip this chunk completely; 68 see https://github.com/glennrp/libpng/pull/413 */ 69 const png_byte kIgnoredPngChunks[] = { 70 104, 73, 83, 84, '\0' /* hIST */ 71 }; 72 73 // Returns floating-point value from the PNG encoding (times 10^5). 74 double F64FromU32(const uint32_t x) { return static_cast<int32_t>(x) * 1E-5; } 75 76 Status DecodeSRGB(const unsigned char* payload, const size_t payload_size, 77 JxlColorEncoding* color_encoding) { 78 if (payload_size != 1) return JXL_FAILURE("Wrong sRGB size"); 79 // (PNG uses the same values as ICC.) 80 if (payload[0] >= 4) return JXL_FAILURE("Invalid Rendering Intent"); 81 color_encoding->white_point = JXL_WHITE_POINT_D65; 82 color_encoding->primaries = JXL_PRIMARIES_SRGB; 83 color_encoding->transfer_function = JXL_TRANSFER_FUNCTION_SRGB; 84 color_encoding->rendering_intent = 85 static_cast<JxlRenderingIntent>(payload[0]); 86 return true; 87 } 88 89 // If the cICP profile is not fully supported, return false and leave 90 // color_encoding unmodified. 91 Status DecodeCICP(const unsigned char* payload, const size_t payload_size, 92 JxlColorEncoding* color_encoding) { 93 if (payload_size != 4) return JXL_FAILURE("Wrong cICP size"); 94 JxlColorEncoding color_enc = *color_encoding; 95 96 // From https://www.itu.int/rec/T-REC-H.273-202107-I/en 97 if (payload[0] == 1) { 98 // IEC 61966-2-1 sRGB 99 color_enc.primaries = JXL_PRIMARIES_SRGB; 100 color_enc.white_point = JXL_WHITE_POINT_D65; 101 } else if (payload[0] == 4) { 102 // Rec. ITU-R BT.470-6 System M 103 color_enc.primaries = JXL_PRIMARIES_CUSTOM; 104 color_enc.primaries_red_xy[0] = 0.67; 105 color_enc.primaries_red_xy[1] = 0.33; 106 color_enc.primaries_green_xy[0] = 0.21; 107 color_enc.primaries_green_xy[1] = 0.71; 108 color_enc.primaries_blue_xy[0] = 0.14; 109 color_enc.primaries_blue_xy[1] = 0.08; 110 color_enc.white_point = JXL_WHITE_POINT_CUSTOM; 111 color_enc.white_point_xy[0] = 0.310; 112 color_enc.white_point_xy[1] = 0.316; 113 } else if (payload[0] == 5) { 114 // Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM 115 color_enc.primaries = JXL_PRIMARIES_CUSTOM; 116 color_enc.primaries_red_xy[0] = 0.64; 117 color_enc.primaries_red_xy[1] = 0.33; 118 color_enc.primaries_green_xy[0] = 0.29; 119 color_enc.primaries_green_xy[1] = 0.60; 120 color_enc.primaries_blue_xy[0] = 0.15; 121 color_enc.primaries_blue_xy[1] = 0.06; 122 color_enc.white_point = JXL_WHITE_POINT_D65; 123 } else if (payload[0] == 6 || payload[0] == 7) { 124 // SMPTE ST 170 (2004) / SMPTE ST 240 (1999) 125 color_enc.primaries = JXL_PRIMARIES_CUSTOM; 126 color_enc.primaries_red_xy[0] = 0.630; 127 color_enc.primaries_red_xy[1] = 0.340; 128 color_enc.primaries_green_xy[0] = 0.310; 129 color_enc.primaries_green_xy[1] = 0.595; 130 color_enc.primaries_blue_xy[0] = 0.155; 131 color_enc.primaries_blue_xy[1] = 0.070; 132 color_enc.white_point = JXL_WHITE_POINT_D65; 133 } else if (payload[0] == 8) { 134 // Generic film (colour filters using Illuminant C) 135 color_enc.primaries = JXL_PRIMARIES_CUSTOM; 136 color_enc.primaries_red_xy[0] = 0.681; 137 color_enc.primaries_red_xy[1] = 0.319; 138 color_enc.primaries_green_xy[0] = 0.243; 139 color_enc.primaries_green_xy[1] = 0.692; 140 color_enc.primaries_blue_xy[0] = 0.145; 141 color_enc.primaries_blue_xy[1] = 0.049; 142 color_enc.white_point = JXL_WHITE_POINT_CUSTOM; 143 color_enc.white_point_xy[0] = 0.310; 144 color_enc.white_point_xy[1] = 0.316; 145 } else if (payload[0] == 9) { 146 // Rec. ITU-R BT.2100-2 147 color_enc.primaries = JXL_PRIMARIES_2100; 148 color_enc.white_point = JXL_WHITE_POINT_D65; 149 } else if (payload[0] == 10) { 150 // CIE 1931 XYZ 151 color_enc.primaries = JXL_PRIMARIES_CUSTOM; 152 color_enc.primaries_red_xy[0] = 1; 153 color_enc.primaries_red_xy[1] = 0; 154 color_enc.primaries_green_xy[0] = 0; 155 color_enc.primaries_green_xy[1] = 1; 156 color_enc.primaries_blue_xy[0] = 0; 157 color_enc.primaries_blue_xy[1] = 0; 158 color_enc.white_point = JXL_WHITE_POINT_E; 159 } else if (payload[0] == 11) { 160 // SMPTE RP 431-2 (2011) 161 color_enc.primaries = JXL_PRIMARIES_P3; 162 color_enc.white_point = JXL_WHITE_POINT_DCI; 163 } else if (payload[0] == 12) { 164 // SMPTE EG 432-1 (2010) 165 color_enc.primaries = JXL_PRIMARIES_P3; 166 color_enc.white_point = JXL_WHITE_POINT_D65; 167 } else if (payload[0] == 22) { 168 color_enc.primaries = JXL_PRIMARIES_CUSTOM; 169 color_enc.primaries_red_xy[0] = 0.630; 170 color_enc.primaries_red_xy[1] = 0.340; 171 color_enc.primaries_green_xy[0] = 0.295; 172 color_enc.primaries_green_xy[1] = 0.605; 173 color_enc.primaries_blue_xy[0] = 0.155; 174 color_enc.primaries_blue_xy[1] = 0.077; 175 color_enc.white_point = JXL_WHITE_POINT_D65; 176 } else { 177 JXL_WARNING("Unsupported primaries specified in cICP chunk: %d", 178 static_cast<int>(payload[0])); 179 return false; 180 } 181 182 if (payload[1] == 1 || payload[1] == 6 || payload[1] == 14 || 183 payload[1] == 15) { 184 // Rec. ITU-R BT.709-6 185 color_enc.transfer_function = JXL_TRANSFER_FUNCTION_709; 186 } else if (payload[1] == 4) { 187 // Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM 188 color_enc.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; 189 color_enc.gamma = 1 / 2.2; 190 } else if (payload[1] == 5) { 191 // Rec. ITU-R BT.470-6 System B, G 192 color_enc.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; 193 color_enc.gamma = 1 / 2.8; 194 } else if (payload[1] == 8 || payload[1] == 13 || payload[1] == 16 || 195 payload[1] == 17 || payload[1] == 18) { 196 // These codes all match the corresponding JXL enum values 197 color_enc.transfer_function = static_cast<JxlTransferFunction>(payload[1]); 198 } else { 199 JXL_WARNING("Unsupported transfer function specified in cICP chunk: %d", 200 static_cast<int>(payload[1])); 201 return false; 202 } 203 204 if (payload[2] != 0) { 205 JXL_WARNING("Unsupported color space specified in cICP chunk: %d", 206 static_cast<int>(payload[2])); 207 return false; 208 } 209 if (payload[3] != 1) { 210 JXL_WARNING("Unsupported full-range flag specified in cICP chunk: %d", 211 static_cast<int>(payload[3])); 212 return false; 213 } 214 // cICP has no rendering intent, so use the default 215 color_enc.rendering_intent = JXL_RENDERING_INTENT_RELATIVE; 216 *color_encoding = color_enc; 217 return true; 218 } 219 220 Status DecodeGAMA(const unsigned char* payload, const size_t payload_size, 221 JxlColorEncoding* color_encoding) { 222 if (payload_size != 4) return JXL_FAILURE("Wrong gAMA size"); 223 color_encoding->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; 224 color_encoding->gamma = F64FromU32(LoadBE32(payload)); 225 return true; 226 } 227 228 Status DecodeCHRM(const unsigned char* payload, const size_t payload_size, 229 JxlColorEncoding* color_encoding) { 230 if (payload_size != 32) return JXL_FAILURE("Wrong cHRM size"); 231 232 color_encoding->white_point = JXL_WHITE_POINT_CUSTOM; 233 color_encoding->white_point_xy[0] = F64FromU32(LoadBE32(payload + 0)); 234 color_encoding->white_point_xy[1] = F64FromU32(LoadBE32(payload + 4)); 235 236 color_encoding->primaries = JXL_PRIMARIES_CUSTOM; 237 color_encoding->primaries_red_xy[0] = F64FromU32(LoadBE32(payload + 8)); 238 color_encoding->primaries_red_xy[1] = F64FromU32(LoadBE32(payload + 12)); 239 color_encoding->primaries_green_xy[0] = F64FromU32(LoadBE32(payload + 16)); 240 color_encoding->primaries_green_xy[1] = F64FromU32(LoadBE32(payload + 20)); 241 color_encoding->primaries_blue_xy[0] = F64FromU32(LoadBE32(payload + 24)); 242 color_encoding->primaries_blue_xy[1] = F64FromU32(LoadBE32(payload + 28)); 243 return true; 244 } 245 246 // Retrieves XMP and EXIF/IPTC from itext and text. 247 class BlobsReaderPNG { 248 public: 249 static Status Decode(const png_text_struct& info, PackedMetadata* metadata) { 250 // We trust these are properly null-terminated by libpng. 251 const char* key = info.key; 252 const char* value = info.text; 253 if (strstr(key, "XML:com.adobe.xmp")) { 254 metadata->xmp.resize(strlen(value)); // safe, see above 255 memcpy(metadata->xmp.data(), value, metadata->xmp.size()); 256 } 257 258 std::string type; 259 std::vector<uint8_t> bytes; 260 261 // Handle text chunks annotated with key "Raw profile type ####", with 262 // #### a type, which may contain metadata. 263 const char* kKey = "Raw profile type "; 264 if (strncmp(key, kKey, strlen(kKey)) != 0) return false; 265 266 if (!MaybeDecodeBase16(key, value, &type, &bytes)) { 267 JXL_WARNING("Couldn't parse 'Raw format type' text chunk"); 268 return false; 269 } 270 if (type == "exif") { 271 // Remove "Exif\0\0" prefix if present 272 if (bytes.size() >= sizeof kExifSignature && 273 memcmp(bytes.data(), kExifSignature, sizeof kExifSignature) == 0) { 274 bytes.erase(bytes.begin(), bytes.begin() + sizeof kExifSignature); 275 } 276 if (!metadata->exif.empty()) { 277 JXL_WARNING("overwriting EXIF (%" PRIuS " bytes) with base16 (%" PRIuS 278 " bytes)", 279 metadata->exif.size(), bytes.size()); 280 } 281 metadata->exif = std::move(bytes); 282 } else if (type == "iptc") { 283 // TODO(jon): Deal with IPTC in some way 284 } else if (type == "8bim") { 285 // TODO(jon): Deal with 8bim in some way 286 } else if (type == "xmp") { 287 if (!metadata->xmp.empty()) { 288 JXL_WARNING("overwriting XMP (%" PRIuS " bytes) with base16 (%" PRIuS 289 " bytes)", 290 metadata->xmp.size(), bytes.size()); 291 } 292 metadata->xmp = std::move(bytes); 293 } else { 294 JXL_WARNING("Unknown type in 'Raw format type' text chunk: %s: %" PRIuS 295 " bytes", 296 type.c_str(), bytes.size()); 297 } 298 return true; 299 } 300 301 private: 302 // Returns false if invalid. 303 static JXL_INLINE Status DecodeNibble(const char c, 304 uint32_t* JXL_RESTRICT nibble) { 305 if ('a' <= c && c <= 'f') { 306 *nibble = 10 + c - 'a'; 307 } else if ('0' <= c && c <= '9') { 308 *nibble = c - '0'; 309 } else { 310 *nibble = 0; 311 return JXL_FAILURE("Invalid metadata nibble"); 312 } 313 JXL_ASSERT(*nibble < 16); 314 return true; 315 } 316 317 // Returns false if invalid. 318 static JXL_INLINE Status DecodeDecimal(const char** pos, const char* end, 319 uint32_t* JXL_RESTRICT value) { 320 size_t len = 0; 321 *value = 0; 322 while (*pos < end) { 323 char next = **pos; 324 if (next >= '0' && next <= '9') { 325 *value = (*value * 10) + static_cast<uint32_t>(next - '0'); 326 len++; 327 if (len > 8) { 328 break; 329 } 330 } else { 331 // Do not consume terminator (non-decimal digit). 332 break; 333 } 334 (*pos)++; 335 } 336 if (len == 0 || len > 8) { 337 return JXL_FAILURE("Failed to parse decimal"); 338 } 339 return true; 340 } 341 342 // Parses a PNG text chunk with key of the form "Raw profile type ####", with 343 // #### a type. 344 // Returns whether it could successfully parse the content. 345 // We trust key and encoded are null-terminated because they come from 346 // libpng. 347 static Status MaybeDecodeBase16(const char* key, const char* encoded, 348 std::string* type, 349 std::vector<uint8_t>* bytes) { 350 const char* encoded_end = encoded + strlen(encoded); 351 352 const char* kKey = "Raw profile type "; 353 if (strncmp(key, kKey, strlen(kKey)) != 0) return false; 354 *type = key + strlen(kKey); 355 const size_t kMaxTypeLen = 20; 356 if (type->length() > kMaxTypeLen) return false; // Type too long 357 358 // Header: freeform string and number of bytes 359 // Expected format is: 360 // \n 361 // profile name/description\n 362 // 40\n (the number of bytes after hex-decoding) 363 // 01234566789abcdef....\n (72 bytes per line max). 364 // 012345667\n (last line) 365 const char* pos = encoded; 366 367 if (*(pos++) != '\n') return false; 368 while (pos < encoded_end && *pos != '\n') { 369 pos++; 370 } 371 if (pos == encoded_end) return false; 372 // We parsed so far a \n, some number of non \n characters and are now 373 // pointing at a \n. 374 if (*(pos++) != '\n') return false; 375 // Skip leading spaces 376 while (pos < encoded_end && *pos == ' ') { 377 pos++; 378 } 379 uint32_t bytes_to_decode = 0; 380 JXL_RETURN_IF_ERROR(DecodeDecimal(&pos, encoded_end, &bytes_to_decode)); 381 382 // We need 2*bytes for the hex values plus 1 byte every 36 values, 383 // plus terminal \n for length. 384 const unsigned long needed_bytes = 385 bytes_to_decode * 2 + 1 + DivCeil(bytes_to_decode, 36); 386 if (needed_bytes != static_cast<size_t>(encoded_end - pos)) { 387 return JXL_FAILURE("Not enough bytes to parse %d bytes in hex", 388 bytes_to_decode); 389 } 390 JXL_ASSERT(bytes->empty()); 391 bytes->reserve(bytes_to_decode); 392 393 // Encoding: base16 with newline after 72 chars. 394 // pos points to the \n before the first line of hex values. 395 for (size_t i = 0; i < bytes_to_decode; ++i) { 396 if (i % 36 == 0) { 397 if (pos + 1 >= encoded_end) return false; // Truncated base16 1 398 if (*pos != '\n') return false; // Expected newline 399 ++pos; 400 } 401 402 if (pos + 2 >= encoded_end) return false; // Truncated base16 2; 403 uint32_t nibble0; 404 uint32_t nibble1; 405 JXL_RETURN_IF_ERROR(DecodeNibble(pos[0], &nibble0)); 406 JXL_RETURN_IF_ERROR(DecodeNibble(pos[1], &nibble1)); 407 bytes->push_back(static_cast<uint8_t>((nibble0 << 4) + nibble1)); 408 pos += 2; 409 } 410 if (pos + 1 != encoded_end) return false; // Too many encoded bytes 411 if (pos[0] != '\n') return false; // Incorrect metadata terminator 412 return true; 413 } 414 }; 415 416 constexpr bool isAbc(char c) { 417 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); 418 } 419 420 constexpr uint32_t kId_IHDR = 0x52444849; 421 constexpr uint32_t kId_acTL = 0x4C546361; 422 constexpr uint32_t kId_fcTL = 0x4C546366; 423 constexpr uint32_t kId_IDAT = 0x54414449; 424 constexpr uint32_t kId_fdAT = 0x54416466; 425 constexpr uint32_t kId_IEND = 0x444E4549; 426 constexpr uint32_t kId_cICP = 0x50434963; 427 constexpr uint32_t kId_iCCP = 0x50434369; 428 constexpr uint32_t kId_sRGB = 0x42475273; 429 constexpr uint32_t kId_gAMA = 0x414D4167; 430 constexpr uint32_t kId_cHRM = 0x4D524863; 431 constexpr uint32_t kId_eXIf = 0x66495865; 432 433 struct APNGFrame { 434 std::vector<uint8_t> pixels; 435 std::vector<uint8_t*> rows; 436 unsigned int w, h, delay_num, delay_den; 437 }; 438 439 struct Reader { 440 const uint8_t* next; 441 const uint8_t* last; 442 bool Read(void* data, size_t len) { 443 size_t cap = last - next; 444 size_t to_copy = std::min(cap, len); 445 memcpy(data, next, to_copy); 446 next += to_copy; 447 return (len == to_copy); 448 } 449 bool Eof() const { return next == last; } 450 }; 451 452 const unsigned long cMaxPNGSize = 1000000UL; 453 const size_t kMaxPNGChunkSize = 1lu << 30; // 1 GB 454 455 void info_fn(png_structp png_ptr, png_infop info_ptr) { 456 png_set_expand(png_ptr); 457 png_set_palette_to_rgb(png_ptr); 458 png_set_tRNS_to_alpha(png_ptr); 459 (void)png_set_interlace_handling(png_ptr); 460 png_read_update_info(png_ptr, info_ptr); 461 } 462 463 void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, 464 int pass) { 465 APNGFrame* frame = 466 reinterpret_cast<APNGFrame*>(png_get_progressive_ptr(png_ptr)); 467 JXL_CHECK(frame); 468 JXL_CHECK(row_num < frame->rows.size()); 469 JXL_CHECK(frame->rows[row_num] < frame->pixels.data() + frame->pixels.size()); 470 png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row); 471 } 472 473 inline unsigned int read_chunk(Reader* r, std::vector<uint8_t>* pChunk) { 474 unsigned char len[4]; 475 if (r->Read(&len, 4)) { 476 const auto size = png_get_uint_32(len); 477 // Check first, to avoid overflow. 478 if (size > kMaxPNGChunkSize) { 479 JXL_WARNING("APNG chunk size is too big"); 480 return 0; 481 } 482 pChunk->resize(size + 12); 483 memcpy(pChunk->data(), len, 4); 484 if (r->Read(pChunk->data() + 4, pChunk->size() - 4)) { 485 return LoadLE32(pChunk->data() + 4); 486 } 487 } 488 return 0; 489 } 490 491 int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr, 492 bool hasInfo, std::vector<uint8_t>& chunkIHDR, 493 std::vector<std::vector<uint8_t>>& chunksInfo) { 494 unsigned char header[8] = {137, 80, 78, 71, 13, 10, 26, 10}; 495 496 // Cleanup prior decoder, if any. 497 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); 498 // Just in case. Not all versions on libpng wipe-out the pointers. 499 png_ptr = nullptr; 500 info_ptr = nullptr; 501 502 png_ptr = 503 png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 504 info_ptr = png_create_info_struct(png_ptr); 505 if (!png_ptr || !info_ptr) return 1; 506 507 if (setjmp(png_jmpbuf(png_ptr))) { 508 return 1; 509 } 510 511 png_set_keep_unknown_chunks(png_ptr, 1, kIgnoredPngChunks, 512 static_cast<int>(sizeof(kIgnoredPngChunks) / 5)); 513 514 png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); 515 png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, nullptr); 516 517 png_process_data(png_ptr, info_ptr, header, 8); 518 png_process_data(png_ptr, info_ptr, chunkIHDR.data(), chunkIHDR.size()); 519 520 if (hasInfo) { 521 for (auto& chunk : chunksInfo) { 522 png_process_data(png_ptr, info_ptr, chunk.data(), chunk.size()); 523 } 524 } 525 return 0; 526 } 527 528 int processing_data(png_structp png_ptr, png_infop info_ptr, unsigned char* p, 529 unsigned int size) { 530 if (!png_ptr || !info_ptr) return 1; 531 532 if (setjmp(png_jmpbuf(png_ptr))) { 533 return 1; 534 } 535 536 png_process_data(png_ptr, info_ptr, p, size); 537 return 0; 538 } 539 540 int processing_finish(png_structp png_ptr, png_infop info_ptr, 541 PackedMetadata* metadata) { 542 unsigned char footer[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130}; 543 544 if (!png_ptr || !info_ptr) return 1; 545 546 if (setjmp(png_jmpbuf(png_ptr))) { 547 return 1; 548 } 549 550 png_process_data(png_ptr, info_ptr, footer, 12); 551 // before destroying: check if we encountered any metadata chunks 552 png_textp text_ptr; 553 int num_text; 554 png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); 555 for (int i = 0; i < num_text; i++) { 556 (void)BlobsReaderPNG::Decode(text_ptr[i], metadata); 557 } 558 559 return 0; 560 } 561 562 } // namespace 563 #endif 564 565 bool CanDecodeAPNG() { 566 #if JPEGXL_ENABLE_APNG 567 return true; 568 #else 569 return false; 570 #endif 571 } 572 573 Status DecodeImageAPNG(const Span<const uint8_t> bytes, 574 const ColorHints& color_hints, PackedPixelFile* ppf, 575 const SizeConstraints* constraints) { 576 #if JPEGXL_ENABLE_APNG 577 Reader r; 578 unsigned char sig[8]; 579 png_structp png_ptr = nullptr; 580 png_infop info_ptr = nullptr; 581 std::vector<uint8_t> chunk; 582 std::vector<uint8_t> chunkIHDR; 583 std::vector<std::vector<uint8_t>> chunksInfo; 584 bool isAnimated = false; 585 bool hasInfo = false; 586 bool seenFctl = false; 587 APNGFrame frameRaw = {}; 588 uint32_t num_channels; 589 JxlPixelFormat format; 590 unsigned int bytes_per_pixel = 0; 591 592 struct FrameInfo { 593 PackedImage data; 594 uint32_t duration; 595 size_t x0, xsize; 596 size_t y0, ysize; 597 uint32_t dispose_op; 598 uint32_t blend_op; 599 }; 600 601 std::vector<FrameInfo> frames; 602 603 // Make sure png memory is released in any case. 604 auto scope_guard = MakeScopeGuard([&]() { 605 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); 606 // Just in case. Not all versions on libpng wipe-out the pointers. 607 png_ptr = nullptr; 608 info_ptr = nullptr; 609 }); 610 611 r = {bytes.data(), bytes.data() + bytes.size()}; 612 // Not a PNG => not an error 613 unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; 614 if (!r.Read(sig, 8) || memcmp(sig, png_signature, 8) != 0) { 615 return false; 616 } 617 unsigned int id = read_chunk(&r, &chunkIHDR); 618 619 ppf->info.exponent_bits_per_sample = 0; 620 ppf->info.alpha_exponent_bits = 0; 621 ppf->info.orientation = JXL_ORIENT_IDENTITY; 622 623 ppf->frames.clear(); 624 625 bool have_color = false; 626 bool have_cicp = false; 627 bool have_iccp = false; 628 bool have_srgb = false; 629 bool errorstate = true; 630 if (id == kId_IHDR && chunkIHDR.size() == 25) { 631 unsigned int x0 = 0; 632 unsigned int y0 = 0; 633 unsigned int delay_num = 1; 634 unsigned int delay_den = 10; 635 unsigned int dop = 0; 636 unsigned int bop = 0; 637 638 unsigned int w = png_get_uint_32(chunkIHDR.data() + 8); 639 unsigned int h = png_get_uint_32(chunkIHDR.data() + 12); 640 unsigned int w0 = w; 641 unsigned int h0 = h; 642 if (w > cMaxPNGSize || h > cMaxPNGSize) { 643 return false; 644 } 645 646 // default settings in case e.g. only gAMA is given 647 ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB; 648 ppf->color_encoding.white_point = JXL_WHITE_POINT_D65; 649 ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB; 650 ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; 651 ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_RELATIVE; 652 653 if (!processing_start(png_ptr, info_ptr, static_cast<void*>(&frameRaw), 654 hasInfo, chunkIHDR, chunksInfo)) { 655 while (!r.Eof()) { 656 id = read_chunk(&r, &chunk); 657 if (!id) break; 658 seenFctl |= (id == kId_fcTL); 659 660 if (id == kId_acTL && !hasInfo && !isAnimated) { 661 isAnimated = true; 662 ppf->info.have_animation = JXL_TRUE; 663 ppf->info.animation.tps_numerator = 1000; 664 ppf->info.animation.tps_denominator = 1; 665 } else if (id == kId_IEND || 666 (id == kId_fcTL && (!hasInfo || isAnimated))) { 667 if (hasInfo) { 668 if (!processing_finish(png_ptr, info_ptr, &ppf->metadata)) { 669 // Allocates the frame buffer. 670 uint32_t duration = delay_num * 1000 / delay_den; 671 frames.push_back(FrameInfo{PackedImage(w0, h0, format), duration, 672 x0, w0, y0, h0, dop, bop}); 673 auto& frame = frames.back().data; 674 for (size_t y = 0; y < h0; ++y) { 675 memcpy(static_cast<uint8_t*>(frame.pixels()) + frame.stride * y, 676 frameRaw.rows[y], bytes_per_pixel * w0); 677 } 678 } else { 679 break; 680 } 681 } 682 683 if (id == kId_IEND) { 684 errorstate = false; 685 break; 686 } 687 if (chunk.size() < 34) { 688 return JXL_FAILURE("Received a chunk that is too small (%" PRIuS 689 "B)", 690 chunk.size()); 691 } 692 // At this point the old frame is done. Let's start a new one. 693 w0 = png_get_uint_32(chunk.data() + 12); 694 h0 = png_get_uint_32(chunk.data() + 16); 695 x0 = png_get_uint_32(chunk.data() + 20); 696 y0 = png_get_uint_32(chunk.data() + 24); 697 delay_num = png_get_uint_16(chunk.data() + 28); 698 delay_den = png_get_uint_16(chunk.data() + 30); 699 dop = chunk[32]; 700 bop = chunk[33]; 701 702 if (!delay_den) delay_den = 100; 703 704 if (w0 > cMaxPNGSize || h0 > cMaxPNGSize || x0 > cMaxPNGSize || 705 y0 > cMaxPNGSize || x0 + w0 > w || y0 + h0 > h || dop > 2 || 706 bop > 1) { 707 break; 708 } 709 710 if (hasInfo) { 711 memcpy(chunkIHDR.data() + 8, chunk.data() + 12, 8); 712 if (processing_start(png_ptr, info_ptr, 713 static_cast<void*>(&frameRaw), hasInfo, 714 chunkIHDR, chunksInfo)) { 715 break; 716 } 717 } 718 } else if (id == kId_IDAT) { 719 // First IDAT chunk means we now have all header info 720 if (seenFctl) { 721 // `fcTL` chunk must appear after all `IDAT` chunks 722 return JXL_FAILURE("IDAT chunk after fcTL chunk"); 723 } 724 hasInfo = true; 725 JXL_CHECK(w == png_get_image_width(png_ptr, info_ptr)); 726 JXL_CHECK(h == png_get_image_height(png_ptr, info_ptr)); 727 int colortype = png_get_color_type(png_ptr, info_ptr); 728 int png_bit_depth = png_get_bit_depth(png_ptr, info_ptr); 729 ppf->info.bits_per_sample = png_bit_depth; 730 png_color_8p sigbits = nullptr; 731 png_get_sBIT(png_ptr, info_ptr, &sigbits); 732 if (colortype & 1) { 733 // palette will actually be 8-bit regardless of the index bitdepth 734 ppf->info.bits_per_sample = 8; 735 } 736 if (colortype & 2) { 737 ppf->info.num_color_channels = 3; 738 ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB; 739 if (sigbits && sigbits->red == sigbits->green && 740 sigbits->green == sigbits->blue) { 741 ppf->info.bits_per_sample = sigbits->red; 742 } else if (sigbits) { 743 int maxbps = std::max(sigbits->red, 744 std::max(sigbits->green, sigbits->blue)); 745 JXL_WARNING( 746 "sBIT chunk: bit depths for R, G, and B are not the same (%i " 747 "%i %i), while in JPEG XL they have to be the same. Setting " 748 "RGB bit depth to %i.", 749 sigbits->red, sigbits->green, sigbits->blue, maxbps); 750 ppf->info.bits_per_sample = maxbps; 751 } 752 } else { 753 ppf->info.num_color_channels = 1; 754 ppf->color_encoding.color_space = JXL_COLOR_SPACE_GRAY; 755 if (sigbits) ppf->info.bits_per_sample = sigbits->gray; 756 } 757 if (colortype & 4 || 758 png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { 759 ppf->info.alpha_bits = ppf->info.bits_per_sample; 760 if (sigbits && sigbits->alpha != ppf->info.bits_per_sample) { 761 JXL_WARNING( 762 "sBIT chunk: bit depths for RGBA are inconsistent " 763 "(%i %i %i %i). Setting A bitdepth to %i.", 764 sigbits->red, sigbits->green, sigbits->blue, sigbits->alpha, 765 ppf->info.bits_per_sample); 766 } 767 } else { 768 ppf->info.alpha_bits = 0; 769 } 770 ppf->color_encoding.color_space = 771 (ppf->info.num_color_channels == 1 ? JXL_COLOR_SPACE_GRAY 772 : JXL_COLOR_SPACE_RGB); 773 ppf->info.xsize = w; 774 ppf->info.ysize = h; 775 JXL_RETURN_IF_ERROR(VerifyDimensions(constraints, w, h)); 776 num_channels = 777 ppf->info.num_color_channels + (ppf->info.alpha_bits ? 1 : 0); 778 format = { 779 /*num_channels=*/num_channels, 780 /*data_type=*/ppf->info.bits_per_sample > 8 ? JXL_TYPE_UINT16 781 : JXL_TYPE_UINT8, 782 /*endianness=*/JXL_BIG_ENDIAN, 783 /*align=*/0, 784 }; 785 if (png_bit_depth > 8 && format.data_type == JXL_TYPE_UINT8) { 786 png_set_strip_16(png_ptr); 787 } 788 bytes_per_pixel = 789 num_channels * (format.data_type == JXL_TYPE_UINT16 ? 2 : 1); 790 unsigned int rowbytes = w * bytes_per_pixel; 791 unsigned int imagesize = h * rowbytes; 792 frameRaw.pixels.resize(imagesize); 793 frameRaw.rows.resize(h); 794 for (unsigned int j = 0; j < h; j++) { 795 frameRaw.rows[j] = frameRaw.pixels.data() + j * rowbytes; 796 } 797 798 if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) { 799 break; 800 } 801 } else if (id == kId_fdAT && isAnimated) { 802 if (!hasInfo) { 803 return JXL_FAILURE("fDAT chunk before iDAT"); 804 } 805 png_save_uint_32(chunk.data() + 4, chunk.size() - 16); 806 memcpy(chunk.data() + 8, "IDAT", 4); 807 if (processing_data(png_ptr, info_ptr, chunk.data() + 4, 808 chunk.size() - 4)) { 809 break; 810 } 811 } else if (id == kId_cICP) { 812 // Color profile chunks: cICP has the highest priority, followed by 813 // iCCP and sRGB (which shouldn't co-exist, but if they do, we use 814 // iCCP), followed finally by gAMA and cHRM. 815 if (DecodeCICP(chunk.data() + 8, chunk.size() - 12, 816 &ppf->color_encoding)) { 817 have_cicp = true; 818 have_color = true; 819 ppf->icc.clear(); 820 ppf->primary_color_representation = 821 PackedPixelFile::kColorEncodingIsPrimary; 822 } 823 } else if (!have_cicp && id == kId_iCCP) { 824 if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) { 825 JXL_WARNING("Corrupt iCCP chunk"); 826 break; 827 } 828 829 // TODO(jon): catch special case of PQ and synthesize color encoding 830 // in that case 831 int compression_type; 832 png_bytep profile; 833 png_charp name; 834 png_uint_32 proflen = 0; 835 auto ok = png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, 836 &profile, &proflen); 837 if (ok && proflen) { 838 ppf->icc.assign(profile, profile + proflen); 839 ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; 840 have_color = true; 841 have_iccp = true; 842 } else { 843 // TODO(eustas): JXL_WARNING? 844 } 845 } else if (!have_cicp && !have_iccp && id == kId_sRGB) { 846 JXL_RETURN_IF_ERROR(DecodeSRGB(chunk.data() + 8, chunk.size() - 12, 847 &ppf->color_encoding)); 848 have_srgb = true; 849 have_color = true; 850 } else if (!have_cicp && !have_srgb && !have_iccp && id == kId_gAMA) { 851 JXL_RETURN_IF_ERROR(DecodeGAMA(chunk.data() + 8, chunk.size() - 12, 852 &ppf->color_encoding)); 853 have_color = true; 854 } else if (!have_cicp && !have_srgb && !have_iccp && id == kId_cHRM) { 855 JXL_RETURN_IF_ERROR(DecodeCHRM(chunk.data() + 8, chunk.size() - 12, 856 &ppf->color_encoding)); 857 have_color = true; 858 } else if (id == kId_eXIf) { 859 ppf->metadata.exif.resize(chunk.size() - 12); 860 memcpy(ppf->metadata.exif.data(), chunk.data() + 8, 861 chunk.size() - 12); 862 } else if (!isAbc(chunk[4]) || !isAbc(chunk[5]) || !isAbc(chunk[6]) || 863 !isAbc(chunk[7])) { 864 break; 865 } else { 866 if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) { 867 break; 868 } 869 if (!hasInfo) { 870 chunksInfo.push_back(chunk); 871 continue; 872 } 873 } 874 } 875 } 876 877 JXL_RETURN_IF_ERROR(ApplyColorHints( 878 color_hints, have_color, ppf->info.num_color_channels == 1, ppf)); 879 } 880 881 if (errorstate) return false; 882 883 bool has_nontrivial_background = false; 884 bool previous_frame_should_be_cleared = false; 885 enum { 886 DISPOSE_OP_NONE = 0, 887 DISPOSE_OP_BACKGROUND = 1, 888 DISPOSE_OP_PREVIOUS = 2, 889 }; 890 enum { 891 BLEND_OP_SOURCE = 0, 892 BLEND_OP_OVER = 1, 893 }; 894 for (size_t i = 0; i < frames.size(); i++) { 895 auto& frame = frames[i]; 896 JXL_ASSERT(frame.data.xsize == frame.xsize); 897 JXL_ASSERT(frame.data.ysize == frame.ysize); 898 899 // Before encountering a DISPOSE_OP_NONE frame, the canvas is filled with 0, 900 // so DISPOSE_OP_BACKGROUND and DISPOSE_OP_PREVIOUS are equivalent. 901 if (frame.dispose_op == DISPOSE_OP_NONE) { 902 has_nontrivial_background = true; 903 } 904 bool should_blend = frame.blend_op == BLEND_OP_OVER; 905 bool use_for_next_frame = 906 has_nontrivial_background && frame.dispose_op != DISPOSE_OP_PREVIOUS; 907 size_t x0 = frame.x0; 908 size_t y0 = frame.y0; 909 size_t xsize = frame.data.xsize; 910 size_t ysize = frame.data.ysize; 911 if (previous_frame_should_be_cleared) { 912 size_t px0 = frames[i - 1].x0; 913 size_t py0 = frames[i - 1].y0; 914 size_t pxs = frames[i - 1].xsize; 915 size_t pys = frames[i - 1].ysize; 916 if (px0 >= x0 && py0 >= y0 && px0 + pxs <= x0 + xsize && 917 py0 + pys <= y0 + ysize && frame.blend_op == BLEND_OP_SOURCE && 918 use_for_next_frame) { 919 // If the previous frame is entirely contained in the current frame and 920 // we are using BLEND_OP_SOURCE, nothing special needs to be done. 921 ppf->frames.emplace_back(std::move(frame.data)); 922 } else if (px0 == x0 && py0 == y0 && px0 + pxs == x0 + xsize && 923 py0 + pys == y0 + ysize && use_for_next_frame) { 924 // If the new frame has the same size as the old one, but we are 925 // blending, we can instead just not blend. 926 should_blend = false; 927 ppf->frames.emplace_back(std::move(frame.data)); 928 } else if (px0 <= x0 && py0 <= y0 && px0 + pxs >= x0 + xsize && 929 py0 + pys >= y0 + ysize && use_for_next_frame) { 930 // If the new frame is contained within the old frame, we can pad the 931 // new frame with zeros and not blend. 932 PackedImage new_data(pxs, pys, frame.data.format); 933 memset(new_data.pixels(), 0, new_data.pixels_size); 934 for (size_t y = 0; y < ysize; y++) { 935 size_t bytes_per_pixel = 936 PackedImage::BitsPerChannel(new_data.format.data_type) * 937 new_data.format.num_channels / 8; 938 memcpy(static_cast<uint8_t*>(new_data.pixels()) + 939 new_data.stride * (y + y0 - py0) + 940 bytes_per_pixel * (x0 - px0), 941 static_cast<const uint8_t*>(frame.data.pixels()) + 942 frame.data.stride * y, 943 xsize * bytes_per_pixel); 944 } 945 946 x0 = px0; 947 y0 = py0; 948 xsize = pxs; 949 ysize = pys; 950 should_blend = false; 951 ppf->frames.emplace_back(std::move(new_data)); 952 } else { 953 // If all else fails, insert a placeholder blank frame with kReplace. 954 PackedImage blank(pxs, pys, frame.data.format); 955 memset(blank.pixels(), 0, blank.pixels_size); 956 ppf->frames.emplace_back(std::move(blank)); 957 auto& pframe = ppf->frames.back(); 958 pframe.frame_info.layer_info.crop_x0 = px0; 959 pframe.frame_info.layer_info.crop_y0 = py0; 960 pframe.frame_info.layer_info.xsize = pxs; 961 pframe.frame_info.layer_info.ysize = pys; 962 pframe.frame_info.duration = 0; 963 bool is_full_size = px0 == 0 && py0 == 0 && pxs == ppf->info.xsize && 964 pys == ppf->info.ysize; 965 pframe.frame_info.layer_info.have_crop = is_full_size ? 0 : 1; 966 pframe.frame_info.layer_info.blend_info.blendmode = JXL_BLEND_REPLACE; 967 pframe.frame_info.layer_info.blend_info.source = 1; 968 pframe.frame_info.layer_info.save_as_reference = 1; 969 ppf->frames.emplace_back(std::move(frame.data)); 970 } 971 } else { 972 ppf->frames.emplace_back(std::move(frame.data)); 973 } 974 975 auto& pframe = ppf->frames.back(); 976 pframe.frame_info.layer_info.crop_x0 = x0; 977 pframe.frame_info.layer_info.crop_y0 = y0; 978 pframe.frame_info.layer_info.xsize = xsize; 979 pframe.frame_info.layer_info.ysize = ysize; 980 pframe.frame_info.duration = frame.duration; 981 pframe.frame_info.layer_info.blend_info.blendmode = 982 should_blend ? JXL_BLEND_BLEND : JXL_BLEND_REPLACE; 983 bool is_full_size = x0 == 0 && y0 == 0 && xsize == ppf->info.xsize && 984 ysize == ppf->info.ysize; 985 pframe.frame_info.layer_info.have_crop = is_full_size ? 0 : 1; 986 pframe.frame_info.layer_info.blend_info.source = 1; 987 pframe.frame_info.layer_info.blend_info.alpha = 0; 988 pframe.frame_info.layer_info.save_as_reference = use_for_next_frame ? 1 : 0; 989 990 previous_frame_should_be_cleared = 991 has_nontrivial_background && frame.dispose_op == DISPOSE_OP_BACKGROUND; 992 } 993 if (ppf->frames.empty()) return JXL_FAILURE("No frames decoded"); 994 ppf->frames.back().frame_info.is_last = JXL_TRUE; 995 996 return true; 997 #else 998 return false; 999 #endif 1000 } 1001 1002 } // namespace extras 1003 } // namespace jxl