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) { | |
scroggo
2016/04/25 15:10:43
Before I scan this 200 line block line by line to
msarett
2016/04/25 15:18:21
Sorry I should I have mentioned - yes this is all
| |
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 } | |
611 | |
612 *outCodec = new SkPngInterlacedScanlineDecoder(origWidth, origHeight, in fo, stream, | |
613 chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colorSpace); | |
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 |