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 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
254 | 357 |
255 #endif // LIBPNG >= 1.6 | 358 #endif // LIBPNG >= 1.6 |
256 | 359 |
257 // Finally, what should we do if there is no color space information in the
PNG? | 360 // 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 | 361 // 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. | 362 // "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. | 363 // But should we guess sRGB? Most images are sRGB, even if they don't speci
fy. |
261 return nullptr; | 364 return nullptr; |
262 } | 365 } |
263 | 366 |
264 static int bytes_per_pixel(int bitsPerPixel) { | 367 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: | 368 public: |
273 SkPngScanlineDecoder(int width, int height, const SkEncodedInfo& info, SkStr
eam* stream, | 369 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, | 370 SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr, i
nt bitDepth, |
275 sk_sp<SkColorSpace> colorSpace) | 371 sk_sp<SkColorSpace> colorSpace) |
276 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, 1, | 372 : INHERITED(width, height, info, stream, reader, png_ptr, info_ptr, bitD
epth, |
277 colorSpace) | 373 std::move(colorSpace)) |
278 , fSrcRow(nullptr) | 374 , fLinesDecoded(0) |
| 375 , fDst(nullptr) |
| 376 , fRowBytes(0) |
| 377 , fFirstRow(0) |
| 378 , fLastRow(0) |
279 {} | 379 {} |
280 | 380 |
281 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | 381 static void AllRowsCallback(png_structp png_ptr, png_bytep row, png_uint_32
rowNum, int /*pass*/) { |
282 SkPMColor ctable[], int* ctableCount) override { | 382 GetDecoder(png_ptr)->allRowsCallback(row, rowNum); |
283 if (!conversion_possible(dstInfo, this->getInfo())) { | 383 } |
284 return kInvalidConversion; | 384 |
285 } | 385 static void RowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowN
um, int /*pass*/) { |
286 | 386 GetDecoder(png_ptr)->rowCallback(row, rowNum); |
287 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | 387 } |
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: | 388 private: |
332 SkAutoTMalloc<uint8_t> fStorage; | 389 int fLinesDecoded; // FIXME: Move to baseclass? |
333 uint8_t* fSrcRow; | 390 void* fDst; |
| 391 size_t fRowBytes; |
| 392 |
| 393 // Variables for partial decode |
| 394 int fFirstRow; // FIXME: Move to baseclass? |
| 395 int fLastRow; |
334 | 396 |
335 typedef SkPngCodec INHERITED; | 397 typedef SkPngCodec INHERITED; |
| 398 |
| 399 static SkPngNormalDecoder* GetDecoder(png_structp png_ptr) { |
| 400 return static_cast<SkPngNormalDecoder*>(png_get_progressive_ptr(png_ptr)
); |
| 401 } |
| 402 |
| 403 Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override
{ |
| 404 const int height = this->getInfo().height(); |
| 405 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, AllRowsCallb
ack, nullptr); |
| 406 fDst = dst; |
| 407 fRowBytes = rowBytes; |
| 408 |
| 409 fLinesDecoded = 0; |
| 410 |
| 411 this->processData(); |
| 412 |
| 413 if (fLinesDecoded == height) { |
| 414 return SkCodec::kSuccess; |
| 415 } |
| 416 |
| 417 if (rowsDecoded) { |
| 418 *rowsDecoded = fLinesDecoded; |
| 419 } |
| 420 |
| 421 return SkCodec::kIncompleteInput; |
| 422 } |
| 423 |
| 424 void allRowsCallback(png_bytep row, int rowNum) { |
| 425 SkASSERT(rowNum - fFirstRow == fLinesDecoded); |
| 426 fLinesDecoded++; |
| 427 this->swizzler()->swizzle(fDst, row); |
| 428 fDst = SkTAddOffset<void>(fDst, fRowBytes); |
| 429 } |
| 430 |
| 431 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) overrid
e { |
| 432 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, RowCallback,
nullptr); |
| 433 fFirstRow = firstRow; |
| 434 fLastRow = lastRow; |
| 435 fDst = dst; |
| 436 fRowBytes = rowBytes; |
| 437 fLinesDecoded = 0; |
| 438 } |
| 439 |
| 440 SkCodec::Result decode(int* rowsDecoded) override { |
| 441 this->processData(); |
| 442 |
| 443 if (fLinesDecoded == fLastRow - fFirstRow + 1) { |
| 444 return SkCodec::kSuccess; |
| 445 } |
| 446 |
| 447 if (rowsDecoded) { |
| 448 *rowsDecoded = fLinesDecoded; |
| 449 } |
| 450 |
| 451 return SkCodec::kIncompleteInput; |
| 452 } |
| 453 |
| 454 void rowCallback(png_bytep row, int rowNum) { |
| 455 if (rowNum < fFirstRow) { |
| 456 // Ignore this row. |
| 457 return; |
| 458 } |
| 459 |
| 460 SkASSERT(rowNum <= fLastRow); |
| 461 |
| 462 if (this->swizzler()->rowNeeded(fLinesDecoded)) { |
| 463 this->swizzler()->swizzle(fDst, row); |
| 464 fDst = SkTAddOffset<void>(fDst, fRowBytes); |
| 465 } |
| 466 |
| 467 fLinesDecoded++; |
| 468 |
| 469 if (rowNum == fLastRow) { |
| 470 // Fake error to stop decoding scanlines. |
| 471 longjmp(png_jmpbuf(this->png_ptr()), kStopDecoding); |
| 472 } |
| 473 } |
336 }; | 474 }; |
337 | 475 |
338 | 476 class SkPngInterlacedDecoder : public SkPngCodec { |
339 class SkPngInterlacedScanlineDecoder : public SkPngCodec { | |
340 public: | 477 public: |
341 SkPngInterlacedScanlineDecoder(int width, int height, const SkEncodedInfo& i
nfo, | 478 SkPngInterlacedDecoder(int width, int height, const SkEncodedInfo& info, SkS
tream* stream, |
342 SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr
, | 479 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) | 480 sk_sp<SkColorSpace> colorSpace, int numberPasses) |
344 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, | 481 : INHERITED(width, height, info, stream, reader, png_ptr, info_ptr, bitD
epth, |
345 numberPasses, colorSpace) | 482 std::move(colorSpace)) |
346 , fHeight(-1) | 483 , fNumberPasses(numberPasses) |
347 , fCanSkipRewind(false) | 484 , fFirstRow(0) |
348 { | 485 , fLastRow(0) |
349 SkASSERT(numberPasses != 1); | 486 , fLinesDecoded(0) |
350 } | 487 , fInterlacedComplete(false) |
351 | 488 , fPng_rowbytes(0) |
352 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | 489 {} |
353 SkPMColor ctable[], int* ctableCount) override { | 490 |
354 if (!conversion_possible(dstInfo, this->getInfo())) { | 491 static void InterlacedRowCallback(png_structp png_ptr, png_bytep row, png_ui
nt_32 rowNum, int pass) { |
355 return kInvalidConversion; | 492 auto decoder = static_cast<SkPngInterlacedDecoder*>(png_get_progressive_
ptr(png_ptr)); |
356 } | 493 decoder->interlacedRowCallback(row, rowNum, pass); |
357 | 494 } |
358 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | 495 |
359 ctableCount); | 496 private: |
360 if (result != kSuccess) { | 497 const int fNumberPasses; |
361 return result; | 498 int fFirstRow; |
362 } | 499 int fLastRow; |
363 | 500 void* fDst; |
364 fHeight = dstInfo.height(); | 501 size_t fRowBytes; |
365 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. | 502 int fLinesDecoded; |
366 fSrcRowBytes = this->getInfo().width() * | 503 bool fInterlacedComplete; |
367 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel())); | 504 png_uint_32 fPng_rowbytes; |
368 fGarbageRow.reset(fSrcRowBytes); | 505 SkAutoTMalloc<png_byte> fInterlaceBuffer; |
369 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); | 506 |
370 fCanSkipRewind = true; | 507 typedef SkPngCodec INHERITED; |
371 | 508 |
372 return SkCodec::kSuccess; | 509 // FIXME: Currently sharing interlaced callback for all rows and subset. It'
s not |
373 } | 510 // as expensive as the subset version of non-interlaced, but it still does e
xtra |
374 | 511 // work. |
375 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override { | 512 void interlacedRowCallback(png_bytep row, int rowNum, int pass) { |
376 // rewind stream if have previously called onGetScanlines, | 513 if (rowNum < fFirstRow || rowNum > fLastRow) { |
377 // since we need entire progressive image to get scanlines | 514 // Ignore this row |
378 if (fCanSkipRewind) { | 515 return; |
379 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. | 516 } |
380 // Next time onGetScanlines is called, we will need to rewind. | 517 |
381 fCanSkipRewind = false; | 518 png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_
rowbytes; |
| 519 png_progressive_combine_row(this->png_ptr(), oldRow, row); |
| 520 |
| 521 if (0 == pass) { |
| 522 // The first pass initializes all rows. |
| 523 SkASSERT(row); |
| 524 SkASSERT(fLinesDecoded == rowNum - fFirstRow); |
| 525 fLinesDecoded++; |
382 } else { | 526 } else { |
383 // rewindIfNeeded resets fCurrScanline, since it assumes that start | 527 SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1); |
384 // needs to be called again before scanline decoding. PNG scanline | 528 if (fNumberPasses - 1 == pass && rowNum == fLastRow) { |
385 // decoding is the exception, since it needs to rewind between | 529 // 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 | 530 // we do not care about reading anything beyond the end of the i
mage (or |
387 // reset. | 531 // beyond the last scanline requested). |
388 const int currScanline = this->nextScanline(); | 532 fInterlacedComplete = true; |
389 // This method would never be called if currScanline is -1 | 533 // Fake error to stop decoding scanlines. |
390 SkASSERT(currScanline != -1); | 534 longjmp(png_jmpbuf(this->png_ptr()), kStopDecoding); |
391 | |
392 if (!this->rewindIfNeeded()) { | |
393 return kCouldNotRewind; | |
394 } | 535 } |
395 this->updateCurrScanline(currScanline); | 536 } |
396 } | 537 } |
397 | 538 |
398 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 539 SkCodec::Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded)
override { |
399 SkCodecPrintf("setjmp long jump!\n"); | 540 const int height = this->getInfo().height(); |
400 // FIXME (msarett): Returning 0 is pessimistic. If we can complete
a single pass, | 541 this->setUpInterlaceBuffer(height); |
401 // we may be able to report that all of the memory has been initiali
zed. Even if we | 542 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. | 543 |
403 return 0; | 544 fFirstRow = 0; |
404 } | 545 fLastRow = height - 1; |
405 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); | 546 fLinesDecoded = 0; |
406 uint8_t* storagePtr = storage.get(); | 547 |
407 uint8_t* srcRow; | 548 this->processData(); |
408 const int startRow = this->nextScanline(); | 549 |
409 for (int i = 0; i < this->numberPasses(); i++) { | 550 png_bytep srcRow = fInterlaceBuffer.get(); |
410 // read rows we planned to skip into garbage row | 551 // FIXME: When resuming, this may rewrite rows that did not change. |
411 for (int y = 0; y < startRow; y++){ | 552 for (int rowNum = 0; rowNum < fLinesDecoded; rowNum++) { |
412 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); | 553 this->swizzler()->swizzle(dst, srcRow); |
413 } | 554 dst = SkTAddOffset<void>(dst, rowBytes); |
414 // read rows we care about into buffer | 555 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes); |
415 srcRow = storagePtr; | 556 } |
416 for (int y = 0; y < count; y++) { | 557 if (fInterlacedComplete) { |
417 png_read_row(this->png_ptr(), srcRow, nullptr); | 558 return SkCodec::kSuccess; |
418 srcRow += fSrcRowBytes; | 559 } |
419 } | 560 |
420 // read rows we don't want into garbage buffer | 561 if (rowsDecoded) { |
421 for (int y = 0; y < fHeight - startRow - count; y++) { | 562 *rowsDecoded = fLinesDecoded; |
422 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); | 563 } |
423 } | 564 |
424 } | 565 return SkCodec::kIncompleteInput; |
425 //swizzle the rows we care about | 566 } |
426 srcRow = storagePtr; | 567 |
427 void* dstRow = dst; | 568 void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) overrid
e { |
428 for (int y = 0; y < count; y++) { | 569 // FIXME: We could skip rows in the interlace buffer that we won't put i
n the output. |
429 this->swizzler()->swizzle(dstRow, srcRow); | 570 this->setUpInterlaceBuffer(lastRow - firstRow + 1); |
430 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | 571 png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRo
wCallback, nullptr); |
431 srcRow += fSrcRowBytes; | 572 fFirstRow = firstRow; |
432 } | 573 fLastRow = lastRow; |
433 | 574 fDst = dst; |
434 return count; | 575 fRowBytes = rowBytes; |
435 } | 576 fLinesDecoded = 0; |
436 | 577 } |
437 bool onSkipScanlines(int count) override { | 578 |
438 // The non-virtual version will update fCurrScanline. | 579 SkCodec::Result decode(int* rowsDecoded) override { |
439 return true; | 580 this->processData(); |
440 } | 581 |
441 | 582 // Now call the callback on all the rows that were decoded. |
442 SkScanlineOrder onGetScanlineOrder() const override { | 583 if (!fLinesDecoded) { |
443 return kNone_SkScanlineOrder; | 584 return SkCodec::kIncompleteInput; |
444 } | 585 } |
445 | 586 const int lastRow = fLinesDecoded + fFirstRow - 1; |
446 private: | 587 SkASSERT(lastRow <= fLastRow); |
447 int fHeight; | 588 |
448 size_t fSrcRowBytes; | 589 // FIXME: For resuming interlace, we may swizzle a row that hasn't chang
ed. But it |
449 SkAutoMalloc fGarbageRow; | 590 // may be too tricky/expensive to handle that correctly. |
450 uint8_t* fGarbageRowPtr; | 591 png_bytep srcRow = fInterlaceBuffer.get(); |
451 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function | 592 const int sampleY = this->swizzler()->sampleY(); |
452 // is called whenever some action is taken that reads the stream and | 593 void* dst = fDst; |
453 // therefore the next call will require a rewind. So it modifies a boolean | 594 for (int rowNum = fFirstRow; rowNum <= lastRow; rowNum += sampleY) { |
454 // to note that the *next* time it is called a rewind is needed. | 595 this->swizzler()->swizzle(dst, srcRow); |
455 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling | 596 dst = SkTAddOffset<void>(dst, fRowBytes); |
456 // onStartScanlineDecode followed by onGetScanlines does *not* require a | 597 srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes * sampleY); |
457 // rewind. Since rewindIfNeeded does not have this flexibility, we need to | 598 } |
458 // add another layer. | 599 |
459 bool fCanSkipRewind; | 600 if (fInterlacedComplete) { |
460 | 601 return SkCodec::kSuccess; |
461 typedef SkPngCodec INHERITED; | 602 } |
| 603 |
| 604 if (rowsDecoded) { |
| 605 *rowsDecoded = fLinesDecoded; |
| 606 } |
| 607 return SkCodec::kIncompleteInput; |
| 608 } |
| 609 |
| 610 void setUpInterlaceBuffer(int height) { |
| 611 fPng_rowbytes = png_get_rowbytes(this->png_ptr(), this->info_ptr()); |
| 612 fInterlaceBuffer.reset(fPng_rowbytes * height); |
| 613 fInterlacedComplete = false; |
| 614 } |
462 }; | 615 }; |
463 | 616 |
464 // Reads the header and initializes the output fields, if not NULL. | 617 // Reads the header and initializes the output fields, if not NULL. |
465 // | 618 // |
466 // @param stream Input data. Will be read to get enough information to properly | 619 // @param stream Input data. Will be read to get enough information to properly |
467 // setup the codec. | 620 // setup the codec. |
468 // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. | 621 // @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 | 622 // 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. | 623 // 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 | 624 // @param outCodec Optional output variable. If non-NULL, will be set to a new |
472 // SkPngCodec on success. | 625 // SkPngCodec on success. |
473 // @param png_ptrp Optional output variable. If non-NULL, will be set to a new | 626 // @param png_ptrp Optional output variable. If non-NULL, will be set to a new |
474 // png_structp on success. | 627 // png_structp on success. |
475 // @param info_ptrp Optional output variable. If non-NULL, will be set to a new | 628 // @param info_ptrp Optional output variable. If non-NULL, will be set to a new |
476 // png_infop on success; | 629 // png_infop on success; |
477 // @return true on success, in which case the caller is responsible for calling | 630 // @return true on success, in which case the caller is responsible for calling |
478 // png_destroy_read_struct(png_ptrp, info_ptrp). | 631 // png_destroy_read_struct(png_ptrp, info_ptrp). |
479 // If it returns false, the passed in fields (except stream) are unchanged. | 632 // If it returns false, the passed in fields (except stream) are unchanged. |
480 static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec
** outCodec, | 633 static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec
** outCodec, |
481 png_structp* png_ptrp, png_infop* info_ptrp) { | 634 png_structp* png_ptrp, png_infop* info_ptrp) { |
482 // The image is known to be a PNG. Decode enough to know the SkImageInfo. | 635 // 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, | 636 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, |
484 sk_error_fn, sk_warning_fn); | 637 sk_error_fn, sk_warning_fn); |
485 if (!png_ptr) { | 638 if (!png_ptr) { |
486 return false; | 639 return false; |
487 } | 640 } |
488 | 641 |
489 AutoCleanPng autoClean(png_ptr); | 642 AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec); |
490 | 643 |
491 png_infop info_ptr = png_create_info_struct(png_ptr); | 644 png_infop info_ptr = png_create_info_struct(png_ptr); |
492 if (info_ptr == nullptr) { | 645 if (info_ptr == nullptr) { |
493 return false; | 646 return false; |
494 } | 647 } |
495 | 648 |
496 autoClean.setInfoPtr(info_ptr); | 649 autoClean.setInfoPtr(info_ptr); |
497 | 650 |
498 // FIXME: Could we use the return value of setjmp to specify the type of | 651 // FIXME: Could we use the return value of setjmp to specify the type of |
499 // error? | 652 // error? |
500 if (setjmp(png_jmpbuf(png_ptr))) { | 653 if (setjmp(png_jmpbuf(png_ptr))) { |
501 return false; | 654 return false; |
502 } | 655 } |
503 | 656 |
504 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn); | |
505 | |
506 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED | 657 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED |
507 // Hookup our chunkReader so we can see any user-chunks the caller may be in
terested in. | 658 // 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 | 659 // This needs to be installed before we read the png header. Android may st
ore ninepatch |
509 // chunks in the header. | 660 // chunks in the header. |
510 if (chunkReader) { | 661 if (chunkReader) { |
511 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*
)"", 0); | 662 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); | 663 png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_use
r_chunk); |
513 } | 664 } |
514 #endif | 665 #endif |
515 | 666 |
516 // The call to png_read_info() gives us all of the information from the | 667 const bool decodedBounds = autoClean.decodeBounds(); |
517 // PNG file before the first IDAT (image data chunk). | 668 |
518 png_read_info(png_ptr, info_ptr); | 669 if (!decodedBounds) { |
| 670 return false; |
| 671 } |
| 672 |
| 673 // On success, decodeBounds releases ownership of png_ptr and info_ptr. |
| 674 if (png_ptrp) { |
| 675 *png_ptrp = png_ptr; |
| 676 } |
| 677 if (info_ptrp) { |
| 678 *info_ptrp = info_ptr; |
| 679 } |
| 680 |
| 681 // decodeBounds takes care of setting outCodec |
| 682 if (outCodec) { |
| 683 SkASSERT(*outCodec); |
| 684 } |
| 685 return true; |
| 686 } |
| 687 |
| 688 void AutoCleanPng::infoCallback() { |
519 png_uint_32 origWidth, origHeight; | 689 png_uint_32 origWidth, origHeight; |
520 int bitDepth, encodedColorType; | 690 int bitDepth, encodedColorType; |
521 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, | 691 png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth, |
522 &encodedColorType, nullptr, nullptr, nullptr); | 692 &encodedColorType, nullptr, nullptr, nullptr); |
523 | 693 |
524 // Tell libpng to strip 16 bit/color files down to 8 bits/color. | 694 // 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 | 695 // TODO: Should we handle this in SkSwizzler? Could this also benefit |
526 // RAW decodes? | 696 // RAW decodes? |
527 if (bitDepth == 16) { | 697 if (bitDepth == 16) { |
528 SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType); | 698 SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType); |
529 png_set_strip_16(png_ptr); | 699 png_set_strip_16(fPng_ptr); |
530 } | 700 } |
531 | 701 |
532 // Now determine the default colorType and alphaType and set the required tr
ansforms. | 702 // 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 | 703 // 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. | 704 // still depend on libpng for many of the rare and PNG-specific cases. |
535 SkEncodedInfo::Color color; | 705 SkEncodedInfo::Color color; |
536 SkEncodedInfo::Alpha alpha; | 706 SkEncodedInfo::Alpha alpha; |
537 switch (encodedColorType) { | 707 switch (encodedColorType) { |
538 case PNG_COLOR_TYPE_PALETTE: | 708 case PNG_COLOR_TYPE_PALETTE: |
539 // Extract multiple pixels with bit depths of 1, 2, and 4 from a sin
gle | 709 // 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). | 710 // byte into separate bytes (useful for paletted and grayscale image
s). |
541 if (bitDepth < 8) { | 711 if (bitDepth < 8) { |
542 // TODO: Should we use SkSwizzler here? | 712 // TODO: Should we use SkSwizzler here? |
543 png_set_packing(png_ptr); | 713 png_set_packing(fPng_ptr); |
544 } | 714 } |
545 | 715 |
546 color = SkEncodedInfo::kPalette_Color; | 716 color = SkEncodedInfo::kPalette_Color; |
547 // Set the alpha depending on if a transparency chunk exists. | 717 // Set the alpha depending on if a transparency chunk exists. |
548 alpha = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? | 718 alpha = png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS) ? |
549 SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alph
a; | 719 SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alph
a; |
550 break; | 720 break; |
551 case PNG_COLOR_TYPE_RGB: | 721 case PNG_COLOR_TYPE_RGB: |
552 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | 722 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { |
553 // Convert to RGBA if transparency chunk exists. | 723 // Convert to RGBA if transparency chunk exists. |
554 png_set_tRNS_to_alpha(png_ptr); | 724 png_set_tRNS_to_alpha(fPng_ptr); |
555 color = SkEncodedInfo::kRGBA_Color; | 725 color = SkEncodedInfo::kRGBA_Color; |
556 alpha = SkEncodedInfo::kBinary_Alpha; | 726 alpha = SkEncodedInfo::kBinary_Alpha; |
557 } else { | 727 } else { |
558 color = SkEncodedInfo::kRGB_Color; | 728 color = SkEncodedInfo::kRGB_Color; |
559 alpha = SkEncodedInfo::kOpaque_Alpha; | 729 alpha = SkEncodedInfo::kOpaque_Alpha; |
560 } | 730 } |
561 break; | 731 break; |
562 case PNG_COLOR_TYPE_GRAY: | 732 case PNG_COLOR_TYPE_GRAY: |
563 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/p
ixel. | 733 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/p
ixel. |
564 if (bitDepth < 8) { | 734 if (bitDepth < 8) { |
565 // TODO: Should we use SkSwizzler here? | 735 // TODO: Should we use SkSwizzler here? |
566 png_set_expand_gray_1_2_4_to_8(png_ptr); | 736 png_set_expand_gray_1_2_4_to_8(fPng_ptr); |
567 } | 737 } |
568 | 738 |
569 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | 739 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { |
570 png_set_tRNS_to_alpha(png_ptr); | 740 png_set_tRNS_to_alpha(fPng_ptr); |
571 color = SkEncodedInfo::kGrayAlpha_Color; | 741 color = SkEncodedInfo::kGrayAlpha_Color; |
572 alpha = SkEncodedInfo::kBinary_Alpha; | 742 alpha = SkEncodedInfo::kBinary_Alpha; |
573 } else { | 743 } else { |
574 color = SkEncodedInfo::kGray_Color; | 744 color = SkEncodedInfo::kGray_Color; |
575 alpha = SkEncodedInfo::kOpaque_Alpha; | 745 alpha = SkEncodedInfo::kOpaque_Alpha; |
576 } | 746 } |
577 break; | 747 break; |
578 case PNG_COLOR_TYPE_GRAY_ALPHA: | 748 case PNG_COLOR_TYPE_GRAY_ALPHA: |
579 color = SkEncodedInfo::kGrayAlpha_Color; | 749 color = SkEncodedInfo::kGrayAlpha_Color; |
580 alpha = SkEncodedInfo::kUnpremul_Alpha; | 750 alpha = SkEncodedInfo::kUnpremul_Alpha; |
581 break; | 751 break; |
582 case PNG_COLOR_TYPE_RGBA: | 752 case PNG_COLOR_TYPE_RGBA: |
583 color = SkEncodedInfo::kRGBA_Color; | 753 color = SkEncodedInfo::kRGBA_Color; |
584 alpha = SkEncodedInfo::kUnpremul_Alpha; | 754 alpha = SkEncodedInfo::kUnpremul_Alpha; |
585 break; | 755 break; |
586 default: | 756 default: |
587 // All the color types have been covered above. | 757 // All the color types have been covered above. |
588 SkASSERT(false); | 758 SkASSERT(false); |
589 color = SkEncodedInfo::kRGBA_Color; | 759 color = SkEncodedInfo::kRGBA_Color; |
590 alpha = SkEncodedInfo::kUnpremul_Alpha; | 760 alpha = SkEncodedInfo::kUnpremul_Alpha; |
591 } | 761 } |
592 | 762 |
593 int numberPasses = png_set_interlace_handling(png_ptr); | 763 const int numberPasses = png_set_interlace_handling(fPng_ptr); |
594 | 764 |
595 autoClean.release(); | 765 if (fOutCodec) { |
596 if (png_ptrp) { | 766 SkASSERT(nullptr == *fOutCodec); |
597 *png_ptrp = png_ptr; | 767 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); | 768 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); |
606 | |
607 if (1 == numberPasses) { | 769 if (1 == numberPasses) { |
608 *outCodec = new SkPngScanlineDecoder(origWidth, origHeight, info, st
ream, | 770 *fOutCodec = new SkPngNormalDecoder(origWidth, origHeight, info, fSt
ream, |
609 chunkReader, png_ptr, info_ptr, bitDepth, colorSpace); | 771 fChunkReader, fPng_ptr, fInfo_ptr, bitDepth, std::move(color
Space)); |
610 } else { | 772 } else { |
611 *outCodec = new SkPngInterlacedScanlineDecoder(origWidth, origHeight
, info, stream, | 773 *fOutCodec = new SkPngInterlacedDecoder(origWidth, origHeight, info,
fStream, |
612 chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colo
rSpace); | 774 fChunkReader, fPng_ptr, fInfo_ptr, bitDepth, std::move(color
Space), |
| 775 numberPasses); |
613 } | 776 } |
614 } | 777 } |
615 | 778 |
616 return true; | 779 fDecodedBounds = true; |
| 780 // 1 tells libpng to save any extra data. We may be able to be more efficien
t by saving |
| 781 // it ourselves. |
| 782 png_process_data_pause(fPng_ptr, 1); |
| 783 |
| 784 // Release the pointers, which are now owned by the codec or the caller is e
xpected to |
| 785 // take ownership. |
| 786 this->releasePngPtrs(); |
617 } | 787 } |
618 | 788 |
619 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, | 789 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, |
620 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i
nfop info_ptr, | 790 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i
nfop info_ptr, |
621 int bitDepth, int numberPasses, sk_sp<SkColorSpace> color
Space) | 791 int bitDepth, sk_sp<SkColorSpace> colorSpace) |
622 : INHERITED(width, height, info, stream, colorSpace) | 792 : INHERITED(width, height, info, stream, colorSpace) |
623 , fPngChunkReader(SkSafeRef(chunkReader)) | 793 , fPngChunkReader(SkSafeRef(chunkReader)) |
624 , fPng_ptr(png_ptr) | 794 , fPng_ptr(png_ptr) |
625 , fInfo_ptr(info_ptr) | 795 , fInfo_ptr(info_ptr) |
626 , fNumberPasses(numberPasses) | |
627 , fBitDepth(bitDepth) | 796 , fBitDepth(bitDepth) |
628 {} | 797 {} |
629 | 798 |
630 SkPngCodec::~SkPngCodec() { | 799 SkPngCodec::~SkPngCodec() { |
631 this->destroyReadStruct(); | 800 this->destroyReadStruct(); |
632 } | 801 } |
633 | 802 |
634 void SkPngCodec::destroyReadStruct() { | 803 void SkPngCodec::destroyReadStruct() { |
635 if (fPng_ptr) { | 804 if (fPng_ptr) { |
636 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr | 805 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr |
637 SkASSERT(fInfo_ptr); | 806 SkASSERT(fInfo_ptr); |
638 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr); | 807 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr); |
639 fPng_ptr = nullptr; | 808 fPng_ptr = nullptr; |
640 fInfo_ptr = nullptr; | 809 fInfo_ptr = nullptr; |
641 } | 810 } |
642 } | 811 } |
643 | 812 |
644 /////////////////////////////////////////////////////////////////////////////// | 813 /////////////////////////////////////////////////////////////////////////////// |
645 // Getting the pixels | 814 // Getting the pixels |
646 /////////////////////////////////////////////////////////////////////////////// | 815 /////////////////////////////////////////////////////////////////////////////// |
647 | 816 |
648 SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, | 817 bool SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
649 const Options& options, | 818 const Options& options, |
650 SkPMColor ctable[], | 819 SkPMColor ctable[], |
651 int* ctableCount) { | 820 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))) { | 821 if (setjmp(png_jmpbuf(fPng_ptr))) { |
655 SkCodecPrintf("setjmp long jump!\n"); | 822 return false; |
656 return kInvalidInput; | |
657 } | 823 } |
658 png_read_update_info(fPng_ptr, fInfo_ptr); | 824 png_read_update_info(fPng_ptr, fInfo_ptr); |
659 | 825 |
660 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { | 826 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { |
661 if (!this->createColorTable(requestedInfo.colorType(), | 827 if (!this->createColorTable(requestedInfo.colorType(), |
662 kPremul_SkAlphaType == requestedInfo.alphaType(), ctableCount))
{ | 828 kPremul_SkAlphaType == requestedInfo.alphaType(), ctableCount))
{ |
663 return kInvalidInput; | 829 return false; |
664 } | 830 } |
665 } | 831 } |
666 | 832 |
667 // Copy the color table to the client if they request kIndex8 mode | 833 // Copy the color table to the client if they request kIndex8 mode |
668 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); | 834 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); |
669 | 835 |
670 // Create the swizzler. SkPngCodec retains ownership of the color table. | 836 // Create the swizzler. SkPngCodec retains ownership of the color table. |
671 const SkPMColor* colors = get_color_ptr(fColorTable.get()); | 837 const SkPMColor* colors = get_color_ptr(fColorTable.get()); |
672 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, r
equestedInfo, | 838 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, r
equestedInfo, |
673 options)); | 839 options)); |
674 SkASSERT(fSwizzler); | 840 SkASSERT(fSwizzler); |
675 | 841 |
676 return kSuccess; | 842 return true; |
677 } | 843 } |
678 | 844 |
679 | 845 |
680 bool SkPngCodec::onRewind() { | 846 bool SkPngCodec::onRewind() { |
681 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header | 847 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header |
682 // succeeds, they will be repopulated, and if it fails, they will | 848 // succeeds, they will be repopulated, and if it fails, they will |
683 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will | 849 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will |
684 // come through this function which will rewind and again attempt | 850 // come through this function which will rewind and again attempt |
685 // to reinitialize them. | 851 // to reinitialize them. |
686 this->destroyReadStruct(); | 852 this->destroyReadStruct(); |
(...skipping 15 matching lines...) Expand all Loading... |
702 int* rowsDecoded) { | 868 int* rowsDecoded) { |
703 if (!conversion_possible(requestedInfo, this->getInfo())) { | 869 if (!conversion_possible(requestedInfo, this->getInfo())) { |
704 return kInvalidConversion; | 870 return kInvalidConversion; |
705 } | 871 } |
706 if (options.fSubset) { | 872 if (options.fSubset) { |
707 // Subsets are not supported. | 873 // Subsets are not supported. |
708 return kUnimplemented; | 874 return kUnimplemented; |
709 } | 875 } |
710 | 876 |
711 // Note that ctable and ctableCount may be modified if there is a color tabl
e | 877 // 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); | 878 if (!this->initializeSwizzler(requestedInfo, options, ctable, ctableCount))
{ |
713 if (result != kSuccess) { | 879 return kInvalidInput; // or parameters? |
714 return result; | |
715 } | 880 } |
716 | 881 |
717 const int width = requestedInfo.width(); | 882 return this->decodeAllRows(dst, dstRowBytes, rowsDecoded); |
718 const int height = requestedInfo.height(); | 883 } |
719 const int bpp = bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()); | |
720 const size_t srcRowBytes = width * bpp; | |
721 | 884 |
722 // FIXME: Could we use the return value of setjmp to specify the type of | 885 SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo, |
723 // error? | 886 void* dst, size_t rowBytes, const SkCodec::Options& options, |
724 int row = 0; | 887 SkPMColor* ctable, int* ctableCount) { |
725 // This must be declared above the call to setjmp to avoid memory leaks on i
ncomplete images. | 888 if (!conversion_possible(dstInfo, this->getInfo())) { |
726 SkAutoTMalloc<uint8_t> storage; | 889 return kInvalidConversion; |
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 } | 890 } |
747 | 891 |
748 // FIXME: We could split these out based on subclass. | 892 if (!this->initializeSwizzler(dstInfo, options, ctable, ctableCount)) { |
749 void* dstRow = dst; | 893 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 } | 894 } |
778 | 895 |
779 // read rest of file, and get additional comment and time chunks in info_ptr | 896 int firstRow, lastRow; |
780 png_read_end(fPng_ptr, fInfo_ptr); | 897 if (options.fSubset) { |
| 898 firstRow = options.fSubset->top(); |
| 899 lastRow = options.fSubset->bottom() - 1; |
| 900 } else { |
| 901 firstRow = 0; |
| 902 lastRow = dstInfo.height() - 1; |
| 903 } |
| 904 this->setRange(firstRow, lastRow, dst, rowBytes); |
| 905 return kSuccess; |
| 906 } |
781 | 907 |
782 return kSuccess; | 908 SkCodec::Result SkPngCodec::onIncrementalDecode(int* rowsDecoded) { |
| 909 return this->decode(rowsDecoded); |
783 } | 910 } |
784 | 911 |
785 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { | 912 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { |
786 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | 913 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); |
787 if (colorPtr) { | 914 if (colorPtr) { |
788 return get_color_table_fill_value(colorType, colorPtr, 0); | 915 return get_color_table_fill_value(colorType, colorPtr, 0); |
789 } | 916 } |
790 return INHERITED::onGetFillValue(colorType); | 917 return INHERITED::onGetFillValue(colorType); |
791 } | 918 } |
792 | 919 |
793 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { | 920 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { |
794 SkAutoTDelete<SkStream> streamDeleter(stream); | 921 SkAutoTDelete<SkStream> streamDeleter(stream); |
795 | 922 |
796 SkCodec* outCodec; | 923 SkCodec* outCodec = nullptr; |
797 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { | 924 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { |
798 // Codec has taken ownership of the stream. | 925 // Codec has taken ownership of the stream. |
799 SkASSERT(outCodec); | 926 SkASSERT(outCodec); |
800 streamDeleter.release(); | 927 streamDeleter.release(); |
801 return outCodec; | 928 return outCodec; |
802 } | 929 } |
803 | 930 |
804 return nullptr; | 931 return nullptr; |
805 } | 932 } |
OLD | NEW |