Serenity Operating System
at portability 722 lines 23 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/ByteBuffer.h> 28#include <AK/FileSystemPath.h> 29#include <AK/MappedFile.h> 30#include <AK/NetworkOrdered.h> 31#include <LibCore/puff.h> 32#include <LibGfx/PNGLoader.h> 33#include <fcntl.h> 34#include <serenity.h> 35#include <stdio.h> 36#include <string.h> 37#include <sys/mman.h> 38#include <sys/stat.h> 39#include <unistd.h> 40 41namespace Gfx { 42 43static const u8 png_header[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 }; 44 45struct PNG_IHDR { 46 NetworkOrdered<u32> width; 47 NetworkOrdered<u32> height; 48 u8 bit_depth { 0 }; 49 u8 color_type { 0 }; 50 u8 compression_method { 0 }; 51 u8 filter_method { 0 }; 52 u8 interlace_method { 0 }; 53}; 54 55static_assert(sizeof(PNG_IHDR) == 13); 56 57struct Scanline { 58 u8 filter { 0 }; 59 ByteBuffer data {}; 60}; 61 62struct [[gnu::packed]] PaletteEntry 63{ 64 u8 r; 65 u8 g; 66 u8 b; 67 //u8 a; 68}; 69 70struct [[gnu::packed]] Triplet 71{ 72 u8 r; 73 u8 g; 74 u8 b; 75}; 76 77struct [[gnu::packed]] Triplet16 78{ 79 u16 r; 80 u16 g; 81 u16 b; 82}; 83 84struct [[gnu::packed]] Quad16 85{ 86 u16 r; 87 u16 g; 88 u16 b; 89 u16 a; 90}; 91 92struct PNGLoadingContext { 93 enum State { 94 NotDecoded = 0, 95 Error, 96 HeaderDecoded, 97 SizeDecoded, 98 ChunksDecoded, 99 BitmapDecoded, 100 }; 101 State state { State::NotDecoded }; 102 const u8* data { nullptr }; 103 size_t data_size { 0 }; 104 int width { -1 }; 105 int height { -1 }; 106 u8 bit_depth { 0 }; 107 u8 color_type { 0 }; 108 u8 compression_method { 0 }; 109 u8 filter_method { 0 }; 110 u8 interlace_method { 0 }; 111 u8 bytes_per_pixel { 0 }; 112 bool has_seen_zlib_header { false }; 113 bool has_alpha() const { return color_type & 4 || palette_transparency_data.size() > 0; } 114 Vector<Scanline> scanlines; 115 RefPtr<Gfx::Bitmap> bitmap; 116 u8* decompression_buffer { nullptr }; 117 int decompression_buffer_size { 0 }; 118 Vector<u8> compressed_data; 119 Vector<PaletteEntry> palette_data; 120 Vector<u8> palette_transparency_data; 121}; 122 123class Streamer { 124public: 125 Streamer(const u8* data, int size) 126 : m_original_data(data) 127 , m_original_size(size) 128 , m_data_ptr(data) 129 , m_size_remaining(size) 130 { 131 } 132 133 template<typename T> 134 bool read(T& value) 135 { 136 if (m_size_remaining < (int)sizeof(T)) 137 return false; 138 value = *((const NetworkOrdered<T>*)m_data_ptr); 139 m_data_ptr += sizeof(T); 140 m_size_remaining -= sizeof(T); 141 return true; 142 } 143 144 bool read_bytes(u8* buffer, int count) 145 { 146 if (m_size_remaining < count) 147 return false; 148 memcpy(buffer, m_data_ptr, count); 149 m_data_ptr += count; 150 m_size_remaining -= count; 151 return true; 152 } 153 154 bool wrap_bytes(ByteBuffer& buffer, int count) 155 { 156 if (m_size_remaining < count) 157 return false; 158 buffer = ByteBuffer::wrap(m_data_ptr, count); 159 m_data_ptr += count; 160 m_size_remaining -= count; 161 return true; 162 } 163 164 bool at_end() const { return !m_size_remaining; } 165 166private: 167 const u8* m_original_data; 168 int m_original_size; 169 const u8* m_data_ptr; 170 int m_size_remaining; 171}; 172 173static RefPtr<Gfx::Bitmap> load_png_impl(const u8*, int); 174static bool process_chunk(Streamer&, PNGLoadingContext& context, bool decode_size_only); 175 176RefPtr<Gfx::Bitmap> load_png(const StringView& path) 177{ 178 MappedFile mapped_file(path); 179 if (!mapped_file.is_valid()) 180 return nullptr; 181 auto bitmap = load_png_impl((const u8*)mapped_file.data(), mapped_file.size()); 182 if (bitmap) 183 bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded PNG: %s", bitmap->width(), bitmap->height(), canonicalized_path(path).characters())); 184 return bitmap; 185} 186 187RefPtr<Gfx::Bitmap> load_png_from_memory(const u8* data, size_t length) 188{ 189 auto bitmap = load_png_impl(data, length); 190 if (bitmap) 191 bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded PNG: <memory>", bitmap->width(), bitmap->height())); 192 return bitmap; 193} 194 195[[gnu::always_inline]] static inline u8 paeth_predictor(int a, int b, int c) 196{ 197 int p = a + b - c; 198 int pa = abs(p - a); 199 int pb = abs(p - b); 200 int pc = abs(p - c); 201 if (pa <= pb && pa <= pc) 202 return a; 203 if (pb <= pc) 204 return b; 205 return c; 206} 207 208union [[gnu::packed]] Pixel 209{ 210 RGBA32 rgba { 0 }; 211 u8 v[4]; 212 struct { 213 u8 r; 214 u8 g; 215 u8 b; 216 u8 a; 217 }; 218}; 219static_assert(sizeof(Pixel) == 4); 220 221template<bool has_alpha, u8 filter_type> 222[[gnu::always_inline]] static inline void unfilter_impl(Gfx::Bitmap& bitmap, int y, const void* dummy_scanline_data) 223{ 224 auto* dummy_scanline = (const Pixel*)dummy_scanline_data; 225 if constexpr (filter_type == 0) { 226 auto* pixels = (Pixel*)bitmap.scanline(y); 227 for (int i = 0; i < bitmap.width(); ++i) { 228 auto& x = pixels[i]; 229 swap(x.r, x.b); 230 } 231 } 232 233 if constexpr (filter_type == 1) { 234 auto* pixels = (Pixel*)bitmap.scanline(y); 235 swap(pixels[0].r, pixels[0].b); 236 for (int i = 1; i < bitmap.width(); ++i) { 237 auto& x = pixels[i]; 238 swap(x.r, x.b); 239 auto& a = (const Pixel&)pixels[i - 1]; 240 x.v[0] += a.v[0]; 241 x.v[1] += a.v[1]; 242 x.v[2] += a.v[2]; 243 if constexpr (has_alpha) 244 x.v[3] += a.v[3]; 245 } 246 return; 247 } 248 if constexpr (filter_type == 2) { 249 auto* pixels = (Pixel*)bitmap.scanline(y); 250 auto* pixels_y_minus_1 = y == 0 ? dummy_scanline : (Pixel*)bitmap.scanline(y - 1); 251 for (int i = 0; i < bitmap.width(); ++i) { 252 auto& x = pixels[i]; 253 swap(x.r, x.b); 254 const Pixel& b = pixels_y_minus_1[i]; 255 x.v[0] += b.v[0]; 256 x.v[1] += b.v[1]; 257 x.v[2] += b.v[2]; 258 if constexpr (has_alpha) 259 x.v[3] += b.v[3]; 260 } 261 return; 262 } 263 if constexpr (filter_type == 3) { 264 auto* pixels = (Pixel*)bitmap.scanline(y); 265 auto* pixels_y_minus_1 = y == 0 ? dummy_scanline : (Pixel*)bitmap.scanline(y - 1); 266 for (int i = 0; i < bitmap.width(); ++i) { 267 auto& x = pixels[i]; 268 swap(x.r, x.b); 269 Pixel a; 270 if (i != 0) 271 a = pixels[i - 1]; 272 const Pixel& b = pixels_y_minus_1[i]; 273 x.v[0] = x.v[0] + ((a.v[0] + b.v[0]) / 2); 274 x.v[1] = x.v[1] + ((a.v[1] + b.v[1]) / 2); 275 x.v[2] = x.v[2] + ((a.v[2] + b.v[2]) / 2); 276 if constexpr (has_alpha) 277 x.v[3] = x.v[3] + ((a.v[3] + b.v[3]) / 2); 278 } 279 return; 280 } 281 if constexpr (filter_type == 4) { 282 auto* pixels = (Pixel*)bitmap.scanline(y); 283 auto* pixels_y_minus_1 = y == 0 ? dummy_scanline : (Pixel*)bitmap.scanline(y - 1); 284 for (int i = 0; i < bitmap.width(); ++i) { 285 auto& x = pixels[i]; 286 swap(x.r, x.b); 287 Pixel a; 288 const Pixel& b = pixels_y_minus_1[i]; 289 Pixel c; 290 if (i != 0) { 291 a = pixels[i - 1]; 292 c = pixels_y_minus_1[i - 1]; 293 } 294 x.v[0] += paeth_predictor(a.v[0], b.v[0], c.v[0]); 295 x.v[1] += paeth_predictor(a.v[1], b.v[1], c.v[1]); 296 x.v[2] += paeth_predictor(a.v[2], b.v[2], c.v[2]); 297 if constexpr (has_alpha) 298 x.v[3] += paeth_predictor(a.v[3], b.v[3], c.v[3]); 299 } 300 } 301} 302 303[[gnu::noinline]] static void unfilter(PNGLoadingContext& context) 304{ 305 // First unpack the scanlines to RGBA: 306 switch (context.color_type) { 307 case 2: 308 if (context.bit_depth == 8) { 309 for (int y = 0; y < context.height; ++y) { 310 auto* triplets = (Triplet*)context.scanlines[y].data.data(); 311 for (int i = 0; i < context.width; ++i) { 312 auto& pixel = (Pixel&)context.bitmap->scanline(y)[i]; 313 pixel.r = triplets[i].r; 314 pixel.g = triplets[i].g; 315 pixel.b = triplets[i].b; 316 pixel.a = 0xff; 317 } 318 } 319 } else if (context.bit_depth == 16) { 320 for (int y = 0; y < context.height; ++y) { 321 auto* triplets = (Triplet16*)context.scanlines[y].data.data(); 322 for (int i = 0; i < context.width; ++i) { 323 auto& pixel = (Pixel&)context.bitmap->scanline(y)[i]; 324 pixel.r = triplets[i].r & 0xFF; 325 pixel.g = triplets[i].g & 0xFF; 326 pixel.b = triplets[i].b & 0xFF; 327 pixel.a = 0xff; 328 } 329 } 330 } else { 331 ASSERT_NOT_REACHED(); 332 } 333 break; 334 case 6: 335 if (context.bit_depth == 8) { 336 for (int y = 0; y < context.height; ++y) { 337 memcpy(context.bitmap->scanline(y), context.scanlines[y].data.data(), context.scanlines[y].data.size()); 338 } 339 } else if (context.bit_depth == 16) { 340 for (int y = 0; y < context.height; ++y) { 341 auto* triplets = (Quad16*)context.scanlines[y].data.data(); 342 for (int i = 0; i < context.width; ++i) { 343 auto& pixel = (Pixel&)context.bitmap->scanline(y)[i]; 344 pixel.r = triplets[i].r & 0xFF; 345 pixel.g = triplets[i].g & 0xFF; 346 pixel.b = triplets[i].b & 0xFF; 347 pixel.a = triplets[i].a & 0xFF; 348 } 349 } 350 } else { 351 ASSERT_NOT_REACHED(); 352 } 353 break; 354 case 3: 355 for (int y = 0; y < context.height; ++y) { 356 auto* palette_index = (u8*)context.scanlines[y].data.data(); 357 for (int i = 0; i < context.width; ++i) { 358 auto& pixel = (Pixel&)context.bitmap->scanline(y)[i]; 359 auto& color = context.palette_data.at((int)palette_index[i]); 360 auto transparency = context.palette_transparency_data.size() >= palette_index[i] + 1u 361 ? context.palette_transparency_data.data()[palette_index[i]] 362 : 0xff; 363 pixel.r = color.r; 364 pixel.g = color.g; 365 pixel.b = color.b; 366 pixel.a = transparency; 367 } 368 } 369 break; 370 default: 371 ASSERT_NOT_REACHED(); 372 break; 373 } 374 375 auto dummy_scanline = ByteBuffer::create_zeroed(context.width * sizeof(RGBA32)); 376 377 for (int y = 0; y < context.height; ++y) { 378 auto filter = context.scanlines[y].filter; 379 if (filter == 0) { 380 if (context.has_alpha()) 381 unfilter_impl<true, 0>(*context.bitmap, y, dummy_scanline.data()); 382 else 383 unfilter_impl<false, 0>(*context.bitmap, y, dummy_scanline.data()); 384 continue; 385 } 386 if (filter == 1) { 387 if (context.has_alpha()) 388 unfilter_impl<true, 1>(*context.bitmap, y, dummy_scanline.data()); 389 else 390 unfilter_impl<false, 1>(*context.bitmap, y, dummy_scanline.data()); 391 continue; 392 } 393 if (filter == 2) { 394 if (context.has_alpha()) 395 unfilter_impl<true, 2>(*context.bitmap, y, dummy_scanline.data()); 396 else 397 unfilter_impl<false, 2>(*context.bitmap, y, dummy_scanline.data()); 398 continue; 399 } 400 if (filter == 3) { 401 if (context.has_alpha()) 402 unfilter_impl<true, 3>(*context.bitmap, y, dummy_scanline.data()); 403 else 404 unfilter_impl<false, 3>(*context.bitmap, y, dummy_scanline.data()); 405 continue; 406 } 407 if (filter == 4) { 408 if (context.has_alpha()) 409 unfilter_impl<true, 4>(*context.bitmap, y, dummy_scanline.data()); 410 else 411 unfilter_impl<false, 4>(*context.bitmap, y, dummy_scanline.data()); 412 continue; 413 } 414 } 415} 416 417static bool decode_png_header(PNGLoadingContext& context) 418{ 419 if (context.state >= PNGLoadingContext::HeaderDecoded) 420 return true; 421 422 if (memcmp(context.data, png_header, sizeof(png_header)) != 0) { 423 dbg() << "Invalid PNG header"; 424 context.state = PNGLoadingContext::State::Error; 425 return false; 426 } 427 428 context.state = PNGLoadingContext::HeaderDecoded; 429 return true; 430} 431 432static bool decode_png_size(PNGLoadingContext& context) 433{ 434 if (context.state >= PNGLoadingContext::SizeDecoded) 435 return true; 436 437 if (context.state < PNGLoadingContext::HeaderDecoded) { 438 if (!decode_png_header(context)) 439 return false; 440 } 441 442 const u8* data_ptr = context.data + sizeof(png_header); 443 size_t data_remaining = context.data_size - sizeof(png_header); 444 445 Streamer streamer(data_ptr, data_remaining); 446 while (!streamer.at_end()) { 447 if (!process_chunk(streamer, context, true)) { 448 context.state = PNGLoadingContext::State::Error; 449 return false; 450 } 451 if (context.width && context.height) { 452 context.state = PNGLoadingContext::State::SizeDecoded; 453 return true; 454 } 455 } 456 457 return false; 458} 459 460static bool decode_png_chunks(PNGLoadingContext& context) 461{ 462 if (context.state >= PNGLoadingContext::State::ChunksDecoded) 463 return true; 464 465 if (context.state < PNGLoadingContext::HeaderDecoded) { 466 if (!decode_png_header(context)) 467 return false; 468 } 469 470 const u8* data_ptr = context.data + sizeof(png_header); 471 int data_remaining = context.data_size - sizeof(png_header); 472 473 context.compressed_data.ensure_capacity(context.data_size); 474 475 Streamer streamer(data_ptr, data_remaining); 476 while (!streamer.at_end()) { 477 if (!process_chunk(streamer, context, false)) { 478 context.state = PNGLoadingContext::State::Error; 479 return false; 480 } 481 } 482 483 context.state = PNGLoadingContext::State::ChunksDecoded; 484 return true; 485} 486 487static bool decode_png_bitmap(PNGLoadingContext& context) 488{ 489 if (context.state < PNGLoadingContext::State::ChunksDecoded) { 490 if (!decode_png_chunks(context)) 491 return false; 492 } 493 494 if (context.state >= PNGLoadingContext::State::BitmapDecoded) 495 return true; 496 497 unsigned long srclen = context.compressed_data.size() - 6; 498 unsigned long destlen = context.decompression_buffer_size; 499 int ret = puff(context.decompression_buffer, &destlen, context.compressed_data.data() + 2, &srclen); 500 if (ret < 0) { 501 context.state = PNGLoadingContext::State::Error; 502 return false; 503 } 504 context.compressed_data.clear(); 505 506 context.scanlines.ensure_capacity(context.height); 507 Streamer streamer(context.decompression_buffer, context.decompression_buffer_size); 508 for (int y = 0; y < context.height; ++y) { 509 u8 filter; 510 if (!streamer.read(filter)) { 511 context.state = PNGLoadingContext::State::Error; 512 return false; 513 } 514 515 context.scanlines.append({ filter }); 516 auto& scanline_buffer = context.scanlines.last().data; 517 if (!streamer.wrap_bytes(scanline_buffer, context.width * context.bytes_per_pixel)) { 518 context.state = PNGLoadingContext::State::Error; 519 return false; 520 } 521 } 522 523 context.bitmap = Bitmap::create_purgeable(context.has_alpha() ? BitmapFormat::RGBA32 : BitmapFormat::RGB32, { context.width, context.height }); 524 525 unfilter(context); 526 527 munmap(context.decompression_buffer, context.decompression_buffer_size); 528 context.decompression_buffer = nullptr; 529 context.decompression_buffer_size = 0; 530 531 context.state = PNGLoadingContext::State::BitmapDecoded; 532 return true; 533} 534 535static RefPtr<Gfx::Bitmap> load_png_impl(const u8* data, int data_size) 536{ 537 PNGLoadingContext context; 538 context.data = data; 539 context.data_size = data_size; 540 541 if (!decode_png_chunks(context)) 542 return nullptr; 543 544 if (!decode_png_bitmap(context)) 545 return nullptr; 546 547 return context.bitmap; 548} 549 550static bool process_IHDR(const ByteBuffer& data, PNGLoadingContext& context, bool decode_size_only = false) 551{ 552 if (data.size() < (int)sizeof(PNG_IHDR)) 553 return false; 554 auto& ihdr = *(const PNG_IHDR*)data.data(); 555 context.width = ihdr.width; 556 context.height = ihdr.height; 557 context.bit_depth = ihdr.bit_depth; 558 context.color_type = ihdr.color_type; 559 context.compression_method = ihdr.compression_method; 560 context.filter_method = ihdr.filter_method; 561 context.interlace_method = ihdr.interlace_method; 562 563#ifdef PNG_DEBUG 564 printf("PNG: %dx%d (%d bpp)\n", context.width, context.height, context.bit_depth); 565 printf(" Color type: %d\n", context.color_type); 566 printf("Compress Method: %d\n", context.compression_method); 567 printf(" Filter Method: %d\n", context.filter_method); 568 printf(" Interlace type: %d\n", context.interlace_method); 569#endif 570 571 // FIXME: Implement Adam7 deinterlacing 572 if (context.interlace_method != 0) { 573 dbgprintf("PNGLoader::process_IHDR: Interlaced PNGs not currently supported.\n"); 574 return false; 575 } 576 577 switch (context.color_type) { 578 case 0: // Each pixel is a grayscale sample. 579 case 4: // Each pixel is a grayscale sample, followed by an alpha sample. 580 // FIXME: Implement grayscale PNG support. 581 dbgprintf("PNGLoader::process_IHDR: Unsupported grayscale format.\n"); 582 return false; 583 case 2: 584 context.bytes_per_pixel = 3 * (ihdr.bit_depth / 8); 585 break; 586 case 3: // Each pixel is a palette index; a PLTE chunk must appear. 587 // FIXME: Implement support for 1/2/4 bit palette based images. 588 if (ihdr.bit_depth != 8) { 589 dbgprintf("PNGLoader::process_IHDR: Unsupported index-based format (%d bpp).\n", context.bit_depth); 590 return false; 591 } 592 context.bytes_per_pixel = 1; 593 break; 594 case 6: 595 context.bytes_per_pixel = 4 * (ihdr.bit_depth / 8); 596 break; 597 default: 598 ASSERT_NOT_REACHED(); 599 } 600 601 if (!decode_size_only) { 602 context.decompression_buffer_size = (context.width * context.height * context.bytes_per_pixel + context.height); 603 context.decompression_buffer = (u8*)mmap_with_name(nullptr, context.decompression_buffer_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0, "PNG decompression buffer"); 604 } 605 return true; 606} 607 608static bool process_IDAT(const ByteBuffer& data, PNGLoadingContext& context) 609{ 610 context.compressed_data.append(data.data(), data.size()); 611 return true; 612} 613 614static bool process_PLTE(const ByteBuffer& data, PNGLoadingContext& context) 615{ 616 context.palette_data.append((const PaletteEntry*)data.data(), data.size() / 3); 617 return true; 618} 619 620static bool process_tRNS(const ByteBuffer& data, PNGLoadingContext& context) 621{ 622 switch (context.color_type) { 623 case 3: 624 context.palette_transparency_data.append(data.data(), data.size()); 625 break; 626 } 627 return true; 628} 629 630static bool process_chunk(Streamer& streamer, PNGLoadingContext& context, bool decode_size_only) 631{ 632 u32 chunk_size; 633 if (!streamer.read(chunk_size)) { 634 printf("Bail at chunk_size\n"); 635 return false; 636 } 637 u8 chunk_type[5]; 638 chunk_type[4] = '\0'; 639 if (!streamer.read_bytes(chunk_type, 4)) { 640 printf("Bail at chunk_type\n"); 641 return false; 642 } 643 ByteBuffer chunk_data; 644 if (!streamer.wrap_bytes(chunk_data, chunk_size)) { 645 printf("Bail at chunk_data\n"); 646 return false; 647 } 648 u32 chunk_crc; 649 if (!streamer.read(chunk_crc)) { 650 printf("Bail at chunk_crc\n"); 651 return false; 652 } 653#ifdef PNG_DEBUG 654 printf("Chunk type: '%s', size: %u, crc: %x\n", chunk_type, chunk_size, chunk_crc); 655#endif 656 657 if (!strcmp((const char*)chunk_type, "IHDR")) 658 return process_IHDR(chunk_data, context, decode_size_only); 659 if (!strcmp((const char*)chunk_type, "IDAT")) 660 return process_IDAT(chunk_data, context); 661 if (!strcmp((const char*)chunk_type, "PLTE")) 662 return process_PLTE(chunk_data, context); 663 if (!strcmp((const char*)chunk_type, "tRNS")) 664 return process_tRNS(chunk_data, context); 665 return true; 666} 667 668PNGImageDecoderPlugin::PNGImageDecoderPlugin(const u8* data, size_t size) 669{ 670 m_context = make<PNGLoadingContext>(); 671 m_context->data = data; 672 m_context->data_size = size; 673} 674 675PNGImageDecoderPlugin::~PNGImageDecoderPlugin() 676{ 677} 678 679Size PNGImageDecoderPlugin::size() 680{ 681 if (m_context->state == PNGLoadingContext::State::Error) 682 return {}; 683 684 if (m_context->state < PNGLoadingContext::State::SizeDecoded) { 685 bool success = decode_png_size(*m_context); 686 if (!success) 687 return {}; 688 } 689 690 return { m_context->width, m_context->height }; 691} 692 693RefPtr<Gfx::Bitmap> PNGImageDecoderPlugin::bitmap() 694{ 695 if (m_context->state == PNGLoadingContext::State::Error) 696 return nullptr; 697 698 if (m_context->state < PNGLoadingContext::State::BitmapDecoded) { 699 // NOTE: This forces the chunk decoding to happen. 700 bool success = decode_png_bitmap(*m_context); 701 if (!success) 702 return nullptr; 703 } 704 705 ASSERT(m_context->bitmap); 706 return m_context->bitmap; 707} 708 709void PNGImageDecoderPlugin::set_volatile() 710{ 711 if (m_context->bitmap) 712 m_context->bitmap->set_volatile(); 713} 714 715bool PNGImageDecoderPlugin::set_nonvolatile() 716{ 717 if (!m_context->bitmap) 718 return false; 719 return m_context->bitmap->set_nonvolatile(); 720} 721 722}