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