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 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
254 | 254 |
255 #endif // LIBPNG >= 1.6 | 255 #endif // LIBPNG >= 1.6 |
256 | 256 |
257 // Finally, what should we do if there is no color space information in the
PNG? | 257 // Finally, what should we do if there is no color space information in the
PNG? |
258 // The specification says that this indicates "gamma is unknown" and that th
e | 258 // The specification says that this indicates "gamma is unknown" and that th
e |
259 // "color is device dependent". I'm assuming we can represent this with NUL
L. | 259 // "color is device dependent". I'm assuming we can represent this with NUL
L. |
260 // But should we guess sRGB? Most images are sRGB, even if they don't speci
fy. | 260 // But should we guess sRGB? Most images are sRGB, even if they don't speci
fy. |
261 return nullptr; | 261 return nullptr; |
262 } | 262 } |
263 | 263 |
| 264 static int bytes_per_pixel(int bitsPerPixel) { |
| 265 // Note that we will have to change this implementation if we start |
| 266 // supporting outputs from libpng that are less than 8-bits per component. |
| 267 return bitsPerPixel / 8; |
| 268 } |
| 269 |
| 270 // Subclass of SkPngCodec which supports scanline decoding |
| 271 class SkPngScanlineDecoder : public SkPngCodec { |
| 272 public: |
| 273 SkPngScanlineDecoder(int width, int height, const SkEncodedInfo& info, SkStr
eam* stream, |
| 274 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p
tr, int bitDepth, |
| 275 sk_sp<SkColorSpace> colorSpace) |
| 276 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, 1, |
| 277 colorSpace) |
| 278 , fSrcRow(nullptr) |
| 279 {} |
| 280 |
| 281 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, |
| 282 SkPMColor ctable[], int* ctableCount) override { |
| 283 if (!conversion_possible(dstInfo, this->getInfo())) { |
| 284 return kInvalidConversion; |
| 285 } |
| 286 |
| 287 const Result result = this->initializeSwizzler(dstInfo, options, ctable, |
| 288 ctableCount); |
| 289 if (result != kSuccess) { |
| 290 return result; |
| 291 } |
| 292 |
| 293 fStorage.reset(this->getInfo().width() * |
| 294 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()))); |
| 295 fSrcRow = fStorage.get(); |
| 296 |
| 297 return kSuccess; |
| 298 } |
| 299 |
| 300 int onGetScanlines(void* dst, int count, size_t rowBytes) override { |
| 301 // Assume that an error in libpng indicates an incomplete input. |
| 302 int row = 0; |
| 303 if (setjmp(png_jmpbuf(this->png_ptr()))) { |
| 304 SkCodecPrintf("setjmp long jump!\n"); |
| 305 return row; |
| 306 } |
| 307 |
| 308 void* dstRow = dst; |
| 309 for (; row < count; row++) { |
| 310 png_read_row(this->png_ptr(), fSrcRow, nullptr); |
| 311 this->swizzler()->swizzle(dstRow, fSrcRow); |
| 312 dstRow = SkTAddOffset<void>(dstRow, rowBytes); |
| 313 } |
| 314 |
| 315 return row; |
| 316 } |
| 317 |
| 318 bool onSkipScanlines(int count) override { |
| 319 // Assume that an error in libpng indicates an incomplete input. |
| 320 if (setjmp(png_jmpbuf(this->png_ptr()))) { |
| 321 SkCodecPrintf("setjmp long jump!\n"); |
| 322 return false; |
| 323 } |
| 324 |
| 325 for (int row = 0; row < count; row++) { |
| 326 png_read_row(this->png_ptr(), fSrcRow, nullptr); |
| 327 } |
| 328 return true; |
| 329 } |
| 330 |
| 331 private: |
| 332 SkAutoTMalloc<uint8_t> fStorage; |
| 333 uint8_t* fSrcRow; |
| 334 |
| 335 typedef SkPngCodec INHERITED; |
| 336 }; |
| 337 |
| 338 |
| 339 class SkPngInterlacedScanlineDecoder : public SkPngCodec { |
| 340 public: |
| 341 SkPngInterlacedScanlineDecoder(int width, int height, const SkEncodedInfo& i
nfo, |
| 342 SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr
, |
| 343 png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpa
ce> colorSpace) |
| 344 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, |
| 345 numberPasses, colorSpace) |
| 346 , fHeight(-1) |
| 347 , fCanSkipRewind(false) |
| 348 { |
| 349 SkASSERT(numberPasses != 1); |
| 350 } |
| 351 |
| 352 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, |
| 353 SkPMColor ctable[], int* ctableCount) override { |
| 354 if (!conversion_possible(dstInfo, this->getInfo())) { |
| 355 return kInvalidConversion; |
| 356 } |
| 357 |
| 358 const Result result = this->initializeSwizzler(dstInfo, options, ctable, |
| 359 ctableCount); |
| 360 if (result != kSuccess) { |
| 361 return result; |
| 362 } |
| 363 |
| 364 fHeight = dstInfo.height(); |
| 365 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. |
| 366 fSrcRowBytes = this->getInfo().width() * |
| 367 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel())); |
| 368 fGarbageRow.reset(fSrcRowBytes); |
| 369 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); |
| 370 fCanSkipRewind = true; |
| 371 |
| 372 return SkCodec::kSuccess; |
| 373 } |
| 374 |
| 375 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override { |
| 376 // rewind stream if have previously called onGetScanlines, |
| 377 // since we need entire progressive image to get scanlines |
| 378 if (fCanSkipRewind) { |
| 379 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. |
| 380 // Next time onGetScanlines is called, we will need to rewind. |
| 381 fCanSkipRewind = false; |
| 382 } else { |
| 383 // rewindIfNeeded resets fCurrScanline, since it assumes that start |
| 384 // needs to be called again before scanline decoding. PNG scanline |
| 385 // decoding is the exception, since it needs to rewind between |
| 386 // calls to getScanlines. Keep track of fCurrScanline, to undo the |
| 387 // reset. |
| 388 const int currScanline = this->nextScanline(); |
| 389 // This method would never be called if currScanline is -1 |
| 390 SkASSERT(currScanline != -1); |
| 391 |
| 392 if (!this->rewindIfNeeded()) { |
| 393 return kCouldNotRewind; |
| 394 } |
| 395 this->updateCurrScanline(currScanline); |
| 396 } |
| 397 |
| 398 if (setjmp(png_jmpbuf(this->png_ptr()))) { |
| 399 SkCodecPrintf("setjmp long jump!\n"); |
| 400 // FIXME (msarett): Returning 0 is pessimistic. If we can complete
a single pass, |
| 401 // we may be able to report that all of the memory has been initiali
zed. Even if we |
| 402 // fail on the first pass, we can still report than some scanlines a
re initialized. |
| 403 return 0; |
| 404 } |
| 405 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); |
| 406 uint8_t* storagePtr = storage.get(); |
| 407 uint8_t* srcRow; |
| 408 const int startRow = this->nextScanline(); |
| 409 for (int i = 0; i < this->numberPasses(); i++) { |
| 410 // read rows we planned to skip into garbage row |
| 411 for (int y = 0; y < startRow; y++){ |
| 412 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); |
| 413 } |
| 414 // read rows we care about into buffer |
| 415 srcRow = storagePtr; |
| 416 for (int y = 0; y < count; y++) { |
| 417 png_read_row(this->png_ptr(), srcRow, nullptr); |
| 418 srcRow += fSrcRowBytes; |
| 419 } |
| 420 // read rows we don't want into garbage buffer |
| 421 for (int y = 0; y < fHeight - startRow - count; y++) { |
| 422 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); |
| 423 } |
| 424 } |
| 425 //swizzle the rows we care about |
| 426 srcRow = storagePtr; |
| 427 void* dstRow = dst; |
| 428 for (int y = 0; y < count; y++) { |
| 429 this->swizzler()->swizzle(dstRow, srcRow); |
| 430 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
| 431 srcRow += fSrcRowBytes; |
| 432 } |
| 433 |
| 434 return count; |
| 435 } |
| 436 |
| 437 bool onSkipScanlines(int count) override { |
| 438 // The non-virtual version will update fCurrScanline. |
| 439 return true; |
| 440 } |
| 441 |
| 442 SkScanlineOrder onGetScanlineOrder() const override { |
| 443 return kNone_SkScanlineOrder; |
| 444 } |
| 445 |
| 446 private: |
| 447 int fHeight; |
| 448 size_t fSrcRowBytes; |
| 449 SkAutoMalloc fGarbageRow; |
| 450 uint8_t* fGarbageRowPtr; |
| 451 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function |
| 452 // is called whenever some action is taken that reads the stream and |
| 453 // therefore the next call will require a rewind. So it modifies a boolean |
| 454 // to note that the *next* time it is called a rewind is needed. |
| 455 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling |
| 456 // onStartScanlineDecode followed by onGetScanlines does *not* require a |
| 457 // rewind. Since rewindIfNeeded does not have this flexibility, we need to |
| 458 // add another layer. |
| 459 bool fCanSkipRewind; |
| 460 |
| 461 typedef SkPngCodec INHERITED; |
| 462 }; |
| 463 |
264 // Reads the header and initializes the output fields, if not NULL. | 464 // Reads the header and initializes the output fields, if not NULL. |
265 // | 465 // |
266 // @param stream Input data. Will be read to get enough information to properly | 466 // @param stream Input data. Will be read to get enough information to properly |
267 // setup the codec. | 467 // setup the codec. |
268 // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. | 468 // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. |
269 // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is | 469 // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is |
270 // expected to continue to own it for the lifetime of the png_ptr. | 470 // expected to continue to own it for the lifetime of the png_ptr. |
| 471 // @param outCodec Optional output variable. If non-NULL, will be set to a new |
| 472 // SkPngCodec on success. |
271 // @param png_ptrp Optional output variable. If non-NULL, will be set to a new | 473 // @param png_ptrp Optional output variable. If non-NULL, will be set to a new |
272 // png_structp on success. | 474 // png_structp on success. |
273 // @param info_ptrp Optional output variable. If non-NULL, will be set to a new | 475 // @param info_ptrp Optional output variable. If non-NULL, will be set to a new |
274 // png_infop on success; | 476 // png_infop on success; |
275 // @param info Optional output variable. If non-NULL, will be set to | |
276 // reflect the properties of the encoded image on success. | |
277 // @param bitDepthPtr Optional output variable. If non-NULL, will be set to the | |
278 // bit depth of the encoded image on success. | |
279 // @param numberPassesPtr Optional output variable. If non-NULL, will be set to | |
280 // the number_passes of the encoded image on success. | |
281 // @return true on success, in which case the caller is responsible for calling | 477 // @return true on success, in which case the caller is responsible for calling |
282 // png_destroy_read_struct(png_ptrp, info_ptrp). | 478 // png_destroy_read_struct(png_ptrp, info_ptrp). |
283 // If it returns false, the passed in fields (except stream) are unchanged. | 479 // If it returns false, the passed in fields (except stream) are unchanged. |
284 static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, | 480 static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec
** outCodec, |
285 png_structp* png_ptrp, png_infop* info_ptrp, | 481 png_structp* png_ptrp, png_infop* info_ptrp) { |
286 int* width, int* height, SkEncodedInfo* info, int* bitDe
pthPtr, | |
287 int* numberPassesPtr) { | |
288 // The image is known to be a PNG. Decode enough to know the SkImageInfo. | 482 // The image is known to be a PNG. Decode enough to know the SkImageInfo. |
289 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, | 483 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, |
290 sk_error_fn, sk_warning_fn); | 484 sk_error_fn, sk_warning_fn); |
291 if (!png_ptr) { | 485 if (!png_ptr) { |
292 return false; | 486 return false; |
293 } | 487 } |
294 | 488 |
295 AutoCleanPng autoClean(png_ptr); | 489 AutoCleanPng autoClean(png_ptr); |
296 | 490 |
297 png_infop info_ptr = png_create_info_struct(png_ptr); | 491 png_infop info_ptr = png_create_info_struct(png_ptr); |
(...skipping 22 matching lines...) Expand all Loading... |
320 #endif | 514 #endif |
321 | 515 |
322 // The call to png_read_info() gives us all of the information from the | 516 // The call to png_read_info() gives us all of the information from the |
323 // PNG file before the first IDAT (image data chunk). | 517 // PNG file before the first IDAT (image data chunk). |
324 png_read_info(png_ptr, info_ptr); | 518 png_read_info(png_ptr, info_ptr); |
325 png_uint_32 origWidth, origHeight; | 519 png_uint_32 origWidth, origHeight; |
326 int bitDepth, encodedColorType; | 520 int bitDepth, encodedColorType; |
327 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, | 521 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, |
328 &encodedColorType, nullptr, nullptr, nullptr); | 522 &encodedColorType, nullptr, nullptr, nullptr); |
329 | 523 |
330 if (bitDepthPtr) { | |
331 *bitDepthPtr = bitDepth; | |
332 } | |
333 | |
334 // Tell libpng to strip 16 bit/color files down to 8 bits/color. | 524 // Tell libpng to strip 16 bit/color files down to 8 bits/color. |
335 // TODO: Should we handle this in SkSwizzler? Could this also benefit | 525 // TODO: Should we handle this in SkSwizzler? Could this also benefit |
336 // RAW decodes? | 526 // RAW decodes? |
337 if (bitDepth == 16) { | 527 if (bitDepth == 16) { |
338 SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType); | 528 SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType); |
339 png_set_strip_16(png_ptr); | 529 png_set_strip_16(png_ptr); |
340 } | 530 } |
341 | 531 |
342 // Now determine the default colorType and alphaType and set the required tr
ansforms. | 532 // Now determine the default colorType and alphaType and set the required tr
ansforms. |
343 // Often, we depend on SkSwizzler to perform any transforms that we need. H
owever, we | 533 // Often, we depend on SkSwizzler to perform any transforms that we need. H
owever, we |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 alpha = SkEncodedInfo::kUnpremul_Alpha; | 584 alpha = SkEncodedInfo::kUnpremul_Alpha; |
395 break; | 585 break; |
396 default: | 586 default: |
397 // All the color types have been covered above. | 587 // All the color types have been covered above. |
398 SkASSERT(false); | 588 SkASSERT(false); |
399 color = SkEncodedInfo::kRGBA_Color; | 589 color = SkEncodedInfo::kRGBA_Color; |
400 alpha = SkEncodedInfo::kUnpremul_Alpha; | 590 alpha = SkEncodedInfo::kUnpremul_Alpha; |
401 } | 591 } |
402 | 592 |
403 int numberPasses = png_set_interlace_handling(png_ptr); | 593 int numberPasses = png_set_interlace_handling(png_ptr); |
404 if (numberPassesPtr) { | |
405 *numberPassesPtr = numberPasses; | |
406 } | |
407 | 594 |
408 if (info) { | |
409 *info = SkEncodedInfo::Make(color, alpha, 8); | |
410 } | |
411 if (width) { | |
412 *width = origWidth; | |
413 } | |
414 if (height) { | |
415 *height = origHeight; | |
416 } | |
417 autoClean.release(); | 595 autoClean.release(); |
418 if (png_ptrp) { | 596 if (png_ptrp) { |
419 *png_ptrp = png_ptr; | 597 *png_ptrp = png_ptr; |
420 } | 598 } |
421 if (info_ptrp) { | 599 if (info_ptrp) { |
422 *info_ptrp = info_ptr; | 600 *info_ptrp = info_ptr; |
423 } | 601 } |
424 | 602 |
| 603 if (outCodec) { |
| 604 sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr); |
| 605 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); |
| 606 |
| 607 if (1 == numberPasses) { |
| 608 *outCodec = new SkPngScanlineDecoder(origWidth, origHeight, info, st
ream, |
| 609 chunkReader, png_ptr, info_ptr, bitDepth, colorSpace); |
| 610 } else { |
| 611 *outCodec = new SkPngInterlacedScanlineDecoder(origWidth, origHeight
, info, stream, |
| 612 chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colo
rSpace); |
| 613 } |
| 614 } |
| 615 |
425 return true; | 616 return true; |
426 } | 617 } |
427 | 618 |
428 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, | 619 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, |
429 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i
nfop info_ptr, | 620 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i
nfop info_ptr, |
430 int bitDepth, int numberPasses, sk_sp<SkColorSpace> color
Space) | 621 int bitDepth, int numberPasses, sk_sp<SkColorSpace> color
Space) |
431 : INHERITED(width, height, info, stream, colorSpace) | 622 : INHERITED(width, height, info, stream, colorSpace) |
432 , fPngChunkReader(SkSafeRef(chunkReader)) | 623 , fPngChunkReader(SkSafeRef(chunkReader)) |
433 , fPng_ptr(png_ptr) | 624 , fPng_ptr(png_ptr) |
434 , fInfo_ptr(info_ptr) | 625 , fInfo_ptr(info_ptr) |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
489 bool SkPngCodec::onRewind() { | 680 bool SkPngCodec::onRewind() { |
490 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header | 681 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header |
491 // succeeds, they will be repopulated, and if it fails, they will | 682 // succeeds, they will be repopulated, and if it fails, they will |
492 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will | 683 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will |
493 // come through this function which will rewind and again attempt | 684 // come through this function which will rewind and again attempt |
494 // to reinitialize them. | 685 // to reinitialize them. |
495 this->destroyReadStruct(); | 686 this->destroyReadStruct(); |
496 | 687 |
497 png_structp png_ptr; | 688 png_structp png_ptr; |
498 png_infop info_ptr; | 689 png_infop info_ptr; |
499 if (!read_header(this->stream(), fPngChunkReader.get(), &png_ptr, &info_ptr, | 690 if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, &
info_ptr)) { |
500 nullptr, nullptr, nullptr, nullptr, nullptr)) { | |
501 return false; | 691 return false; |
502 } | 692 } |
503 | 693 |
504 fPng_ptr = png_ptr; | 694 fPng_ptr = png_ptr; |
505 fInfo_ptr = info_ptr; | 695 fInfo_ptr = info_ptr; |
506 return true; | 696 return true; |
507 } | 697 } |
508 | 698 |
509 static int bytes_per_pixel(int bitsPerPixel) { | |
510 // Note that we will have to change this implementation if we start | |
511 // supporting outputs from libpng that are less than 8-bits per component. | |
512 return bitsPerPixel / 8; | |
513 } | |
514 | |
515 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
dst, | 699 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
dst, |
516 size_t dstRowBytes, const Options& optio
ns, | 700 size_t dstRowBytes, const Options& optio
ns, |
517 SkPMColor ctable[], int* ctableCount, | 701 SkPMColor ctable[], int* ctableCount, |
518 int* rowsDecoded) { | 702 int* rowsDecoded) { |
519 if (!conversion_possible(requestedInfo, this->getInfo())) { | 703 if (!conversion_possible(requestedInfo, this->getInfo())) { |
520 return kInvalidConversion; | 704 return kInvalidConversion; |
521 } | 705 } |
522 if (options.fSubset) { | 706 if (options.fSubset) { |
523 // Subsets are not supported. | 707 // Subsets are not supported. |
524 return kUnimplemented; | 708 return kUnimplemented; |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
599 } | 783 } |
600 | 784 |
601 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { | 785 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { |
602 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | 786 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); |
603 if (colorPtr) { | 787 if (colorPtr) { |
604 return get_color_table_fill_value(colorType, colorPtr, 0); | 788 return get_color_table_fill_value(colorType, colorPtr, 0); |
605 } | 789 } |
606 return INHERITED::onGetFillValue(colorType); | 790 return INHERITED::onGetFillValue(colorType); |
607 } | 791 } |
608 | 792 |
609 // Subclass of SkPngCodec which supports scanline decoding | 793 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { |
610 class SkPngScanlineDecoder : public SkPngCodec { | 794 SkAutoTDelete<SkStream> streamDeleter(stream); |
611 public: | |
612 SkPngScanlineDecoder(int width, int height, const SkEncodedInfo& info, SkStr
eam* stream, | |
613 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p
tr, int bitDepth, | |
614 sk_sp<SkColorSpace> colorSpace) | |
615 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, 1, | |
616 colorSpace) | |
617 , fSrcRow(nullptr) | |
618 {} | |
619 | 795 |
620 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | 796 SkCodec* outCodec; |
621 SkPMColor ctable[], int* ctableCount) override { | 797 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { |
622 if (!conversion_possible(dstInfo, this->getInfo())) { | 798 // Codec has taken ownership of the stream. |
623 return kInvalidConversion; | 799 SkASSERT(outCodec); |
624 } | 800 streamDeleter.release(); |
625 | 801 return outCodec; |
626 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | |
627 ctableCount); | |
628 if (result != kSuccess) { | |
629 return result; | |
630 } | |
631 | |
632 fStorage.reset(this->getInfo().width() * | |
633 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()))); | |
634 fSrcRow = fStorage.get(); | |
635 | |
636 return kSuccess; | |
637 } | 802 } |
638 | 803 |
639 int onGetScanlines(void* dst, int count, size_t rowBytes) override { | 804 return nullptr; |
640 // Assume that an error in libpng indicates an incomplete input. | |
641 int row = 0; | |
642 if (setjmp(png_jmpbuf(this->png_ptr()))) { | |
643 SkCodecPrintf("setjmp long jump!\n"); | |
644 return row; | |
645 } | |
646 | |
647 void* dstRow = dst; | |
648 for (; row < count; row++) { | |
649 png_read_row(this->png_ptr(), fSrcRow, nullptr); | |
650 this->swizzler()->swizzle(dstRow, fSrcRow); | |
651 dstRow = SkTAddOffset<void>(dstRow, rowBytes); | |
652 } | |
653 | |
654 return row; | |
655 } | |
656 | |
657 bool onSkipScanlines(int count) override { | |
658 // Assume that an error in libpng indicates an incomplete input. | |
659 if (setjmp(png_jmpbuf(this->png_ptr()))) { | |
660 SkCodecPrintf("setjmp long jump!\n"); | |
661 return false; | |
662 } | |
663 | |
664 for (int row = 0; row < count; row++) { | |
665 png_read_row(this->png_ptr(), fSrcRow, nullptr); | |
666 } | |
667 return true; | |
668 } | |
669 | |
670 private: | |
671 SkAutoTMalloc<uint8_t> fStorage; | |
672 uint8_t* fSrcRow; | |
673 | |
674 typedef SkPngCodec INHERITED; | |
675 }; | |
676 | |
677 | |
678 class SkPngInterlacedScanlineDecoder : public SkPngCodec { | |
679 public: | |
680 SkPngInterlacedScanlineDecoder(int width, int height, const SkEncodedInfo& i
nfo, | |
681 SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr
, | |
682 png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpa
ce> colorSpace) | |
683 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, | |
684 numberPasses, colorSpace) | |
685 , fHeight(-1) | |
686 , fCanSkipRewind(false) | |
687 { | |
688 SkASSERT(numberPasses != 1); | |
689 } | |
690 | |
691 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | |
692 SkPMColor ctable[], int* ctableCount) override { | |
693 if (!conversion_possible(dstInfo, this->getInfo())) { | |
694 return kInvalidConversion; | |
695 } | |
696 | |
697 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | |
698 ctableCount); | |
699 if (result != kSuccess) { | |
700 return result; | |
701 } | |
702 | |
703 fHeight = dstInfo.height(); | |
704 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. | |
705 fSrcRowBytes = this->getInfo().width() * | |
706 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel())); | |
707 fGarbageRow.reset(fSrcRowBytes); | |
708 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); | |
709 fCanSkipRewind = true; | |
710 | |
711 return SkCodec::kSuccess; | |
712 } | |
713 | |
714 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override { | |
715 // rewind stream if have previously called onGetScanlines, | |
716 // since we need entire progressive image to get scanlines | |
717 if (fCanSkipRewind) { | |
718 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. | |
719 // Next time onGetScanlines is called, we will need to rewind. | |
720 fCanSkipRewind = false; | |
721 } else { | |
722 // rewindIfNeeded resets fCurrScanline, since it assumes that start | |
723 // needs to be called again before scanline decoding. PNG scanline | |
724 // decoding is the exception, since it needs to rewind between | |
725 // calls to getScanlines. Keep track of fCurrScanline, to undo the | |
726 // reset. | |
727 const int currScanline = this->nextScanline(); | |
728 // This method would never be called if currScanline is -1 | |
729 SkASSERT(currScanline != -1); | |
730 | |
731 if (!this->rewindIfNeeded()) { | |
732 return kCouldNotRewind; | |
733 } | |
734 this->updateCurrScanline(currScanline); | |
735 } | |
736 | |
737 if (setjmp(png_jmpbuf(this->png_ptr()))) { | |
738 SkCodecPrintf("setjmp long jump!\n"); | |
739 // FIXME (msarett): Returning 0 is pessimistic. If we can complete
a single pass, | |
740 // we may be able to report that all of the memory has been initiali
zed. Even if we | |
741 // fail on the first pass, we can still report than some scanlines a
re initialized. | |
742 return 0; | |
743 } | |
744 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); | |
745 uint8_t* storagePtr = storage.get(); | |
746 uint8_t* srcRow; | |
747 const int startRow = this->nextScanline(); | |
748 for (int i = 0; i < this->numberPasses(); i++) { | |
749 // read rows we planned to skip into garbage row | |
750 for (int y = 0; y < startRow; y++){ | |
751 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); | |
752 } | |
753 // read rows we care about into buffer | |
754 srcRow = storagePtr; | |
755 for (int y = 0; y < count; y++) { | |
756 png_read_row(this->png_ptr(), srcRow, nullptr); | |
757 srcRow += fSrcRowBytes; | |
758 } | |
759 // read rows we don't want into garbage buffer | |
760 for (int y = 0; y < fHeight - startRow - count; y++) { | |
761 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); | |
762 } | |
763 } | |
764 //swizzle the rows we care about | |
765 srcRow = storagePtr; | |
766 void* dstRow = dst; | |
767 for (int y = 0; y < count; y++) { | |
768 this->swizzler()->swizzle(dstRow, srcRow); | |
769 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
770 srcRow += fSrcRowBytes; | |
771 } | |
772 | |
773 return count; | |
774 } | |
775 | |
776 bool onSkipScanlines(int count) override { | |
777 // The non-virtual version will update fCurrScanline. | |
778 return true; | |
779 } | |
780 | |
781 SkScanlineOrder onGetScanlineOrder() const override { | |
782 return kNone_SkScanlineOrder; | |
783 } | |
784 | |
785 private: | |
786 int fHeight; | |
787 size_t fSrcRowBytes; | |
788 SkAutoMalloc fGarbageRow; | |
789 uint8_t* fGarbageRowPtr; | |
790 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function | |
791 // is called whenever some action is taken that reads the stream and | |
792 // therefore the next call will require a rewind. So it modifies a boolean | |
793 // to note that the *next* time it is called a rewind is needed. | |
794 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling | |
795 // onStartScanlineDecode followed by onGetScanlines does *not* require a | |
796 // rewind. Since rewindIfNeeded does not have this flexibility, we need to | |
797 // add another layer. | |
798 bool fCanSkipRewind; | |
799 | |
800 typedef SkPngCodec INHERITED; | |
801 }; | |
802 | |
803 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { | |
804 SkAutoTDelete<SkStream> streamDeleter(stream); | |
805 png_structp png_ptr; | |
806 png_infop info_ptr; | |
807 int width, height; | |
808 SkEncodedInfo imageInfo; | |
809 int bitDepth; | |
810 int numberPasses; | |
811 | |
812 if (!read_header(stream, chunkReader, &png_ptr, &info_ptr, &width, &height,
&imageInfo, | |
813 &bitDepth, &numberPasses)) { | |
814 return nullptr; | |
815 } | |
816 | |
817 auto colorSpace = read_color_space(png_ptr, info_ptr); | |
818 | |
819 if (1 == numberPasses) { | |
820 return new SkPngScanlineDecoder(width, height, imageInfo, streamDeleter.
release(), | |
821 chunkReader, png_ptr, info_ptr, bitDepth
, colorSpace); | |
822 } | |
823 | |
824 return new SkPngInterlacedScanlineDecoder(width, height, imageInfo, streamDe
leter.release(), | |
825 chunkReader, png_ptr, info_ptr, bi
tDepth, | |
826 numberPasses, colorSpace); | |
827 } | 805 } |
OLD | NEW |