Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Side by Side Diff: src/codec/SkPngCodec.cpp

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

Powered by Google App Engine
This is Rietveld 408576698