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