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

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

Powered by Google App Engine
This is Rietveld 408576698