OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkBitmap.h" | 8 #include "SkBitmap.h" |
9 #include "SkCodecPriv.h" | 9 #include "SkCodecPriv.h" |
10 #include "SkColorPriv.h" | 10 #include "SkColorPriv.h" |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
80 fPng_ptr = nullptr; | 80 fPng_ptr = nullptr; |
81 fInfo_ptr = nullptr; | 81 fInfo_ptr = nullptr; |
82 } | 82 } |
83 | 83 |
84 private: | 84 private: |
85 png_structp fPng_ptr; | 85 png_structp fPng_ptr; |
86 png_infop fInfo_ptr; | 86 png_infop fInfo_ptr; |
87 }; | 87 }; |
88 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) | 88 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) |
89 | 89 |
90 // Note: SkColorTable claims to store SkPMColors, which is not necessarily | 90 // Note: SkColorTable claims to store SkPMColors, which is not necessarily the c
ase here. |
91 // the case here. | |
92 // TODO: If we add support for non-native swizzles, we'll need to handle that he
re. | |
93 bool SkPngCodec::createColorTable(SkColorType dstColorType, bool premultiply, in
t* ctableCount) { | 91 bool SkPngCodec::createColorTable(SkColorType dstColorType, bool premultiply, in
t* ctableCount) { |
94 | 92 |
95 int numColors; | 93 int numColors; |
96 png_color* palette; | 94 png_color* palette; |
97 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { | 95 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { |
98 return false; | 96 return false; |
99 } | 97 } |
100 | 98 |
101 // Note: These are not necessarily SkPMColors. | 99 // Note: These are not necessarily SkPMColors. |
102 SkPMColor colorPtr[256]; | 100 SkPMColor colorPtr[256]; |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
329 // implemented to guess sRGB in this case. | 327 // implemented to guess sRGB in this case. |
330 return nullptr; | 328 return nullptr; |
331 } | 329 } |
332 | 330 |
333 static int bytes_per_pixel(int bitsPerPixel) { | 331 static int bytes_per_pixel(int bitsPerPixel) { |
334 // Note that we will have to change this implementation if we start | 332 // Note that we will have to change this implementation if we start |
335 // supporting outputs from libpng that are less than 8-bits per component. | 333 // supporting outputs from libpng that are less than 8-bits per component. |
336 return bitsPerPixel / 8; | 334 return bitsPerPixel / 8; |
337 } | 335 } |
338 | 336 |
339 // Subclass of SkPngCodec which supports scanline decoding | 337 static bool png_conversion_possible(const SkImageInfo& dst, const SkImageInfo& s
rc) { |
340 class SkPngScanlineDecoder : public SkPngCodec { | 338 // Ensure the alpha type is valid |
| 339 if (!valid_alpha(dst.alphaType(), src.alphaType())) { |
| 340 return false; |
| 341 } |
| 342 |
| 343 // Check for supported color types |
| 344 switch (dst.colorType()) { |
| 345 case kRGBA_8888_SkColorType: |
| 346 case kBGRA_8888_SkColorType: |
| 347 case kRGBA_F16_SkColorType: |
| 348 return true; |
| 349 case kRGB_565_SkColorType: |
| 350 return kOpaque_SkAlphaType == src.alphaType(); |
| 351 default: |
| 352 return dst.colorType() == src.colorType(); |
| 353 } |
| 354 } |
| 355 |
| 356 void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) { |
| 357 const int width = this->getInfo().width(); |
| 358 size_t colorXformBytes = fColorXform ? |
| 359 width * sizeof(SkColorTypeBytesPerPixel(dstInfo.colorType())) : 0; |
| 360 |
| 361 fStorage.reset(fSrcRowBytes + colorXformBytes); |
| 362 fSwizzlerSrcRow = fStorage.get(); |
| 363 fColorXformSrcRow = fColorXform ? SkTAddOffset<uint32_t>(fSwizzlerSrcRow, fS
rcRowBytes) : 0; |
| 364 } |
| 365 |
| 366 class SkPngNormalCodec : public SkPngCodec { |
341 public: | 367 public: |
342 SkPngScanlineDecoder(int width, int height, const SkEncodedInfo& info, SkStr
eam* stream, | 368 SkPngNormalCodec(int width, int height, const SkEncodedInfo& info, SkStream*
stream, |
343 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p
tr, int bitDepth, | 369 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p
tr, int bitDepth, |
344 sk_sp<SkColorSpace> colorSpace) | 370 sk_sp<SkColorSpace> colorSpace) |
345 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, 1, | 371 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, 1, |
346 colorSpace) | 372 colorSpace) |
347 , fSrcRow(nullptr) | |
348 {} | 373 {} |
349 | 374 |
350 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | 375 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, |
351 SkPMColor ctable[], int* ctableCount) override { | 376 SkPMColor ctable[], int* ctableCount) override { |
352 if (!conversion_possible(dstInfo, this->getInfo())) { | 377 if (!png_conversion_possible(dstInfo, this->getInfo()) || |
| 378 !this->initializeXforms(dstInfo, options, ctable, ctableCount)) |
| 379 { |
353 return kInvalidConversion; | 380 return kInvalidConversion; |
354 } | 381 } |
355 | 382 |
356 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | 383 this->allocateStorage(dstInfo); |
357 ctableCount); | |
358 if (result != kSuccess) { | |
359 return result; | |
360 } | |
361 | |
362 fStorage.reset(this->getInfo().width() * | |
363 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()))); | |
364 fSrcRow = fStorage.get(); | |
365 | |
366 return kSuccess; | 384 return kSuccess; |
367 } | 385 } |
368 | 386 |
369 int onGetScanlines(void* dst, int count, size_t rowBytes) override { | 387 int readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int cou
nt, int startRow) |
| 388 override { |
| 389 SkASSERT(0 == startRow); |
| 390 |
370 // Assume that an error in libpng indicates an incomplete input. | 391 // Assume that an error in libpng indicates an incomplete input. |
371 int row = 0; | 392 int y = 0; |
372 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 393 if (setjmp(png_jmpbuf(fPng_ptr))) { |
373 SkCodecPrintf("setjmp long jump!\n"); | 394 SkCodecPrintf("Failed to read row.\n"); |
374 return row; | 395 return y; |
375 } | 396 } |
376 | 397 |
377 void* dstRow = dst; | 398 void* swizzlerDstRow = dst; |
378 for (; row < count; row++) { | 399 size_t swizzlerDstRowBytes = rowBytes; |
379 png_read_row(this->png_ptr(), fSrcRow, nullptr); | 400 if (fColorXform) { |
380 this->swizzler()->swizzle(dstRow, fSrcRow); | 401 swizzlerDstRow = fColorXformSrcRow; |
381 dstRow = SkTAddOffset<void>(dstRow, rowBytes); | 402 swizzlerDstRowBytes = 0; |
382 } | 403 } |
383 | 404 |
384 return row; | 405 bool needsPremul = kUnpremul_SkAlphaType == this->getInfo().alphaType()
&& |
| 406 kPremul_SkAlphaType == dstInfo.alphaType(); |
| 407 for (; y < count; y++) { |
| 408 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); |
| 409 fSwizzler->swizzle(swizzlerDstRow, fSwizzlerSrcRow); |
| 410 |
| 411 if (fColorXform) { |
| 412 fColorXform->apply(dst, (const uint32_t*) swizzlerDstRow, dstInf
o.width(), |
| 413 dstInfo.colorType(), needsPremul); |
| 414 dst = SkTAddOffset<void>(dst, rowBytes); |
| 415 } |
| 416 |
| 417 swizzlerDstRow = SkTAddOffset<void>(swizzlerDstRow, swizzlerDstRowBy
tes); |
| 418 } |
| 419 |
| 420 return y; |
| 421 } |
| 422 |
| 423 int onGetScanlines(void* dst, int count, size_t rowBytes) override { |
| 424 return this->readRows(this->dstInfo(), dst, rowBytes, count, 0); |
385 } | 425 } |
386 | 426 |
387 bool onSkipScanlines(int count) override { | 427 bool onSkipScanlines(int count) override { |
388 // Assume that an error in libpng indicates an incomplete input. | 428 if (setjmp(png_jmpbuf(fPng_ptr))) { |
389 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 429 SkCodecPrintf("Failed to skip row.\n"); |
390 SkCodecPrintf("setjmp long jump!\n"); | |
391 return false; | 430 return false; |
392 } | 431 } |
393 | 432 |
394 for (int row = 0; row < count; row++) { | 433 for (int row = 0; row < count; row++) { |
395 png_read_row(this->png_ptr(), fSrcRow, nullptr); | 434 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); |
396 } | 435 } |
397 return true; | 436 return true; |
398 } | 437 } |
399 | 438 |
400 private: | |
401 SkAutoTMalloc<uint8_t> fStorage; | |
402 uint8_t* fSrcRow; | |
403 | |
404 typedef SkPngCodec INHERITED; | 439 typedef SkPngCodec INHERITED; |
405 }; | 440 }; |
406 | 441 |
407 | 442 |
408 class SkPngInterlacedScanlineDecoder : public SkPngCodec { | 443 class SkPngInterlacedCodec : public SkPngCodec { |
409 public: | 444 public: |
410 SkPngInterlacedScanlineDecoder(int width, int height, const SkEncodedInfo& i
nfo, | 445 SkPngInterlacedCodec(int width, int height, const SkEncodedInfo& info, |
411 SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr
, | 446 SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr
, |
412 png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpa
ce> colorSpace) | 447 png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpa
ce> colorSpace) |
413 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, | 448 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, |
414 numberPasses, colorSpace) | 449 numberPasses, colorSpace) |
415 , fHeight(-1) | |
416 , fCanSkipRewind(false) | 450 , fCanSkipRewind(false) |
417 { | 451 { |
418 SkASSERT(numberPasses != 1); | 452 SkASSERT(numberPasses != 1); |
419 } | 453 } |
420 | 454 |
421 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | 455 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, |
422 SkPMColor ctable[], int* ctableCount) override { | 456 SkPMColor ctable[], int* ctableCount) override { |
423 if (!conversion_possible(dstInfo, this->getInfo())) { | 457 if (!png_conversion_possible(dstInfo, this->getInfo()) || |
| 458 !this->initializeXforms(dstInfo, options, ctable, ctableCount)) |
| 459 { |
424 return kInvalidConversion; | 460 return kInvalidConversion; |
425 } | 461 } |
426 | 462 |
427 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | 463 this->allocateStorage(dstInfo); |
428 ctableCount); | |
429 if (result != kSuccess) { | |
430 return result; | |
431 } | |
432 | |
433 fHeight = dstInfo.height(); | |
434 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. | |
435 fSrcRowBytes = this->getInfo().width() * | |
436 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel())); | |
437 fGarbageRow.reset(fSrcRowBytes); | |
438 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); | |
439 fCanSkipRewind = true; | 464 fCanSkipRewind = true; |
440 | |
441 return SkCodec::kSuccess; | 465 return SkCodec::kSuccess; |
442 } | 466 } |
443 | 467 |
444 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override { | 468 int readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int cou
nt, int startRow) |
| 469 override { |
| 470 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 471 SkCodecPrintf("Failed to get scanlines.\n"); |
| 472 // FIXME (msarett): Returning 0 is pessimistic. If we can complete
a single pass, |
| 473 // we may be able to report that all of the memory has been initiali
zed. Even if we |
| 474 // fail on the first pass, we can still report than some scanlines a
re initialized. |
| 475 return 0; |
| 476 } |
| 477 |
| 478 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); |
| 479 uint8_t* srcRow; |
| 480 for (int i = 0; i < fNumberPasses; i++) { |
| 481 // Discard rows that we planned to skip. |
| 482 for (int y = 0; y < startRow; y++){ |
| 483 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); |
| 484 } |
| 485 // Read rows we care about into storage. |
| 486 srcRow = storage.get(); |
| 487 for (int y = 0; y < count; y++) { |
| 488 png_read_row(fPng_ptr, srcRow, nullptr); |
| 489 srcRow += fSrcRowBytes; |
| 490 } |
| 491 // Discard rows that we don't need. |
| 492 for (int y = 0; y < this->getInfo().height() - startRow - count; y++
) { |
| 493 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); |
| 494 } |
| 495 } |
| 496 |
| 497 // Swizzle and xform the rows we care about |
| 498 void* swizzlerDstRow = dst; |
| 499 size_t swizzlerDstRowBytes = rowBytes; |
| 500 if (fColorXform) { |
| 501 swizzlerDstRow = fColorXformSrcRow; |
| 502 swizzlerDstRowBytes = 0; |
| 503 } |
| 504 |
| 505 bool needsPremul = kUnpremul_SkAlphaType == this->getInfo().alphaType()
&& |
| 506 kPremul_SkAlphaType == dstInfo.alphaType(); |
| 507 srcRow = storage.get(); |
| 508 for (int y = 0; y < count; y++) { |
| 509 fSwizzler->swizzle(swizzlerDstRow, srcRow); |
| 510 srcRow = SkTAddOffset<uint8_t>(srcRow, fSrcRowBytes); |
| 511 |
| 512 if (fColorXform) { |
| 513 if (fColorXform) { |
| 514 fColorXform->apply(dst, (const uint32_t*) swizzlerDstRow, ds
tInfo.width(), |
| 515 dstInfo.colorType(), needsPremul); |
| 516 dst = SkTAddOffset<void>(dst, rowBytes); |
| 517 } |
| 518 } |
| 519 |
| 520 swizzlerDstRow = SkTAddOffset<void>(swizzlerDstRow, swizzlerDstRowBy
tes); |
| 521 } |
| 522 |
| 523 return count; |
| 524 } |
| 525 |
| 526 int onGetScanlines(void* dst, int count, size_t rowBytes) override { |
445 // rewind stream if have previously called onGetScanlines, | 527 // rewind stream if have previously called onGetScanlines, |
446 // since we need entire progressive image to get scanlines | 528 // since we need entire progressive image to get scanlines |
447 if (fCanSkipRewind) { | 529 if (fCanSkipRewind) { |
448 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. | 530 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. |
449 // Next time onGetScanlines is called, we will need to rewind. | 531 // Next time onGetScanlines is called, we will need to rewind. |
450 fCanSkipRewind = false; | 532 fCanSkipRewind = false; |
451 } else { | 533 } else { |
452 // rewindIfNeeded resets fCurrScanline, since it assumes that start | 534 // rewindIfNeeded resets fCurrScanline, since it assumes that start |
453 // needs to be called again before scanline decoding. PNG scanline | 535 // needs to be called again before scanline decoding. PNG scanline |
454 // decoding is the exception, since it needs to rewind between | 536 // decoding is the exception, since it needs to rewind between |
455 // calls to getScanlines. Keep track of fCurrScanline, to undo the | 537 // calls to getScanlines. Keep track of fCurrScanline, to undo the |
456 // reset. | 538 // reset. |
457 const int currScanline = this->nextScanline(); | 539 const int currScanline = this->nextScanline(); |
458 // This method would never be called if currScanline is -1 | 540 // This method would never be called if currScanline is -1 |
459 SkASSERT(currScanline != -1); | 541 SkASSERT(currScanline != -1); |
460 | 542 |
461 if (!this->rewindIfNeeded()) { | 543 if (!this->rewindIfNeeded()) { |
462 return kCouldNotRewind; | 544 return kCouldNotRewind; |
463 } | 545 } |
464 this->updateCurrScanline(currScanline); | 546 this->updateCurrScanline(currScanline); |
465 } | 547 } |
466 | 548 |
467 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 549 return this->readRows(this->dstInfo(), dst, rowBytes, count, this->nextS
canline()); |
468 SkCodecPrintf("setjmp long jump!\n"); | |
469 // FIXME (msarett): Returning 0 is pessimistic. If we can complete
a single pass, | |
470 // we may be able to report that all of the memory has been initiali
zed. Even if we | |
471 // fail on the first pass, we can still report than some scanlines a
re initialized. | |
472 return 0; | |
473 } | |
474 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); | |
475 uint8_t* storagePtr = storage.get(); | |
476 uint8_t* srcRow; | |
477 const int startRow = this->nextScanline(); | |
478 for (int i = 0; i < this->numberPasses(); i++) { | |
479 // read rows we planned to skip into garbage row | |
480 for (int y = 0; y < startRow; y++){ | |
481 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); | |
482 } | |
483 // read rows we care about into buffer | |
484 srcRow = storagePtr; | |
485 for (int y = 0; y < count; y++) { | |
486 png_read_row(this->png_ptr(), srcRow, nullptr); | |
487 srcRow += fSrcRowBytes; | |
488 } | |
489 // read rows we don't want into garbage buffer | |
490 for (int y = 0; y < fHeight - startRow - count; y++) { | |
491 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); | |
492 } | |
493 } | |
494 //swizzle the rows we care about | |
495 srcRow = storagePtr; | |
496 void* dstRow = dst; | |
497 for (int y = 0; y < count; y++) { | |
498 this->swizzler()->swizzle(dstRow, srcRow); | |
499 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
500 srcRow += fSrcRowBytes; | |
501 } | |
502 | |
503 return count; | |
504 } | 550 } |
505 | 551 |
506 bool onSkipScanlines(int count) override { | 552 bool onSkipScanlines(int count) override { |
507 // The non-virtual version will update fCurrScanline. | 553 // The non-virtual version will update fCurrScanline. |
508 return true; | 554 return true; |
509 } | 555 } |
510 | 556 |
511 SkScanlineOrder onGetScanlineOrder() const override { | 557 SkScanlineOrder onGetScanlineOrder() const override { |
512 return kNone_SkScanlineOrder; | 558 return kNone_SkScanlineOrder; |
513 } | 559 } |
514 | 560 |
515 private: | 561 private: |
516 int fHeight; | |
517 size_t fSrcRowBytes; | |
518 SkAutoMalloc fGarbageRow; | |
519 uint8_t* fGarbageRowPtr; | |
520 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function | 562 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function |
521 // is called whenever some action is taken that reads the stream and | 563 // is called whenever some action is taken that reads the stream and |
522 // therefore the next call will require a rewind. So it modifies a boolean | 564 // therefore the next call will require a rewind. So it modifies a boolean |
523 // to note that the *next* time it is called a rewind is needed. | 565 // to note that the *next* time it is called a rewind is needed. |
524 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling | 566 // SkPngInterlacedCodec has an extra wrinkle - calling |
525 // onStartScanlineDecode followed by onGetScanlines does *not* require a | 567 // onStartScanlineDecode followed by onGetScanlines does *not* require a |
526 // rewind. Since rewindIfNeeded does not have this flexibility, we need to | 568 // rewind. Since rewindIfNeeded does not have this flexibility, we need to |
527 // add another layer. | 569 // add another layer. |
528 bool fCanSkipRewind; | 570 bool fCanSkipRewind; |
529 | 571 |
530 typedef SkPngCodec INHERITED; | 572 typedef SkPngCodec INHERITED; |
531 }; | 573 }; |
532 | 574 |
533 // Reads the header and initializes the output fields, if not NULL. | 575 // Reads the header and initializes the output fields, if not NULL. |
534 // | 576 // |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
672 if (outCodec) { | 714 if (outCodec) { |
673 sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr); | 715 sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr); |
674 if (!colorSpace) { | 716 if (!colorSpace) { |
675 // Treat unmarked pngs as sRGB. | 717 // Treat unmarked pngs as sRGB. |
676 colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); | 718 colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); |
677 } | 719 } |
678 | 720 |
679 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); | 721 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); |
680 | 722 |
681 if (1 == numberPasses) { | 723 if (1 == numberPasses) { |
682 *outCodec = new SkPngScanlineDecoder(origWidth, origHeight, info, st
ream, | 724 *outCodec = new SkPngNormalCodec(origWidth, origHeight, info, stream
, |
683 chunkReader, png_ptr, info_ptr, bitDepth, colorSpace); | 725 chunkReader, png_ptr, info_ptr, bitDepth, colorSpace); |
684 } else { | 726 } else { |
685 *outCodec = new SkPngInterlacedScanlineDecoder(origWidth, origHeight
, info, stream, | 727 *outCodec = new SkPngInterlacedCodec(origWidth, origHeight, info, st
ream, |
686 chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colo
rSpace); | 728 chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colo
rSpace); |
687 } | 729 } |
688 } | 730 } |
689 | 731 |
690 return true; | 732 return true; |
691 } | 733 } |
692 | 734 |
693 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, | 735 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, |
694 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i
nfop info_ptr, | 736 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i
nfop info_ptr, |
695 int bitDepth, int numberPasses, sk_sp<SkColorSpace> color
Space) | 737 int bitDepth, int numberPasses, sk_sp<SkColorSpace> color
Space) |
696 : INHERITED(width, height, info, stream, colorSpace) | 738 : INHERITED(width, height, info, stream, colorSpace) |
697 , fPngChunkReader(SkSafeRef(chunkReader)) | 739 , fPngChunkReader(SkSafeRef(chunkReader)) |
698 , fPng_ptr(png_ptr) | 740 , fPng_ptr(png_ptr) |
699 , fInfo_ptr(info_ptr) | 741 , fInfo_ptr(info_ptr) |
| 742 , fSwizzlerSrcRow(nullptr) |
| 743 , fColorXformSrcRow(nullptr) |
| 744 , fSrcRowBytes(width * (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel(
)))) |
700 , fNumberPasses(numberPasses) | 745 , fNumberPasses(numberPasses) |
701 , fBitDepth(bitDepth) | 746 , fBitDepth(bitDepth) |
702 {} | 747 {} |
703 | 748 |
704 SkPngCodec::~SkPngCodec() { | 749 SkPngCodec::~SkPngCodec() { |
705 this->destroyReadStruct(); | 750 this->destroyReadStruct(); |
706 } | 751 } |
707 | 752 |
708 void SkPngCodec::destroyReadStruct() { | 753 void SkPngCodec::destroyReadStruct() { |
709 if (fPng_ptr) { | 754 if (fPng_ptr) { |
710 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr | 755 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr |
711 SkASSERT(fInfo_ptr); | 756 SkASSERT(fInfo_ptr); |
712 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr); | 757 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr); |
713 fPng_ptr = nullptr; | 758 fPng_ptr = nullptr; |
714 fInfo_ptr = nullptr; | 759 fInfo_ptr = nullptr; |
715 } | 760 } |
716 } | 761 } |
717 | 762 |
718 /////////////////////////////////////////////////////////////////////////////// | 763 /////////////////////////////////////////////////////////////////////////////// |
719 // Getting the pixels | 764 // Getting the pixels |
720 /////////////////////////////////////////////////////////////////////////////// | 765 /////////////////////////////////////////////////////////////////////////////// |
721 | 766 |
722 SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, | 767 bool SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& opt
ions, |
723 const Options& options, | 768 SkPMColor ctable[], int* ctableCount) { |
724 SkPMColor ctable[], | |
725 int* ctableCount) { | |
726 // FIXME: Could we use the return value of setjmp to specify the type of | |
727 // error? | |
728 if (setjmp(png_jmpbuf(fPng_ptr))) { | 769 if (setjmp(png_jmpbuf(fPng_ptr))) { |
729 SkCodecPrintf("setjmp long jump!\n"); | 770 SkCodecPrintf("Failed on png_read_update_info.\n"); |
730 return kInvalidInput; | 771 return false; |
731 } | 772 } |
732 png_read_update_info(fPng_ptr, fInfo_ptr); | 773 png_read_update_info(fPng_ptr, fInfo_ptr); |
733 | 774 |
| 775 SkImageInfo swizzlerInfo = dstInfo; |
| 776 bool needsColorXform = needs_color_xform(dstInfo, this->getInfo()); |
| 777 if (needsColorXform) { |
| 778 switch (dstInfo.colorType()) { |
| 779 case kRGBA_8888_SkColorType: |
| 780 case kBGRA_8888_SkColorType: |
| 781 case kRGBA_F16_SkColorType: |
| 782 swizzlerInfo = swizzlerInfo.makeColorType(kRGBA_8888_SkColorType
); |
| 783 if (kPremul_SkAlphaType == dstInfo.alphaType()) { |
| 784 swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaT
ype); |
| 785 } |
| 786 break; |
| 787 default: |
| 788 return false; |
| 789 } |
| 790 |
| 791 fColorXform = SkColorSpaceXform::New(sk_ref_sp(this->getInfo().colorSpac
e()), |
| 792 sk_ref_sp(dstInfo.colorSpace())); |
| 793 |
| 794 if (!fColorXform && kRGBA_F16_SkColorType == dstInfo.colorType()) { |
| 795 return kInvalidParameters; |
| 796 } |
| 797 } |
| 798 |
734 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { | 799 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { |
735 if (!this->createColorTable(requestedInfo.colorType(), | 800 if (!this->createColorTable(swizzlerInfo.colorType(), |
736 kPremul_SkAlphaType == requestedInfo.alphaType(), ctableCount))
{ | 801 kPremul_SkAlphaType == swizzlerInfo.alphaTyp
e(), ctableCount)) { |
737 return kInvalidInput; | 802 return false; |
738 } | 803 } |
739 } | 804 } |
740 | 805 |
741 // Copy the color table to the client if they request kIndex8 mode | 806 // Copy the color table to the client if they request kIndex8 mode |
742 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); | 807 copy_color_table(swizzlerInfo, fColorTable, ctable, ctableCount); |
743 | 808 |
744 // Create the swizzler. SkPngCodec retains ownership of the color table. | 809 // Create the swizzler. SkPngCodec retains ownership of the color table. |
745 const SkPMColor* colors = get_color_ptr(fColorTable.get()); | 810 const SkPMColor* colors = get_color_ptr(fColorTable.get()); |
746 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, r
equestedInfo, | 811 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, s
wizzlerInfo, |
747 options)); | 812 options)); |
748 SkASSERT(fSwizzler); | 813 SkASSERT(fSwizzler); |
749 | 814 return true; |
750 return kSuccess; | |
751 } | 815 } |
752 | 816 |
753 | |
754 bool SkPngCodec::onRewind() { | 817 bool SkPngCodec::onRewind() { |
755 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header | 818 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header |
756 // succeeds, they will be repopulated, and if it fails, they will | 819 // succeeds, they will be repopulated, and if it fails, they will |
757 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will | 820 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will |
758 // come through this function which will rewind and again attempt | 821 // come through this function which will rewind and again attempt |
759 // to reinitialize them. | 822 // to reinitialize them. |
760 this->destroyReadStruct(); | 823 this->destroyReadStruct(); |
761 | 824 |
762 png_structp png_ptr; | 825 png_structp png_ptr; |
763 png_infop info_ptr; | 826 png_infop info_ptr; |
764 if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, &
info_ptr)) { | 827 if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, &
info_ptr)) { |
765 return false; | 828 return false; |
766 } | 829 } |
767 | 830 |
768 fPng_ptr = png_ptr; | 831 fPng_ptr = png_ptr; |
769 fInfo_ptr = info_ptr; | 832 fInfo_ptr = info_ptr; |
| 833 |
| 834 fSwizzler.reset(nullptr); |
| 835 fColorXform.reset(nullptr); |
770 return true; | 836 return true; |
771 } | 837 } |
772 | 838 |
773 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
dst, | 839 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, |
774 size_t dstRowBytes, const Options& optio
ns, | 840 size_t rowBytes, const Options& options, |
775 SkPMColor ctable[], int* ctableCount, | 841 SkPMColor ctable[], int* ctableCount, |
776 int* rowsDecoded) { | 842 int* rowsDecoded) { |
777 if (!conversion_possible(requestedInfo, this->getInfo())) { | 843 if (!png_conversion_possible(dstInfo, this->getInfo()) || |
| 844 !this->initializeXforms(dstInfo, options, ctable, ctableCount)) |
| 845 { |
778 return kInvalidConversion; | 846 return kInvalidConversion; |
779 } | 847 } |
| 848 |
780 if (options.fSubset) { | 849 if (options.fSubset) { |
781 // Subsets are not supported. | |
782 return kUnimplemented; | 850 return kUnimplemented; |
783 } | 851 } |
784 | 852 |
785 // Note that ctable and ctableCount may be modified if there is a color tabl
e | 853 this->allocateStorage(dstInfo); |
786 const Result result = this->initializeSwizzler(requestedInfo, options, ctabl
e, ctableCount); | 854 int count = this->readRows(dstInfo, dst, rowBytes, dstInfo.height(), 0); |
787 if (result != kSuccess) { | 855 if (count > dstInfo.height()) { |
788 return result; | 856 *rowsDecoded = count; |
| 857 return kIncompleteInput; |
789 } | 858 } |
790 | 859 |
791 const int width = requestedInfo.width(); | |
792 const int height = requestedInfo.height(); | |
793 const int bpp = bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()); | |
794 const size_t srcRowBytes = width * bpp; | |
795 | |
796 // FIXME: Could we use the return value of setjmp to specify the type of | |
797 // error? | |
798 int row = 0; | |
799 // This must be declared above the call to setjmp to avoid memory leaks on i
ncomplete images. | |
800 SkAutoTMalloc<uint8_t> storage; | |
801 if (setjmp(png_jmpbuf(fPng_ptr))) { | |
802 // Assume that any error that occurs while reading rows is caused by an
incomplete input. | |
803 if (fNumberPasses > 1) { | |
804 // FIXME (msarett): Handle incomplete interlaced pngs. | |
805 return (row == height) ? kSuccess : kInvalidInput; | |
806 } | |
807 // FIXME: We do a poor job on incomplete pngs compared to other decoders
(ex: Chromium, | |
808 // Ubuntu Image Viewer). This is because we use the default buffer size
in libpng (8192 | |
809 // bytes), and if we can't fill the buffer, we immediately fail. | |
810 // For example, if we try to read 8192 bytes, and the image (incorrectly
) only contains | |
811 // half that, which may have been enough to contain a non-zero number of
lines, we fail | |
812 // when we could have decoded a few more lines and then failed. | |
813 // The read function that we provide for libpng has no way of indicating
that we have | |
814 // made a partial read. | |
815 // Making our buffer size smaller improves our incomplete decodes, but w
hat impact does | |
816 // it have on regular decode performance? Should we investigate using a
different API | |
817 // instead of png_read_row? Chromium uses png_process_data. | |
818 *rowsDecoded = row; | |
819 return (row == height) ? kSuccess : kIncompleteInput; | |
820 } | |
821 | |
822 // FIXME: We could split these out based on subclass. | |
823 void* dstRow = dst; | |
824 if (fNumberPasses > 1) { | |
825 storage.reset(height * srcRowBytes); | |
826 uint8_t* const base = storage.get(); | |
827 | |
828 for (int i = 0; i < fNumberPasses; i++) { | |
829 uint8_t* srcRow = base; | |
830 for (int y = 0; y < height; y++) { | |
831 png_read_row(fPng_ptr, srcRow, nullptr); | |
832 srcRow += srcRowBytes; | |
833 } | |
834 } | |
835 | |
836 // Now swizzle it. | |
837 uint8_t* srcRow = base; | |
838 for (; row < height; row++) { | |
839 fSwizzler->swizzle(dstRow, srcRow); | |
840 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
841 srcRow += srcRowBytes; | |
842 } | |
843 } else { | |
844 storage.reset(srcRowBytes); | |
845 uint8_t* srcRow = storage.get(); | |
846 for (; row < height; row++) { | |
847 png_read_row(fPng_ptr, srcRow, nullptr); | |
848 fSwizzler->swizzle(dstRow, srcRow); | |
849 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
850 } | |
851 } | |
852 | |
853 // read rest of file, and get additional comment and time chunks in info_ptr | |
854 png_read_end(fPng_ptr, fInfo_ptr); | |
855 | |
856 return kSuccess; | 860 return kSuccess; |
857 } | 861 } |
858 | 862 |
859 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { | 863 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { |
860 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | 864 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); |
861 if (colorPtr) { | 865 if (colorPtr) { |
862 return get_color_table_fill_value(colorType, colorPtr, 0); | 866 return get_color_table_fill_value(colorType, colorPtr, 0); |
863 } | 867 } |
864 return INHERITED::onGetFillValue(colorType); | 868 return INHERITED::onGetFillValue(colorType); |
865 } | 869 } |
866 | 870 |
867 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { | 871 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { |
868 SkAutoTDelete<SkStream> streamDeleter(stream); | 872 SkAutoTDelete<SkStream> streamDeleter(stream); |
869 | 873 |
870 SkCodec* outCodec; | 874 SkCodec* outCodec; |
871 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { | 875 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { |
872 // Codec has taken ownership of the stream. | 876 // Codec has taken ownership of the stream. |
873 SkASSERT(outCodec); | 877 SkASSERT(outCodec); |
874 streamDeleter.release(); | 878 streamDeleter.release(); |
875 return outCodec; | 879 return outCodec; |
876 } | 880 } |
877 | 881 |
878 return nullptr; | 882 return nullptr; |
879 } | 883 } |
OLD | NEW |