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