Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Side by Side Diff: src/codec/SkPngCodec.cpp

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

Powered by Google App Engine
This is Rietveld 408576698