| 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 14 matching lines...) Expand all Loading... |
| 25 | 25 |
| 26 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { | 26 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { |
| 27 SkCodecPrintf("------ png error %s\n", msg); | 27 SkCodecPrintf("------ png error %s\n", msg); |
| 28 longjmp(png_jmpbuf(png_ptr), 1); | 28 longjmp(png_jmpbuf(png_ptr), 1); |
| 29 } | 29 } |
| 30 | 30 |
| 31 void sk_warning_fn(png_structp, png_const_charp msg) { | 31 void sk_warning_fn(png_structp, png_const_charp msg) { |
| 32 SkCodecPrintf("----- png warning %s\n", msg); | 32 SkCodecPrintf("----- png warning %s\n", msg); |
| 33 } | 33 } |
| 34 | 34 |
| 35 static void sk_read_fn(png_structp png_ptr, png_bytep data, | |
| 36 png_size_t length) { | |
| 37 SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr)); | |
| 38 const size_t bytes = stream->read(data, length); | |
| 39 if (bytes != length) { | |
| 40 // FIXME: We want to report the fact that the stream was truncated. | |
| 41 // One way to do that might be to pass a enum to longjmp so setjmp can | |
| 42 // specify the failure. | |
| 43 png_error(png_ptr, "Read Error!"); | |
| 44 } | |
| 45 } | |
| 46 | |
| 47 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED | 35 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED |
| 48 static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { | 36 static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { |
| 49 SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(pn
g_ptr); | 37 SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(pn
g_ptr); |
| 50 // readChunk() returning true means continue decoding | 38 // readChunk() returning true means continue decoding |
| 51 return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->
size) ? 1 : -1; | 39 return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->
size) ? 1 : -1; |
| 52 } | 40 } |
| 53 #endif | 41 #endif |
| 54 | 42 |
| 55 /////////////////////////////////////////////////////////////////////////////// | 43 /////////////////////////////////////////////////////////////////////////////// |
| 56 // Helpers | 44 // Helpers |
| 57 /////////////////////////////////////////////////////////////////////////////// | 45 /////////////////////////////////////////////////////////////////////////////// |
| 58 | 46 |
| 59 class AutoCleanPng : public SkNoncopyable { | 47 class AutoCleanPng : public SkNoncopyable { |
| 60 public: | 48 public: |
| 61 AutoCleanPng(png_structp png_ptr) | 49 /* |
| 50 * This class does not take ownership of stream or reader, but if codecPtr |
| 51 * is non-NULL, and decodeBounds succeeds, it will have created a new |
| 52 * SkCodec (pointed to by *codecPtr) which will own/ref them, as well as |
| 53 * the png_ptr and info_ptr. |
| 54 */ |
| 55 AutoCleanPng(png_structp png_ptr, SkStream* stream, SkPngChunkReader* reader
, |
| 56 SkCodec** codecPtr) |
| 62 : fPng_ptr(png_ptr) | 57 : fPng_ptr(png_ptr) |
| 63 , fInfo_ptr(nullptr) {} | 58 , fInfo_ptr(nullptr) |
| 59 , fDecodedBounds(false) |
| 60 , fStream(stream) |
| 61 , fChunkReader(reader) |
| 62 , fOutCodec(codecPtr) |
| 63 {} |
| 64 | 64 |
| 65 ~AutoCleanPng() { | 65 ~AutoCleanPng() { |
| 66 // fInfo_ptr will never be non-nullptr unless fPng_ptr is. | 66 // fInfo_ptr will never be non-nullptr unless fPng_ptr is. |
| 67 if (fPng_ptr) { | 67 if (fPng_ptr) { |
| 68 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr; | 68 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr; |
| 69 png_destroy_read_struct(&fPng_ptr, info_pp, nullptr); | 69 png_destroy_read_struct(&fPng_ptr, info_pp, nullptr); |
| 70 } | 70 } |
| 71 } | 71 } |
| 72 | 72 |
| 73 void setInfoPtr(png_infop info_ptr) { | 73 void setInfoPtr(png_infop info_ptr) { |
| 74 SkASSERT(nullptr == fInfo_ptr); | 74 SkASSERT(nullptr == fInfo_ptr); |
| 75 fInfo_ptr = info_ptr; | 75 fInfo_ptr = info_ptr; |
| 76 } | 76 } |
| 77 | 77 |
| 78 void release() { | 78 /** |
| 79 * Reads enough of the input stream to decode the bounds. |
| 80 * @return false if the stream is not a valid PNG (or too short). |
| 81 * true if it read enough of the stream to determine the bounds. |
| 82 * In the latter case, the stream may have been read beyond the |
| 83 * point to determine the bounds, and the png_ptr will have saved |
| 84 * any extra data. Further, if the codecPtr supplied to the |
| 85 * constructor was not NULL, it will now point to a new SkCodec, |
| 86 * which owns (or refs, in the case of the SkPngChunkReader) the |
| 87 * inputs. If codecPtr was NULL, the png_ptr and info_ptr are |
| 88 * unowned, and it is up to the caller to destroy them. |
| 89 */ |
| 90 bool decodeBounds(); |
| 91 |
| 92 private: |
| 93 png_structp fPng_ptr; |
| 94 png_infop fInfo_ptr; |
| 95 bool fDecodedBounds; |
| 96 SkStream* fStream; |
| 97 SkPngChunkReader* fChunkReader; |
| 98 SkCodec** fOutCodec; |
| 99 |
| 100 /** |
| 101 * Supplied to libpng to call when it has read enough data to determine |
| 102 * bounds. |
| 103 */ |
| 104 static void InfoCallback(png_structp png_ptr, png_infop info_ptr) { |
| 105 // png_get_progressive_ptr returns the pointer we set on the png_ptr wit
h |
| 106 // png_set_progressive_read_fn |
| 107 static_cast<AutoCleanPng*>(png_get_progressive_ptr(png_ptr))->infoCallba
ck(); |
| 108 } |
| 109 |
| 110 void infoCallback(); |
| 111 |
| 112 void releasePngPtrs() { |
| 79 fPng_ptr = nullptr; | 113 fPng_ptr = nullptr; |
| 80 fInfo_ptr = nullptr; | 114 fInfo_ptr = nullptr; |
| 81 } | 115 } |
| 82 | |
| 83 private: | |
| 84 png_structp fPng_ptr; | |
| 85 png_infop fInfo_ptr; | |
| 86 }; | 116 }; |
| 87 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) | 117 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) |
| 88 | 118 |
| 119 bool AutoCleanPng::decodeBounds() { |
| 120 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 121 return false; |
| 122 } |
| 123 |
| 124 png_set_progressive_read_fn(fPng_ptr, this, InfoCallback, nullptr, nullptr); |
| 125 |
| 126 // Arbitrary buffer size, though note that it matches (below) |
| 127 // SkPngCodec::processData(). FIXME: Can we better suit this to the size of |
| 128 // the PNG header? |
| 129 constexpr size_t kBufferSize = 4096; |
| 130 char buffer[kBufferSize]; |
| 131 |
| 132 while (true) { |
| 133 const size_t bytesRead = fStream->read(buffer, kBufferSize); |
| 134 if (!bytesRead) { |
| 135 // We have read to the end of the input without decoding bounds. |
| 136 break; |
| 137 } |
| 138 |
| 139 png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, bytesRead); |
| 140 if (fDecodedBounds) { |
| 141 break; |
| 142 } |
| 143 } |
| 144 |
| 145 // For safety, clear the pointer to this object. |
| 146 png_set_progressive_read_fn(fPng_ptr, nullptr, nullptr, nullptr, nullptr); |
| 147 return fDecodedBounds; |
| 148 } |
| 149 |
| 150 void SkPngCodec::processData() { |
| 151 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 152 // FIXME: I think we need to throw out the png_ptr at this point. |
| 153 // A straightforward way to do that might be to set a flag telling |
| 154 // ourselves that we need to rewind. This makes sense if we |
| 155 // deliberately called png_error (i.e. when we decoded as many |
| 156 // scanlines as we wanted to), but maybe not otherwise (so maybe |
| 157 // we should do something there? what to do here?) |
| 158 return; |
| 159 } |
| 160 |
| 161 // Arbitrary buffer size |
| 162 constexpr size_t kBufferSize = 4096; |
| 163 char buffer[kBufferSize]; |
| 164 |
| 165 while (true) { |
| 166 const size_t bytesRead = this->stream()->read(buffer, kBufferSize); |
| 167 png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, bytesRead); |
| 168 |
| 169 if (!bytesRead) { |
| 170 // We have read to the end of the input. Note that we quit *after* |
| 171 // calling png_process_data, because decodeBounds may have told |
| 172 // libpng to save the remainder of the buffer, in which case |
| 173 // png_process_data will process the saved buffer, though the |
| 174 // stream has no more to read. |
| 175 break; |
| 176 } |
| 177 } |
| 178 } |
| 179 |
| 89 // Note: SkColorTable claims to store SkPMColors, which is not necessarily | 180 // Note: SkColorTable claims to store SkPMColors, which is not necessarily |
| 90 // the case here. | 181 // the case here. |
| 91 // TODO: If we add support for non-native swizzles, we'll need to handle that he
re. | |
| 92 bool SkPngCodec::createColorTable(SkColorType dstColorType, bool premultiply, in
t* ctableCount) { | 182 bool SkPngCodec::createColorTable(SkColorType dstColorType, bool premultiply, in
t* ctableCount) { |
| 93 | 183 |
| 94 int numColors; | 184 int numColors; |
| 95 png_color* palette; | 185 png_color* palette; |
| 96 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { | 186 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { |
| 97 return false; | 187 return false; |
| 98 } | 188 } |
| 99 | 189 |
| 100 // Note: These are not necessarily SkPMColors. | 190 // Note: These are not necessarily SkPMColors. |
| 101 SkPMColor colorPtr[256]; | 191 SkPMColor colorPtr[256]; |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 | 344 |
| 255 #endif // LIBPNG >= 1.6 | 345 #endif // LIBPNG >= 1.6 |
| 256 | 346 |
| 257 // Finally, what should we do if there is no color space information in the
PNG? | 347 // 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 | 348 // 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. | 349 // "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. | 350 // But should we guess sRGB? Most images are sRGB, even if they don't speci
fy. |
| 261 return nullptr; | 351 return nullptr; |
| 262 } | 352 } |
| 263 | 353 |
| 264 static int bytes_per_pixel(int bitsPerPixel) { | 354 class SkPngNormalDecoder : public SkPngCodec { |
| 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: | 355 public: |
| 273 SkPngScanlineDecoder(int width, int height, const SkEncodedInfo& info, SkStr
eam* stream, | 356 SkPngNormalDecoder(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, |
| 274 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p
tr, int bitDepth, | 357 SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr, i
nt bitDepth, |
| 275 sk_sp<SkColorSpace> colorSpace) | 358 sk_sp<SkColorSpace> colorSpace) |
| 276 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, 1, | 359 : INHERITED(width, height, info, stream, reader, png_ptr, info_ptr, bitD
epth, |
| 277 colorSpace) | 360 std::move(colorSpace)) |
| 278 , fSrcRow(nullptr) | 361 , fLinesDecoded(0) |
| 362 , fDst(nullptr) |
| 363 , fRowBytes(0) |
| 364 , fFirstRow(0) |
| 365 , fLastRow(0) |
| 366 , fCallback(nullptr) |
| 279 {} | 367 {} |
| 280 | 368 |
| 281 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | 369 static void AllRowsCallback(png_structp png_ptr, png_bytep row, png_uint_32
rowNum, int /*pass*/) { |
| 282 SkPMColor ctable[], int* ctableCount) override { | 370 GetDecoder(png_ptr)->allRowsCallback(row, rowNum); |
| 283 if (!conversion_possible(dstInfo, this->getInfo())) { | 371 } |
| 284 return kInvalidConversion; | 372 |
| 285 } | 373 static void RowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowN
um, int /*pass*/) { |
| 286 | 374 GetDecoder(png_ptr)->rowCallback(row, rowNum); |
| 287 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | 375 } |
| 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: | 376 private: |
| 332 SkAutoTMalloc<uint8_t> fStorage; | 377 int fLinesDecoded; // FIXME: Move to baseclass? |
| 333 uint8_t* fSrcRow; | 378 |
| 379 // Variables for decodeAllRows/allRowsCallback |
| 380 void* fDst; |
| 381 size_t fRowBytes; |
| 382 |
| 383 // Variables for partial decode |
| 384 int fFirstRow; // FIXME: Move to baseclass? |
| 385 int fLastRow; |
| 386 std::function<void*(int)> fCallback; |
| 334 | 387 |
| 335 typedef SkPngCodec INHERITED; | 388 typedef SkPngCodec INHERITED; |
| 389 |
| 390 static SkPngNormalDecoder* GetDecoder(png_structp png_ptr) { |
| 391 return static_cast<SkPngNormalDecoder*>(png_get_progressive_ptr(png_ptr)
); |
| 392 } |
| 393 |
| 394 Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override
{ |
| 395 const int height = this->getInfo().height(); |
| 396 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, AllRowsCallb
ack, nullptr); |
| 397 fDst = dst; |
| 398 fRowBytes = rowBytes; |
| 399 |
| 400 fLinesDecoded = 0; |
| 401 |
| 402 this->processData(); |
| 403 |
| 404 if (fLinesDecoded == height) { |
| 405 return SkCodec::kSuccess; |
| 406 } |
| 407 |
| 408 if (rowsDecoded) { |
| 409 *rowsDecoded = fLinesDecoded; |
| 410 } |
| 411 |
| 412 return SkCodec::kIncompleteInput; |
| 413 } |
| 414 |
| 415 void allRowsCallback(png_bytep row, int rowNum) { |
| 416 SkASSERT(rowNum - fFirstRow == fLinesDecoded); |
| 417 fLinesDecoded++; |
| 418 this->swizzler()->swizzle(fDst, row); |
| 419 fDst = SkTAddOffset<void>(fDst, fRowBytes); |
| 420 } |
| 421 |
| 422 void setRange(int firstRow, int lastRow) override { |
| 423 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, RowCallback,
nullptr); |
| 424 fFirstRow = firstRow; |
| 425 fLastRow = lastRow; |
| 426 fLinesDecoded = 0; |
| 427 } |
| 428 |
| 429 SkCodec::Result decode(std::function<void*(int)> callback, int* rowsDecoded)
override { |
| 430 fCallback = callback; |
| 431 |
| 432 this->processData(); |
| 433 |
| 434 if (fLinesDecoded == fLastRow - fFirstRow + 1) { |
| 435 return SkCodec::kSuccess; |
| 436 } |
| 437 |
| 438 if (rowsDecoded) { |
| 439 *rowsDecoded = fLinesDecoded; |
| 440 } |
| 441 |
| 442 return SkCodec::kIncompleteInput; |
| 443 } |
| 444 |
| 445 void rowCallback(png_bytep row, int rowNum) { |
| 446 if (rowNum < fFirstRow) { |
| 447 // Ignore this row. |
| 448 return; |
| 449 } |
| 450 |
| 451 SkASSERT(rowNum <= fLastRow); |
| 452 |
| 453 fLinesDecoded++; |
| 454 void* dst = fCallback(rowNum); |
| 455 if (dst) { |
| 456 this->swizzler()->swizzle(dst, row); |
| 457 } |
| 458 |
| 459 if (rowNum == fLastRow) { |
| 460 png_error(this->png_ptr(), "Fake error to stop decoding scanlines.")
; |
| 461 } |
| 462 } |
| 336 }; | 463 }; |
| 337 | 464 |
| 338 | 465 class SkPngInterlacedDecoder : public SkPngCodec { |
| 339 class SkPngInterlacedScanlineDecoder : public SkPngCodec { | |
| 340 public: | 466 public: |
| 341 SkPngInterlacedScanlineDecoder(int width, int height, const SkEncodedInfo& i
nfo, | 467 SkPngInterlacedDecoder(int width, int height, const SkEncodedInfo& info, SkS
tream* stream, |
| 342 SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr
, | 468 SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr, i
nt bitDepth, |
| 343 png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpa
ce> colorSpace) | 469 sk_sp<SkColorSpace> colorSpace, int numberPasses) |
| 344 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, | 470 : INHERITED(width, height, info, stream, reader, png_ptr, info_ptr, bitD
epth, |
| 345 numberPasses, colorSpace) | 471 std::move(colorSpace)) |
| 346 , fHeight(-1) | 472 , fNumberPasses(numberPasses) |
| 347 , fCanSkipRewind(false) | 473 , fFirstRow(0) |
| 348 { | 474 , fLastRow(0) |
| 349 SkASSERT(numberPasses != 1); | 475 , fLinesDecoded(0) |
| 350 } | 476 , fInterlacedComplete(false) |
| 351 | 477 , fPng_rowbytes(0) |
| 352 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | 478 {} |
| 353 SkPMColor ctable[], int* ctableCount) override { | 479 |
| 354 if (!conversion_possible(dstInfo, this->getInfo())) { | 480 static void InterlacedRowCallback(png_structp png_ptr, png_bytep row, png_ui
nt_32 rowNum, int pass) { |
| 355 return kInvalidConversion; | 481 auto decoder = static_cast<SkPngInterlacedDecoder*>(png_get_progressive_
ptr(png_ptr)); |
| 356 } | 482 decoder->interlacedRowCallback(row, rowNum, pass); |
| 357 | 483 } |
| 358 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | 484 |
| 359 ctableCount); | 485 private: |
| 360 if (result != kSuccess) { | 486 const int fNumberPasses; |
| 361 return result; | 487 int fFirstRow; |
| 362 } | 488 int fLastRow; |
| 363 | 489 int fLinesDecoded; |
| 364 fHeight = dstInfo.height(); | 490 bool fInterlacedComplete; |
| 365 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. | 491 png_uint_32 fPng_rowbytes; |
| 366 fSrcRowBytes = this->getInfo().width() * | 492 SkAutoTMalloc<png_byte> fInterlaceBuffer; |
| 367 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel())); | 493 |
| 368 fGarbageRow.reset(fSrcRowBytes); | 494 typedef SkPngCodec INHERITED; |
| 369 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); | 495 |
| 370 fCanSkipRewind = true; | 496 // FIXME: Currently sharing interlaced callback for all rows and subset. It'
s not |
| 371 | 497 // as expensive as the subset version of non-interlaced, but it still does e
xtra |
| 372 return SkCodec::kSuccess; | 498 // work. |
| 373 } | 499 void interlacedRowCallback(png_bytep row, int rowNum, int pass) { |
| 374 | 500 if (rowNum < fFirstRow || rowNum > fLastRow) { |
| 375 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override { | 501 // Ignore this row |
| 376 // rewind stream if have previously called onGetScanlines, | 502 return; |
| 377 // since we need entire progressive image to get scanlines | 503 } |
| 378 if (fCanSkipRewind) { | 504 |
| 379 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. | 505 png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_
rowbytes; |
| 380 // Next time onGetScanlines is called, we will need to rewind. | 506 png_progressive_combine_row(this->png_ptr(), oldRow, row); |
| 381 fCanSkipRewind = false; | 507 |
| 508 if (0 == pass) { |
| 509 // The first pass initializes all rows. |
| 510 SkASSERT(row); |
| 511 SkASSERT(fLinesDecoded == rowNum - fFirstRow); |
| 512 fLinesDecoded++; |
| 382 } else { | 513 } else { |
| 383 // rewindIfNeeded resets fCurrScanline, since it assumes that start | 514 SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1); |
| 384 // needs to be called again before scanline decoding. PNG scanline | 515 if (fNumberPasses - 1 == pass && rowNum == fLastRow) { |
| 385 // decoding is the exception, since it needs to rewind between | 516 // Last pass, and we have read all of the rows we care about. No
te that |
| 386 // calls to getScanlines. Keep track of fCurrScanline, to undo the | 517 // we do not care about reading anything beyond the end of the i
mage (or |
| 387 // reset. | 518 // beyond the last scanline requested). |
| 388 const int currScanline = this->nextScanline(); | 519 fInterlacedComplete = true; |
| 389 // This method would never be called if currScanline is -1 | 520 png_error(this->png_ptr(), "Fake error to stop interlaced scanli
ne decoding"); |
| 390 SkASSERT(currScanline != -1); | |
| 391 | |
| 392 if (!this->rewindIfNeeded()) { | |
| 393 return kCouldNotRewind; | |
| 394 } | 521 } |
| 395 this->updateCurrScanline(currScanline); | 522 } |
| 396 } | 523 } |
| 397 | 524 |
| 398 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 525 SkCodec::Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded)
override { |
| 399 SkCodecPrintf("setjmp long jump!\n"); | 526 const int height = this->getInfo().height(); |
| 400 // FIXME (msarett): Returning 0 is pessimistic. If we can complete
a single pass, | 527 this->setUpInterlaceBuffer(height); |
| 401 // we may be able to report that all of the memory has been initiali
zed. Even if we | 528 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRo
wCallback, nullptr); |
| 402 // fail on the first pass, we can still report than some scanlines a
re initialized. | 529 |
| 403 return 0; | 530 fFirstRow = 0; |
| 404 } | 531 fLastRow = height - 1; |
| 405 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); | 532 fLinesDecoded = 0; |
| 406 uint8_t* storagePtr = storage.get(); | 533 |
| 407 uint8_t* srcRow; | 534 this->processData(); |
| 408 const int startRow = this->nextScanline(); | 535 |
| 409 for (int i = 0; i < this->numberPasses(); i++) { | 536 png_bytep srcRow = fInterlaceBuffer.get(); |
| 410 // read rows we planned to skip into garbage row | 537 // FIXME: When resuming, this may rewrite rows that did not change. |
| 411 for (int y = 0; y < startRow; y++){ | 538 for (int rowNum = 0; rowNum <= fLastRow; rowNum++) { |
| 412 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); | 539 this->swizzler()->swizzle(dst, srcRow); |
| 540 dst = SkTAddOffset<void>(dst, rowBytes); |
| 541 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes); |
| 542 } |
| 543 if (fInterlacedComplete) { |
| 544 return SkCodec::kSuccess; |
| 545 } |
| 546 |
| 547 if (rowsDecoded) { |
| 548 *rowsDecoded = fLinesDecoded; |
| 549 } |
| 550 |
| 551 return SkCodec::kIncompleteInput; |
| 552 } |
| 553 |
| 554 void setRange(int firstRow, int lastRow) override { |
| 555 this->setUpInterlaceBuffer(lastRow - firstRow + 1); |
| 556 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRo
wCallback, nullptr); |
| 557 fFirstRow = firstRow; |
| 558 fLastRow = lastRow; |
| 559 fLinesDecoded = 0; |
| 560 } |
| 561 |
| 562 SkCodec::Result decode(std::function<void*(int)> callback, int* rowsDecoded)
override { |
| 563 this->processData(); |
| 564 |
| 565 // Now call the callback on all the rows that were decoded. |
| 566 if (!fLinesDecoded) { |
| 567 return SkCodec::kIncompleteInput; |
| 568 } |
| 569 const int lastRow = fLinesDecoded + fFirstRow - 1; |
| 570 SkASSERT(lastRow <= fLastRow); |
| 571 |
| 572 // FIXME: For resuming interlace, we may swizzle a row that hasn't chang
ed. But it |
| 573 // may be too tricky/expensive to handle that correctly. |
| 574 png_bytep srcRow = fInterlaceBuffer.get(); |
| 575 for (int rowNum = fFirstRow; rowNum <= lastRow; rowNum++, srcRow += fPng
_rowbytes) { |
| 576 void* dst = callback(rowNum); |
| 577 if (dst) { |
| 578 this->swizzler()->swizzle(dst, srcRow); |
| 413 } | 579 } |
| 414 // read rows we care about into buffer | 580 } |
| 415 srcRow = storagePtr; | 581 |
| 416 for (int y = 0; y < count; y++) { | 582 if (fInterlacedComplete) { |
| 417 png_read_row(this->png_ptr(), srcRow, nullptr); | 583 return SkCodec::kSuccess; |
| 418 srcRow += fSrcRowBytes; | 584 } |
| 419 } | 585 |
| 420 // read rows we don't want into garbage buffer | 586 if (rowsDecoded) { |
| 421 for (int y = 0; y < fHeight - startRow - count; y++) { | 587 *rowsDecoded = fLinesDecoded; |
| 422 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); | 588 } |
| 423 } | 589 return SkCodec::kIncompleteInput; |
| 424 } | 590 } |
| 425 //swizzle the rows we care about | 591 |
| 426 srcRow = storagePtr; | 592 void setUpInterlaceBuffer(int height) { |
| 427 void* dstRow = dst; | 593 fPng_rowbytes = png_get_rowbytes(this->png_ptr(), this->info_ptr()); |
| 428 for (int y = 0; y < count; y++) { | 594 fInterlaceBuffer.reset(fPng_rowbytes * height); |
| 429 this->swizzler()->swizzle(dstRow, srcRow); | 595 fInterlacedComplete = false; |
| 430 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | 596 } |
| 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 }; | 597 }; |
| 463 | 598 |
| 464 // Reads the header and initializes the output fields, if not NULL. | 599 // Reads the header and initializes the output fields, if not NULL. |
| 465 // | 600 // |
| 466 // @param stream Input data. Will be read to get enough information to properly | 601 // @param stream Input data. Will be read to get enough information to properly |
| 467 // setup the codec. | 602 // setup the codec. |
| 468 // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. | 603 // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. |
| 469 // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is | 604 // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is |
| 470 // expected to continue to own it for the lifetime of the png_ptr. | 605 // 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 | 606 // @param outCodec Optional output variable. If non-NULL, will be set to a new |
| 472 // SkPngCodec on success. | 607 // SkPngCodec on success. |
| 473 // @param png_ptrp Optional output variable. If non-NULL, will be set to a new | 608 // @param png_ptrp Optional output variable. If non-NULL, will be set to a new |
| 474 // png_structp on success. | 609 // png_structp on success. |
| 475 // @param info_ptrp Optional output variable. If non-NULL, will be set to a new | 610 // @param info_ptrp Optional output variable. If non-NULL, will be set to a new |
| 476 // png_infop on success; | 611 // png_infop on success; |
| 477 // @return true on success, in which case the caller is responsible for calling | 612 // @return true on success, in which case the caller is responsible for calling |
| 478 // png_destroy_read_struct(png_ptrp, info_ptrp). | 613 // png_destroy_read_struct(png_ptrp, info_ptrp). |
| 479 // If it returns false, the passed in fields (except stream) are unchanged. | 614 // If it returns false, the passed in fields (except stream) are unchanged. |
| 480 static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec
** outCodec, | 615 static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec
** outCodec, |
| 481 png_structp* png_ptrp, png_infop* info_ptrp) { | 616 png_structp* png_ptrp, png_infop* info_ptrp) { |
| 482 // The image is known to be a PNG. Decode enough to know the SkImageInfo. | 617 // The image is known to be a PNG. Decode enough to know the SkImageInfo. |
| 483 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, | 618 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, |
| 484 sk_error_fn, sk_warning_fn); | 619 sk_error_fn, sk_warning_fn); |
| 485 if (!png_ptr) { | 620 if (!png_ptr) { |
| 486 return false; | 621 return false; |
| 487 } | 622 } |
| 488 | 623 |
| 489 AutoCleanPng autoClean(png_ptr); | 624 AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec); |
| 490 | 625 |
| 491 png_infop info_ptr = png_create_info_struct(png_ptr); | 626 png_infop info_ptr = png_create_info_struct(png_ptr); |
| 492 if (info_ptr == nullptr) { | 627 if (info_ptr == nullptr) { |
| 493 return false; | 628 return false; |
| 494 } | 629 } |
| 495 | 630 |
| 496 autoClean.setInfoPtr(info_ptr); | 631 autoClean.setInfoPtr(info_ptr); |
| 497 | 632 |
| 498 // FIXME: Could we use the return value of setjmp to specify the type of | 633 // FIXME: Could we use the return value of setjmp to specify the type of |
| 499 // error? | 634 // error? |
| 500 if (setjmp(png_jmpbuf(png_ptr))) { | 635 if (setjmp(png_jmpbuf(png_ptr))) { |
| 501 return false; | 636 return false; |
| 502 } | 637 } |
| 503 | 638 |
| 504 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn); | |
| 505 | |
| 506 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED | 639 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED |
| 507 // Hookup our chunkReader so we can see any user-chunks the caller may be in
terested in. | 640 // Hookup our chunkReader so we can see any user-chunks the caller may be in
terested in. |
| 508 // This needs to be installed before we read the png header. Android may st
ore ninepatch | 641 // This needs to be installed before we read the png header. Android may st
ore ninepatch |
| 509 // chunks in the header. | 642 // chunks in the header. |
| 510 if (chunkReader) { | 643 if (chunkReader) { |
| 511 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*
)"", 0); | 644 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*
)"", 0); |
| 512 png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_use
r_chunk); | 645 png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_use
r_chunk); |
| 513 } | 646 } |
| 514 #endif | 647 #endif |
| 515 | 648 |
| 516 // The call to png_read_info() gives us all of the information from the | 649 const bool decodedBounds = autoClean.decodeBounds(); |
| 517 // PNG file before the first IDAT (image data chunk). | 650 |
| 518 png_read_info(png_ptr, info_ptr); | 651 if (!decodedBounds) { |
| 652 return false; |
| 653 } |
| 654 |
| 655 // On success, decodeBounds releases ownership of png_ptr and info_ptr. |
| 656 if (png_ptrp) { |
| 657 *png_ptrp = png_ptr; |
| 658 } |
| 659 if (info_ptrp) { |
| 660 *info_ptrp = info_ptr; |
| 661 } |
| 662 |
| 663 // decodeBounds takes care of setting outCodec |
| 664 if (outCodec) { |
| 665 SkASSERT(*outCodec); |
| 666 } |
| 667 return true; |
| 668 } |
| 669 |
| 670 void AutoCleanPng::infoCallback() { |
| 519 png_uint_32 origWidth, origHeight; | 671 png_uint_32 origWidth, origHeight; |
| 520 int bitDepth, encodedColorType; | 672 int bitDepth, encodedColorType; |
| 521 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, | 673 png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth, |
| 522 &encodedColorType, nullptr, nullptr, nullptr); | 674 &encodedColorType, nullptr, nullptr, nullptr); |
| 523 | 675 |
| 524 // Tell libpng to strip 16 bit/color files down to 8 bits/color. | 676 // Tell libpng to strip 16 bit/color files down to 8 bits/color. |
| 525 // TODO: Should we handle this in SkSwizzler? Could this also benefit | 677 // TODO: Should we handle this in SkSwizzler? Could this also benefit |
| 526 // RAW decodes? | 678 // RAW decodes? |
| 527 if (bitDepth == 16) { | 679 if (bitDepth == 16) { |
| 528 SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType); | 680 SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType); |
| 529 png_set_strip_16(png_ptr); | 681 png_set_strip_16(fPng_ptr); |
| 530 } | 682 } |
| 531 | 683 |
| 532 // Now determine the default colorType and alphaType and set the required tr
ansforms. | 684 // Now determine the default colorType and alphaType and set the required tr
ansforms. |
| 533 // Often, we depend on SkSwizzler to perform any transforms that we need. H
owever, we | 685 // Often, we depend on SkSwizzler to perform any transforms that we need. H
owever, we |
| 534 // still depend on libpng for many of the rare and PNG-specific cases. | 686 // still depend on libpng for many of the rare and PNG-specific cases. |
| 535 SkEncodedInfo::Color color; | 687 SkEncodedInfo::Color color; |
| 536 SkEncodedInfo::Alpha alpha; | 688 SkEncodedInfo::Alpha alpha; |
| 537 switch (encodedColorType) { | 689 switch (encodedColorType) { |
| 538 case PNG_COLOR_TYPE_PALETTE: | 690 case PNG_COLOR_TYPE_PALETTE: |
| 539 // Extract multiple pixels with bit depths of 1, 2, and 4 from a sin
gle | 691 // Extract multiple pixels with bit depths of 1, 2, and 4 from a sin
gle |
| 540 // byte into separate bytes (useful for paletted and grayscale image
s). | 692 // byte into separate bytes (useful for paletted and grayscale image
s). |
| 541 if (bitDepth < 8) { | 693 if (bitDepth < 8) { |
| 542 // TODO: Should we use SkSwizzler here? | 694 // TODO: Should we use SkSwizzler here? |
| 543 png_set_packing(png_ptr); | 695 png_set_packing(fPng_ptr); |
| 544 } | 696 } |
| 545 | 697 |
| 546 color = SkEncodedInfo::kPalette_Color; | 698 color = SkEncodedInfo::kPalette_Color; |
| 547 // Set the alpha depending on if a transparency chunk exists. | 699 // Set the alpha depending on if a transparency chunk exists. |
| 548 alpha = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? | 700 alpha = png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS) ? |
| 549 SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alph
a; | 701 SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alph
a; |
| 550 break; | 702 break; |
| 551 case PNG_COLOR_TYPE_RGB: | 703 case PNG_COLOR_TYPE_RGB: |
| 552 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | 704 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { |
| 553 // Convert to RGBA if transparency chunk exists. | 705 // Convert to RGBA if transparency chunk exists. |
| 554 png_set_tRNS_to_alpha(png_ptr); | 706 png_set_tRNS_to_alpha(fPng_ptr); |
| 555 color = SkEncodedInfo::kRGBA_Color; | 707 color = SkEncodedInfo::kRGBA_Color; |
| 556 alpha = SkEncodedInfo::kBinary_Alpha; | 708 alpha = SkEncodedInfo::kBinary_Alpha; |
| 557 } else { | 709 } else { |
| 558 color = SkEncodedInfo::kRGB_Color; | 710 color = SkEncodedInfo::kRGB_Color; |
| 559 alpha = SkEncodedInfo::kOpaque_Alpha; | 711 alpha = SkEncodedInfo::kOpaque_Alpha; |
| 560 } | 712 } |
| 561 break; | 713 break; |
| 562 case PNG_COLOR_TYPE_GRAY: | 714 case PNG_COLOR_TYPE_GRAY: |
| 563 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/p
ixel. | 715 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/p
ixel. |
| 564 if (bitDepth < 8) { | 716 if (bitDepth < 8) { |
| 565 // TODO: Should we use SkSwizzler here? | 717 // TODO: Should we use SkSwizzler here? |
| 566 png_set_expand_gray_1_2_4_to_8(png_ptr); | 718 png_set_expand_gray_1_2_4_to_8(fPng_ptr); |
| 567 } | 719 } |
| 568 | 720 |
| 569 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | 721 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { |
| 570 png_set_tRNS_to_alpha(png_ptr); | 722 png_set_tRNS_to_alpha(fPng_ptr); |
| 571 color = SkEncodedInfo::kGrayAlpha_Color; | 723 color = SkEncodedInfo::kGrayAlpha_Color; |
| 572 alpha = SkEncodedInfo::kBinary_Alpha; | 724 alpha = SkEncodedInfo::kBinary_Alpha; |
| 573 } else { | 725 } else { |
| 574 color = SkEncodedInfo::kGray_Color; | 726 color = SkEncodedInfo::kGray_Color; |
| 575 alpha = SkEncodedInfo::kOpaque_Alpha; | 727 alpha = SkEncodedInfo::kOpaque_Alpha; |
| 576 } | 728 } |
| 577 break; | 729 break; |
| 578 case PNG_COLOR_TYPE_GRAY_ALPHA: | 730 case PNG_COLOR_TYPE_GRAY_ALPHA: |
| 579 color = SkEncodedInfo::kGrayAlpha_Color; | 731 color = SkEncodedInfo::kGrayAlpha_Color; |
| 580 alpha = SkEncodedInfo::kUnpremul_Alpha; | 732 alpha = SkEncodedInfo::kUnpremul_Alpha; |
| 581 break; | 733 break; |
| 582 case PNG_COLOR_TYPE_RGBA: | 734 case PNG_COLOR_TYPE_RGBA: |
| 583 color = SkEncodedInfo::kRGBA_Color; | 735 color = SkEncodedInfo::kRGBA_Color; |
| 584 alpha = SkEncodedInfo::kUnpremul_Alpha; | 736 alpha = SkEncodedInfo::kUnpremul_Alpha; |
| 585 break; | 737 break; |
| 586 default: | 738 default: |
| 587 // All the color types have been covered above. | 739 // All the color types have been covered above. |
| 588 SkASSERT(false); | 740 SkASSERT(false); |
| 589 color = SkEncodedInfo::kRGBA_Color; | 741 color = SkEncodedInfo::kRGBA_Color; |
| 590 alpha = SkEncodedInfo::kUnpremul_Alpha; | 742 alpha = SkEncodedInfo::kUnpremul_Alpha; |
| 591 } | 743 } |
| 592 | 744 |
| 593 int numberPasses = png_set_interlace_handling(png_ptr); | 745 const int numberPasses = png_set_interlace_handling(fPng_ptr); |
| 594 | 746 |
| 595 autoClean.release(); | 747 if (fOutCodec) { |
| 596 if (png_ptrp) { | 748 SkASSERT(nullptr == *fOutCodec); |
| 597 *png_ptrp = png_ptr; | 749 sk_sp<SkColorSpace> colorSpace = read_color_space(fPng_ptr, fInfo_ptr); |
| 598 } | |
| 599 if (info_ptrp) { | |
| 600 *info_ptrp = info_ptr; | |
| 601 } | |
| 602 | |
| 603 if (outCodec) { | |
| 604 sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr); | |
| 605 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); | 750 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); |
| 606 | |
| 607 if (1 == numberPasses) { | 751 if (1 == numberPasses) { |
| 608 *outCodec = new SkPngScanlineDecoder(origWidth, origHeight, info, st
ream, | 752 *fOutCodec = new SkPngNormalDecoder(origWidth, origHeight, info, fSt
ream, |
| 609 chunkReader, png_ptr, info_ptr, bitDepth, colorSpace); | 753 fChunkReader, fPng_ptr, fInfo_ptr, bitDepth, std::move(color
Space)); |
| 610 } else { | 754 } else { |
| 611 *outCodec = new SkPngInterlacedScanlineDecoder(origWidth, origHeight
, info, stream, | 755 *fOutCodec = new SkPngInterlacedDecoder(origWidth, origHeight, info,
fStream, |
| 612 chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colo
rSpace); | 756 fChunkReader, fPng_ptr, fInfo_ptr, bitDepth, std::move(color
Space), |
| 757 numberPasses); |
| 613 } | 758 } |
| 614 } | 759 } |
| 615 | 760 |
| 616 return true; | 761 fDecodedBounds = true; |
| 762 // 1 tells libpng to save any extra data. We may be able to be more efficien
t by saving |
| 763 // it ourselves. |
| 764 png_process_data_pause(fPng_ptr, 1); |
| 765 |
| 766 // Release the pointers, which are now owned by the codec or the caller is e
xpected to |
| 767 // take ownership. |
| 768 this->releasePngPtrs(); |
| 617 } | 769 } |
| 618 | 770 |
| 619 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, | 771 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, |
| 620 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i
nfop info_ptr, | 772 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i
nfop info_ptr, |
| 621 int bitDepth, int numberPasses, sk_sp<SkColorSpace> color
Space) | 773 int bitDepth, sk_sp<SkColorSpace> colorSpace) |
| 622 : INHERITED(width, height, info, stream, colorSpace) | 774 : INHERITED(width, height, info, stream, colorSpace) |
| 623 , fPngChunkReader(SkSafeRef(chunkReader)) | 775 , fPngChunkReader(SkSafeRef(chunkReader)) |
| 624 , fPng_ptr(png_ptr) | 776 , fPng_ptr(png_ptr) |
| 625 , fInfo_ptr(info_ptr) | 777 , fInfo_ptr(info_ptr) |
| 626 , fNumberPasses(numberPasses) | |
| 627 , fBitDepth(bitDepth) | 778 , fBitDepth(bitDepth) |
| 628 {} | 779 {} |
| 629 | 780 |
| 630 SkPngCodec::~SkPngCodec() { | 781 SkPngCodec::~SkPngCodec() { |
| 631 this->destroyReadStruct(); | 782 this->destroyReadStruct(); |
| 632 } | 783 } |
| 633 | 784 |
| 634 void SkPngCodec::destroyReadStruct() { | 785 void SkPngCodec::destroyReadStruct() { |
| 635 if (fPng_ptr) { | 786 if (fPng_ptr) { |
| 636 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr | 787 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr |
| 637 SkASSERT(fInfo_ptr); | 788 SkASSERT(fInfo_ptr); |
| 638 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr); | 789 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr); |
| 639 fPng_ptr = nullptr; | 790 fPng_ptr = nullptr; |
| 640 fInfo_ptr = nullptr; | 791 fInfo_ptr = nullptr; |
| 641 } | 792 } |
| 642 } | 793 } |
| 643 | 794 |
| 644 /////////////////////////////////////////////////////////////////////////////// | 795 /////////////////////////////////////////////////////////////////////////////// |
| 645 // Getting the pixels | 796 // Getting the pixels |
| 646 /////////////////////////////////////////////////////////////////////////////// | 797 /////////////////////////////////////////////////////////////////////////////// |
| 647 | 798 |
| 648 SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, | 799 bool SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
| 649 const Options& options, | 800 const Options& options, |
| 650 SkPMColor ctable[], | 801 SkPMColor ctable[], |
| 651 int* ctableCount) { | 802 int* ctableCount) { |
| 652 // FIXME: Could we use the return value of setjmp to specify the type of | |
| 653 // error? | |
| 654 if (setjmp(png_jmpbuf(fPng_ptr))) { | 803 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 655 SkCodecPrintf("setjmp long jump!\n"); | 804 return false; |
| 656 return kInvalidInput; | |
| 657 } | 805 } |
| 658 png_read_update_info(fPng_ptr, fInfo_ptr); | 806 png_read_update_info(fPng_ptr, fInfo_ptr); |
| 659 | 807 |
| 660 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { | 808 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { |
| 661 if (!this->createColorTable(requestedInfo.colorType(), | 809 if (!this->createColorTable(requestedInfo.colorType(), |
| 662 kPremul_SkAlphaType == requestedInfo.alphaType(), ctableCount))
{ | 810 kPremul_SkAlphaType == requestedInfo.alphaType(), ctableCount))
{ |
| 663 return kInvalidInput; | 811 return false; |
| 664 } | 812 } |
| 665 } | 813 } |
| 666 | 814 |
| 667 // Copy the color table to the client if they request kIndex8 mode | 815 // Copy the color table to the client if they request kIndex8 mode |
| 668 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); | 816 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); |
| 669 | 817 |
| 670 // Create the swizzler. SkPngCodec retains ownership of the color table. | 818 // Create the swizzler. SkPngCodec retains ownership of the color table. |
| 671 const SkPMColor* colors = get_color_ptr(fColorTable.get()); | 819 const SkPMColor* colors = get_color_ptr(fColorTable.get()); |
| 672 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, r
equestedInfo, | 820 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, r
equestedInfo, |
| 673 options)); | 821 options)); |
| 674 SkASSERT(fSwizzler); | 822 SkASSERT(fSwizzler); |
| 675 | 823 |
| 676 return kSuccess; | 824 return true; |
| 677 } | 825 } |
| 678 | 826 |
| 679 | 827 |
| 680 bool SkPngCodec::onRewind() { | 828 bool SkPngCodec::onRewind() { |
| 681 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header | 829 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header |
| 682 // succeeds, they will be repopulated, and if it fails, they will | 830 // succeeds, they will be repopulated, and if it fails, they will |
| 683 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will | 831 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will |
| 684 // come through this function which will rewind and again attempt | 832 // come through this function which will rewind and again attempt |
| 685 // to reinitialize them. | 833 // to reinitialize them. |
| 686 this->destroyReadStruct(); | 834 this->destroyReadStruct(); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 702 int* rowsDecoded) { | 850 int* rowsDecoded) { |
| 703 if (!conversion_possible(requestedInfo, this->getInfo())) { | 851 if (!conversion_possible(requestedInfo, this->getInfo())) { |
| 704 return kInvalidConversion; | 852 return kInvalidConversion; |
| 705 } | 853 } |
| 706 if (options.fSubset) { | 854 if (options.fSubset) { |
| 707 // Subsets are not supported. | 855 // Subsets are not supported. |
| 708 return kUnimplemented; | 856 return kUnimplemented; |
| 709 } | 857 } |
| 710 | 858 |
| 711 // Note that ctable and ctableCount may be modified if there is a color tabl
e | 859 // Note that ctable and ctableCount may be modified if there is a color tabl
e |
| 712 const Result result = this->initializeSwizzler(requestedInfo, options, ctabl
e, ctableCount); | 860 if (!this->initializeSwizzler(requestedInfo, options, ctable, ctableCount))
{ |
| 713 if (result != kSuccess) { | 861 return kInvalidInput; // or parameters? |
| 714 return result; | |
| 715 } | 862 } |
| 716 | 863 |
| 717 const int width = requestedInfo.width(); | 864 return this->decodeAllRows(dst, dstRowBytes, rowsDecoded); |
| 718 const int height = requestedInfo.height(); | 865 } |
| 719 const int bpp = bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()); | |
| 720 const size_t srcRowBytes = width * bpp; | |
| 721 | 866 |
| 722 // FIXME: Could we use the return value of setjmp to specify the type of | 867 SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo, |
| 723 // error? | 868 const SkCodec::Options& options, SkPMColor* ctable, int* ctableCount) { |
| 724 int row = 0; | 869 if (!conversion_possible(dstInfo, this->getInfo())) { |
| 725 // This must be declared above the call to setjmp to avoid memory leaks on i
ncomplete images. | 870 return kInvalidConversion; |
| 726 SkAutoTMalloc<uint8_t> storage; | |
| 727 if (setjmp(png_jmpbuf(fPng_ptr))) { | |
| 728 // Assume that any error that occurs while reading rows is caused by an
incomplete input. | |
| 729 if (fNumberPasses > 1) { | |
| 730 // FIXME (msarett): Handle incomplete interlaced pngs. | |
| 731 return (row == height) ? kSuccess : kInvalidInput; | |
| 732 } | |
| 733 // FIXME: We do a poor job on incomplete pngs compared to other decoders
(ex: Chromium, | |
| 734 // Ubuntu Image Viewer). This is because we use the default buffer size
in libpng (8192 | |
| 735 // bytes), and if we can't fill the buffer, we immediately fail. | |
| 736 // For example, if we try to read 8192 bytes, and the image (incorrectly
) only contains | |
| 737 // half that, which may have been enough to contain a non-zero number of
lines, we fail | |
| 738 // when we could have decoded a few more lines and then failed. | |
| 739 // The read function that we provide for libpng has no way of indicating
that we have | |
| 740 // made a partial read. | |
| 741 // Making our buffer size smaller improves our incomplete decodes, but w
hat impact does | |
| 742 // it have on regular decode performance? Should we investigate using a
different API | |
| 743 // instead of png_read_row? Chromium uses png_process_data. | |
| 744 *rowsDecoded = row; | |
| 745 return (row == height) ? kSuccess : kIncompleteInput; | |
| 746 } | 871 } |
| 747 | 872 |
| 748 // FIXME: We could split these out based on subclass. | 873 if (!this->initializeSwizzler(dstInfo, options, ctable, ctableCount)) { |
| 749 void* dstRow = dst; | 874 return kInvalidInput; |
| 750 if (fNumberPasses > 1) { | |
| 751 storage.reset(height * srcRowBytes); | |
| 752 uint8_t* const base = storage.get(); | |
| 753 | |
| 754 for (int i = 0; i < fNumberPasses; i++) { | |
| 755 uint8_t* srcRow = base; | |
| 756 for (int y = 0; y < height; y++) { | |
| 757 png_read_row(fPng_ptr, srcRow, nullptr); | |
| 758 srcRow += srcRowBytes; | |
| 759 } | |
| 760 } | |
| 761 | |
| 762 // Now swizzle it. | |
| 763 uint8_t* srcRow = base; | |
| 764 for (; row < height; row++) { | |
| 765 fSwizzler->swizzle(dstRow, srcRow); | |
| 766 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
| 767 srcRow += srcRowBytes; | |
| 768 } | |
| 769 } else { | |
| 770 storage.reset(srcRowBytes); | |
| 771 uint8_t* srcRow = storage.get(); | |
| 772 for (; row < height; row++) { | |
| 773 png_read_row(fPng_ptr, srcRow, nullptr); | |
| 774 fSwizzler->swizzle(dstRow, srcRow); | |
| 775 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
| 776 } | |
| 777 } | 875 } |
| 778 | 876 |
| 779 // read rest of file, and get additional comment and time chunks in info_ptr | 877 int firstRow, lastRow; |
| 780 png_read_end(fPng_ptr, fInfo_ptr); | 878 if (options.fSubset) { |
| 879 firstRow = options.fSubset->top(); |
| 880 lastRow = options.fSubset->bottom() - 1; |
| 881 } else { |
| 882 firstRow = 0; |
| 883 lastRow = dstInfo.height() - 1; |
| 884 } |
| 885 this->setRange(firstRow, lastRow); |
| 886 return kSuccess; |
| 887 } |
| 781 | 888 |
| 782 return kSuccess; | 889 SkCodec::Result SkPngCodec::onIncrementalDecode(std::function<void*(int)> callba
ck, |
| 890 int* rowsDecoded) { |
| 891 return this->decode(callback, rowsDecoded); |
| 783 } | 892 } |
| 784 | 893 |
| 785 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { | 894 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { |
| 786 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | 895 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); |
| 787 if (colorPtr) { | 896 if (colorPtr) { |
| 788 return get_color_table_fill_value(colorType, colorPtr, 0); | 897 return get_color_table_fill_value(colorType, colorPtr, 0); |
| 789 } | 898 } |
| 790 return INHERITED::onGetFillValue(colorType); | 899 return INHERITED::onGetFillValue(colorType); |
| 791 } | 900 } |
| 792 | 901 |
| 793 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { | 902 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { |
| 794 SkAutoTDelete<SkStream> streamDeleter(stream); | 903 SkAutoTDelete<SkStream> streamDeleter(stream); |
| 795 | 904 |
| 796 SkCodec* outCodec; | 905 SkCodec* outCodec = nullptr; |
| 797 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { | 906 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { |
| 798 // Codec has taken ownership of the stream. | 907 // Codec has taken ownership of the stream. |
| 799 SkASSERT(outCodec); | 908 SkASSERT(outCodec); |
| 800 streamDeleter.release(); | 909 streamDeleter.release(); |
| 801 return outCodec; | 910 return outCodec; |
| 802 } | 911 } |
| 803 | 912 |
| 804 return nullptr; | 913 return nullptr; |
| 805 } | 914 } |
| OLD | NEW |