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

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

Issue 1613043003: Rename SkCodec_libpng to SkPngCodec (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 4 years, 11 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
« no previous file with comments | « src/codec/SkCodec_libpng.h ('k') | src/codec/SkIcoCodec.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkCodec_libpng.h"
9 #include "SkCodecPriv.h"
10 #include "SkColorPriv.h"
11 #include "SkColorTable.h"
12 #include "SkBitmap.h"
13 #include "SkMath.h"
14 #include "SkSize.h"
15 #include "SkStream.h"
16 #include "SkSwizzler.h"
17 #include "SkTemplates.h"
18
19 ///////////////////////////////////////////////////////////////////////////////
20 // Helper macros
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #ifndef png_jmpbuf
24 # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
25 #endif
26
27 /* These were dropped in libpng >= 1.4 */
28 #ifndef png_infopp_NULL
29 #define png_infopp_NULL nullptr
30 #endif
31
32 #ifndef png_bytepp_NULL
33 #define png_bytepp_NULL nullptr
34 #endif
35
36 #ifndef int_p_NULL
37 #define int_p_NULL nullptr
38 #endif
39
40 #ifndef png_flush_ptr_NULL
41 #define png_flush_ptr_NULL nullptr
42 #endif
43
44 ///////////////////////////////////////////////////////////////////////////////
45 // Callback functions
46 ///////////////////////////////////////////////////////////////////////////////
47
48 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
49 SkCodecPrintf("------ png error %s\n", msg);
50 longjmp(png_jmpbuf(png_ptr), 1);
51 }
52
53 void sk_warning_fn(png_structp, png_const_charp msg) {
54 SkCodecPrintf("----- png warning %s\n", msg);
55 }
56
57 static void sk_read_fn(png_structp png_ptr, png_bytep data,
58 png_size_t length) {
59 SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr));
60 const size_t bytes = stream->read(data, length);
61 if (bytes != length) {
62 // FIXME: We want to report the fact that the stream was truncated.
63 // One way to do that might be to pass a enum to longjmp so setjmp can
64 // specify the failure.
65 png_error(png_ptr, "Read Error!");
66 }
67 }
68
69 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
70 static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
71 SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(pn g_ptr);
72 // readChunk() returning true means continue decoding
73 return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk-> size) ? 1 : -1;
74 }
75 #endif
76
77 ///////////////////////////////////////////////////////////////////////////////
78 // Helpers
79 ///////////////////////////////////////////////////////////////////////////////
80
81 class AutoCleanPng : public SkNoncopyable {
82 public:
83 AutoCleanPng(png_structp png_ptr)
84 : fPng_ptr(png_ptr)
85 , fInfo_ptr(nullptr) {}
86
87 ~AutoCleanPng() {
88 // fInfo_ptr will never be non-nullptr unless fPng_ptr is.
89 if (fPng_ptr) {
90 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr;
91 png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL);
92 }
93 }
94
95 void setInfoPtr(png_infop info_ptr) {
96 SkASSERT(nullptr == fInfo_ptr);
97 fInfo_ptr = info_ptr;
98 }
99
100 void detach() {
101 fPng_ptr = nullptr;
102 fInfo_ptr = nullptr;
103 }
104
105 private:
106 png_structp fPng_ptr;
107 png_infop fInfo_ptr;
108 };
109 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
110
111 //checks if there is transparency info in the tRNS chunk
112 //image types which could have data in the tRNS chunk include: Index8, Gray8, RG B
113 static bool has_transparency_in_tRNS(png_structp png_ptr,
114 png_infop info_ptr) {
115 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
116 return false;
117 }
118
119 png_bytep trans;
120 int num_trans;
121 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, nullptr);
122 return num_trans > 0;
123 }
124
125 // Method for coverting to either an SkPMColor or a similarly packed
126 // unpremultiplied color.
127 typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
128
129 // Note: SkColorTable claims to store SkPMColors, which is not necessarily
130 // the case here.
131 bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) {
132 int numPalette;
133 png_colorp palette;
134 png_bytep trans;
135
136 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) {
137 return false;
138 }
139
140 // Note: These are not necessarily SkPMColors
141 SkPMColor colorStorage[256]; // worst-case storage
142 SkPMColor* colorPtr = colorStorage;
143
144 int numTrans;
145 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
146 png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, nullptr);
147 } else {
148 numTrans = 0;
149 }
150
151 // check for bad images that might make us crash
152 if (numTrans > numPalette) {
153 numTrans = numPalette;
154 }
155
156 int index = 0;
157
158 // Choose which function to use to create the color table. If the final dest ination's
159 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
160 PackColorProc proc;
161 if (premultiply) {
162 proc = &SkPreMultiplyARGB;
163 } else {
164 proc = &SkPackARGB32NoCheck;
165 }
166 for (; index < numTrans; index++) {
167 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue );
168 palette++;
169 }
170
171 for (; index < numPalette; index++) {
172 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette-> blue);
173 palette++;
174 }
175
176 /* BUGGY IMAGE WORKAROUND
177 Invalid images could contain pixel values that are greater than the numb er of palette
178 entries. Since we use pixel values as indices into the palette this coul d result in reading
179 beyond the end of the palette which could leak the contents of uninitial ized memory. To
180 ensure this doesn't happen, we grow the colortable to the maximum size t hat can be
181 addressed by the bitdepth of the image and fill it with the last palette color or black if
182 the palette is empty (really broken image).
183 */
184 int colorCount = SkTMax(numPalette, 1 << SkTMin(fBitDepth, 8));
185 SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0) ;
186 for (; index < colorCount; index++) {
187 *colorPtr++ = lastColor;
188 }
189
190 // Set the new color count
191 if (ctableCount != nullptr) {
192 *ctableCount = colorCount;
193 }
194
195 fColorTable.reset(new SkColorTable(colorStorage, colorCount));
196 return true;
197 }
198
199 ///////////////////////////////////////////////////////////////////////////////
200 // Creation
201 ///////////////////////////////////////////////////////////////////////////////
202
203 bool SkPngCodec::IsPng(const char* buf, size_t bytesRead) {
204 return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead);
205 }
206
207 // Reads the header and initializes the output fields, if not NULL.
208 //
209 // @param stream Input data. Will be read to get enough information to properly
210 // setup the codec.
211 // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL.
212 // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is
213 // expected to continue to own it for the lifetime of the png_ptr.
214 // @param png_ptrp Optional output variable. If non-NULL, will be set to a new
215 // png_structp on success.
216 // @param info_ptrp Optional output variable. If non-NULL, will be set to a new
217 // png_infop on success;
218 // @param imageInfo Optional output variable. If non-NULL, will be set to
219 // reflect the properties of the encoded image on success.
220 // @param bitDepthPtr Optional output variable. If non-NULL, will be set to the
221 // bit depth of the encoded image on success.
222 // @param numberPassesPtr Optional output variable. If non-NULL, will be set to
223 // the number_passes of the encoded image on success.
224 // @return true on success, in which case the caller is responsible for calling
225 // png_destroy_read_struct(png_ptrp, info_ptrp).
226 // If it returns false, the passed in fields (except stream) are unchanged.
227 static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader,
228 png_structp* png_ptrp, png_infop* info_ptrp,
229 SkImageInfo* imageInfo, int* bitDepthPtr, int* numberPas sesPtr) {
230 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
231 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
232 sk_error_fn, sk_warning_fn);
233 if (!png_ptr) {
234 return false;
235 }
236
237 AutoCleanPng autoClean(png_ptr);
238
239 png_infop info_ptr = png_create_info_struct(png_ptr);
240 if (info_ptr == nullptr) {
241 return false;
242 }
243
244 autoClean.setInfoPtr(info_ptr);
245
246 // FIXME: Could we use the return value of setjmp to specify the type of
247 // error?
248 if (setjmp(png_jmpbuf(png_ptr))) {
249 return false;
250 }
251
252 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn);
253
254 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
255 // Hookup our chunkReader so we can see any user-chunks the caller may be in terested in.
256 // This needs to be installed before we read the png header. Android may st ore ninepatch
257 // chunks in the header.
258 if (chunkReader) {
259 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte* )"", 0);
260 png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_use r_chunk);
261 }
262 #endif
263
264 // The call to png_read_info() gives us all of the information from the
265 // PNG file before the first IDAT (image data chunk).
266 png_read_info(png_ptr, info_ptr);
267 png_uint_32 origWidth, origHeight;
268 int bitDepth, colorType;
269 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
270 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
271
272 if (bitDepthPtr) {
273 *bitDepthPtr = bitDepth;
274 }
275
276 // sanity check for size
277 {
278 int64_t size = sk_64_mul(origWidth, origHeight);
279 // now check that if we are 4-bytes per pixel, we also don't overflow
280 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
281 return false;
282 }
283 }
284
285 // Tell libpng to strip 16 bit/color files down to 8 bits/color
286 if (bitDepth == 16) {
287 png_set_strip_16(png_ptr);
288 }
289 #ifdef PNG_READ_PACK_SUPPORTED
290 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
291 // byte into separate bytes (useful for paletted and grayscale images).
292 if (bitDepth < 8) {
293 png_set_packing(png_ptr);
294 }
295 #endif
296 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
297 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
298 png_set_expand_gray_1_2_4_to_8(png_ptr);
299 }
300
301 // Now determine the default SkColorType and SkAlphaType and set required tr ansforms
302 SkColorType skColorType = kUnknown_SkColorType;
303 SkAlphaType skAlphaType = kUnknown_SkAlphaType;
304 switch (colorType) {
305 case PNG_COLOR_TYPE_PALETTE:
306 skColorType = kIndex_8_SkColorType;
307 skAlphaType = has_transparency_in_tRNS(png_ptr, info_ptr) ?
308 kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
309 break;
310 case PNG_COLOR_TYPE_RGB:
311 if (has_transparency_in_tRNS(png_ptr, info_ptr)) {
312 //convert to RGBA with tranparency information in tRNS chunk if it exists
313 png_set_tRNS_to_alpha(png_ptr);
314 skAlphaType = kUnpremul_SkAlphaType;
315 } else {
316 skAlphaType = kOpaque_SkAlphaType;
317 }
318 skColorType = kN32_SkColorType;
319 break;
320 case PNG_COLOR_TYPE_GRAY:
321 if (has_transparency_in_tRNS(png_ptr, info_ptr)) {
322 //FIXME: support gray with alpha as a color type
323 //convert to RGBA if there is transparentcy info in the tRNS chu nk
324 png_set_tRNS_to_alpha(png_ptr);
325 png_set_gray_to_rgb(png_ptr);
326 skColorType = kN32_SkColorType;
327 skAlphaType = kUnpremul_SkAlphaType;
328 } else {
329 skColorType = kGray_8_SkColorType;
330 skAlphaType = kOpaque_SkAlphaType;
331 }
332 break;
333 case PNG_COLOR_TYPE_GRAY_ALPHA:
334 //FIXME: support gray with alpha as a color type
335 //convert to RGBA
336 png_set_gray_to_rgb(png_ptr);
337 skColorType = kN32_SkColorType;
338 skAlphaType = kUnpremul_SkAlphaType;
339 break;
340 case PNG_COLOR_TYPE_RGBA:
341 skColorType = kN32_SkColorType;
342 skAlphaType = kUnpremul_SkAlphaType;
343 break;
344 default:
345 //all the color types have been covered above
346 SkASSERT(false);
347 }
348
349 int numberPasses = png_set_interlace_handling(png_ptr);
350 if (numberPassesPtr) {
351 *numberPassesPtr = numberPasses;
352 }
353
354 // FIXME: Also need to check for sRGB ( https://bug.skia.org/3471 ).
355
356 if (imageInfo) {
357 *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType, skAlp haType);
358 }
359 autoClean.detach();
360 if (png_ptrp) {
361 *png_ptrp = png_ptr;
362 }
363 if (info_ptrp) {
364 *info_ptrp = info_ptr;
365 }
366
367 return true;
368 }
369
370 SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, SkPngChunkRead er* chunkReader,
371 png_structp png_ptr, png_infop info_ptr, int bitDepth, in t numberPasses)
372 : INHERITED(info, stream)
373 , fPngChunkReader(SkSafeRef(chunkReader))
374 , fPng_ptr(png_ptr)
375 , fInfo_ptr(info_ptr)
376 , fSrcConfig(SkSwizzler::kUnknown)
377 , fNumberPasses(numberPasses)
378 , fBitDepth(bitDepth)
379 {}
380
381 SkPngCodec::~SkPngCodec() {
382 this->destroyReadStruct();
383 }
384
385 void SkPngCodec::destroyReadStruct() {
386 if (fPng_ptr) {
387 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr
388 SkASSERT(fInfo_ptr);
389 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
390 fPng_ptr = nullptr;
391 fInfo_ptr = nullptr;
392 }
393 }
394
395 ///////////////////////////////////////////////////////////////////////////////
396 // Getting the pixels
397 ///////////////////////////////////////////////////////////////////////////////
398
399 SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
400 const Options& options,
401 SkPMColor ctable[],
402 int* ctableCount) {
403 // FIXME: Could we use the return value of setjmp to specify the type of
404 // error?
405 if (setjmp(png_jmpbuf(fPng_ptr))) {
406 SkCodecPrintf("setjmp long jump!\n");
407 return kInvalidInput;
408 }
409 png_read_update_info(fPng_ptr, fInfo_ptr);
410
411 //srcColorType was determined in read_header() which determined png color ty pe
412 const SkColorType srcColorType = this->getInfo().colorType();
413
414 switch (srcColorType) {
415 case kIndex_8_SkColorType:
416 //decode palette to Skia format
417 fSrcConfig = SkSwizzler::kIndex;
418 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaT ype(),
419 ctableCount)) {
420 return kInvalidInput;
421 }
422 break;
423 case kGray_8_SkColorType:
424 fSrcConfig = SkSwizzler::kGray;
425 break;
426 case kN32_SkColorType:
427 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
428 fSrcConfig = SkSwizzler::kRGB;
429 } else {
430 fSrcConfig = SkSwizzler::kRGBA;
431 }
432 break;
433 default:
434 //would have exited before now if the colorType was supported by png
435 SkASSERT(false);
436 }
437
438 // Copy the color table to the client if they request kIndex8 mode
439 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
440
441 // Create the swizzler. SkPngCodec retains ownership of the color table.
442 const SkPMColor* colors = get_color_ptr(fColorTable.get());
443 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo , options));
444 if (!fSwizzler) {
445 // FIXME: CreateSwizzler could fail for another reason.
446 return kUnimplemented;
447 }
448 return kSuccess;
449 }
450
451
452 bool SkPngCodec::onRewind() {
453 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header
454 // succeeds, they will be repopulated, and if it fails, they will
455 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will
456 // come through this function which will rewind and again attempt
457 // to reinitialize them.
458 this->destroyReadStruct();
459
460 png_structp png_ptr;
461 png_infop info_ptr;
462 if (!read_header(this->stream(), fPngChunkReader.get(), &png_ptr, &info_ptr,
463 nullptr, nullptr, nullptr)) {
464 return false;
465 }
466
467 fPng_ptr = png_ptr;
468 fInfo_ptr = info_ptr;
469 return true;
470 }
471
472 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
473 size_t dstRowBytes, const Options& optio ns,
474 SkPMColor ctable[], int* ctableCount,
475 int* rowsDecoded) {
476 if (!conversion_possible(requestedInfo, this->getInfo())) {
477 return kInvalidConversion;
478 }
479 if (options.fSubset) {
480 // Subsets are not supported.
481 return kUnimplemented;
482 }
483
484 // Note that ctable and ctableCount may be modified if there is a color tabl e
485 const Result result = this->initializeSwizzler(requestedInfo, options, ctabl e, ctableCount);
486 if (result != kSuccess) {
487 return result;
488 }
489 // FIXME: Could we use the return value of setjmp to specify the type of
490 // error?
491 int row = 0;
492 // This must be declared above the call to setjmp to avoid memory leaks on i ncomplete images.
493 SkAutoTMalloc<uint8_t> storage;
494 if (setjmp(png_jmpbuf(fPng_ptr))) {
495 // Assume that any error that occurs while reading rows is caused by an incomplete input.
496 if (fNumberPasses > 1) {
497 // FIXME (msarett): Handle incomplete interlaced pngs.
498 return kInvalidInput;
499 }
500 // FIXME: We do a poor job on incomplete pngs compared to other decoders (ex: Chromium,
501 // Ubuntu Image Viewer). This is because we use the default buffer size in libpng (8192
502 // bytes), and if we can't fill the buffer, we immediately fail.
503 // For example, if we try to read 8192 bytes, and the image (incorrectly ) only contains
504 // half that, which may have been enough to contain a non-zero number of lines, we fail
505 // when we could have decoded a few more lines and then failed.
506 // The read function that we provide for libpng has no way of indicating that we have
507 // made a partial read.
508 // Making our buffer size smaller improves our incomplete decodes, but w hat impact does
509 // it have on regular decode performance? Should we investigate using a different API
510 // instead of png_read_row(s)? Chromium uses png_process_data.
511 *rowsDecoded = row;
512 return kIncompleteInput;
513 }
514
515 // FIXME: We could split these out based on subclass.
516 void* dstRow = dst;
517 if (fNumberPasses > 1) {
518 const int width = requestedInfo.width();
519 const int height = requestedInfo.height();
520 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
521 const size_t srcRowBytes = width * bpp;
522
523 storage.reset(width * height * bpp);
524 uint8_t* const base = storage.get();
525
526 for (int i = 0; i < fNumberPasses; i++) {
527 uint8_t* srcRow = base;
528 for (int y = 0; y < height; y++) {
529 uint8_t* bmRow = srcRow;
530 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
531 srcRow += srcRowBytes;
532 }
533 }
534
535 // Now swizzle it.
536 uint8_t* srcRow = base;
537 for (int y = 0; y < height; y++) {
538 fSwizzler->swizzle(dstRow, srcRow);
539 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
540 srcRow += srcRowBytes;
541 }
542 } else {
543 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConf ig));
544 uint8_t* srcRow = storage.get();
545 for (; row < requestedInfo.height(); row++) {
546 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
547 // FIXME: Only call IsOpaque once, outside the loop. Same for onGetS canlines.
548 fSwizzler->swizzle(dstRow, srcRow);
549 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
550 }
551 }
552
553 // FIXME: do we need substituteTranspColor? Note that we cannot do it for
554 // scanline decoding, but we could do it here. Alternatively, we could do
555 // it as we go, instead of in post-processing like SkPNGImageDecoder.
556
557 if (setjmp(png_jmpbuf(fPng_ptr))) {
558 // We've already read all the scanlines. This is a success.
559 return kSuccess;
560 }
561
562 // read rest of file, and get additional comment and time chunks in info_ptr
563 png_read_end(fPng_ptr, fInfo_ptr);
564
565 return kSuccess;
566 }
567
568 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType ) const {
569 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
570 if (colorPtr) {
571 return get_color_table_fill_value(colorType, colorPtr, 0);
572 }
573 return INHERITED::onGetFillValue(colorType, alphaType);
574 }
575
576 // Subclass of SkPngCodec which supports scanline decoding
577 class SkPngScanlineDecoder : public SkPngCodec {
578 public:
579 SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
580 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p tr, int bitDepth)
581 : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1 )
582 , fSrcRow(nullptr)
583 {}
584
585 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti ons,
586 SkPMColor ctable[], int* ctableCount) override {
587 if (!conversion_possible(dstInfo, this->getInfo())) {
588 return kInvalidConversion;
589 }
590
591 const Result result = this->initializeSwizzler(dstInfo, options, ctable,
592 ctableCount);
593 if (result != kSuccess) {
594 return result;
595 }
596
597 fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this- >srcConfig()));
598 fSrcRow = fStorage.get();
599
600 return kSuccess;
601 }
602
603 int onGetScanlines(void* dst, int count, size_t rowBytes) override {
604 // Assume that an error in libpng indicates an incomplete input.
605 int row = 0;
606 if (setjmp(png_jmpbuf(this->png_ptr()))) {
607 SkCodecPrintf("setjmp long jump!\n");
608 return row;
609 }
610
611 void* dstRow = dst;
612 for (; row < count; row++) {
613 png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
614 this->swizzler()->swizzle(dstRow, fSrcRow);
615 dstRow = SkTAddOffset<void>(dstRow, rowBytes);
616 }
617
618 return row;
619 }
620
621 bool onSkipScanlines(int count) override {
622 // Assume that an error in libpng indicates an incomplete input.
623 if (setjmp(png_jmpbuf(this->png_ptr()))) {
624 SkCodecPrintf("setjmp long jump!\n");
625 return false;
626 }
627 //there is a potential tradeoff of memory vs speed created by putting th is in a loop.
628 //calling png_read_rows in a loop is insignificantly slower than calling it once with count
629 //as png_read_rows has it's own loop which calls png_read_row count time s.
630 for (int row = 0; row < count; row++) {
631 png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
632 }
633 return true;
634 }
635
636 private:
637 SkAutoTMalloc<uint8_t> fStorage;
638 uint8_t* fSrcRow;
639
640 typedef SkPngCodec INHERITED;
641 };
642
643
644 class SkPngInterlacedScanlineDecoder : public SkPngCodec {
645 public:
646 SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
647 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p tr,
648 int bitDepth, int numberPasses)
649 : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, n umberPasses)
650 , fHeight(-1)
651 , fCanSkipRewind(false)
652 {
653 SkASSERT(numberPasses != 1);
654 }
655
656 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti ons,
657 SkPMColor ctable[], int* ctableCount) override {
658 if (!conversion_possible(dstInfo, this->getInfo())) {
659 return kInvalidConversion;
660 }
661
662 const Result result = this->initializeSwizzler(dstInfo, options, ctable,
663 ctableCount);
664 if (result != kSuccess) {
665 return result;
666 }
667
668 fHeight = dstInfo.height();
669 // FIXME: This need not be called on a second call to onStartScanlineDec ode.
670 fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this- >srcConfig());
671 fGarbageRow.reset(fSrcRowBytes);
672 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
673 fCanSkipRewind = true;
674
675 return SkCodec::kSuccess;
676 }
677
678 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
679 // rewind stream if have previously called onGetScanlines,
680 // since we need entire progressive image to get scanlines
681 if (fCanSkipRewind) {
682 // We already rewound in onStartScanlineDecode, so there is no reaso n to rewind.
683 // Next time onGetScanlines is called, we will need to rewind.
684 fCanSkipRewind = false;
685 } else {
686 // rewindIfNeeded resets fCurrScanline, since it assumes that start
687 // needs to be called again before scanline decoding. PNG scanline
688 // decoding is the exception, since it needs to rewind between
689 // calls to getScanlines. Keep track of fCurrScanline, to undo the
690 // reset.
691 const int currScanline = this->nextScanline();
692 // This method would never be called if currScanline is -1
693 SkASSERT(currScanline != -1);
694
695 if (!this->rewindIfNeeded()) {
696 return kCouldNotRewind;
697 }
698 this->updateCurrScanline(currScanline);
699 }
700
701 if (setjmp(png_jmpbuf(this->png_ptr()))) {
702 SkCodecPrintf("setjmp long jump!\n");
703 // FIXME (msarett): Returning 0 is pessimistic. If we can complete a single pass,
704 // we may be able to report that all of the memory has been initiali zed. Even if we
705 // fail on the first pass, we can still report than some scanlines a re initialized.
706 return 0;
707 }
708 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes);
709 uint8_t* storagePtr = storage.get();
710 uint8_t* srcRow;
711 const int startRow = this->nextScanline();
712 for (int i = 0; i < this->numberPasses(); i++) {
713 // read rows we planned to skip into garbage row
714 for (int y = 0; y < startRow; y++){
715 png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1);
716 }
717 // read rows we care about into buffer
718 srcRow = storagePtr;
719 for (int y = 0; y < count; y++) {
720 png_read_rows(this->png_ptr(), &srcRow, png_bytepp_NULL, 1);
721 srcRow += fSrcRowBytes;
722 }
723 // read rows we don't want into garbage buffer
724 for (int y = 0; y < fHeight - startRow - count; y++) {
725 png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1);
726 }
727 }
728 //swizzle the rows we care about
729 srcRow = storagePtr;
730 void* dstRow = dst;
731 for (int y = 0; y < count; y++) {
732 this->swizzler()->swizzle(dstRow, srcRow);
733 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
734 srcRow += fSrcRowBytes;
735 }
736
737 return count;
738 }
739
740 bool onSkipScanlines(int count) override {
741 // The non-virtual version will update fCurrScanline.
742 return true;
743 }
744
745 SkScanlineOrder onGetScanlineOrder() const override {
746 return kNone_SkScanlineOrder;
747 }
748
749 private:
750 int fHeight;
751 size_t fSrcRowBytes;
752 SkAutoMalloc fGarbageRow;
753 uint8_t* fGarbageRowPtr;
754 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function
755 // is called whenever some action is taken that reads the stream and
756 // therefore the next call will require a rewind. So it modifies a boolean
757 // to note that the *next* time it is called a rewind is needed.
758 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling
759 // onStartScanlineDecode followed by onGetScanlines does *not* require a
760 // rewind. Since rewindIfNeeded does not have this flexibility, we need to
761 // add another layer.
762 bool fCanSkipRewind;
763
764 typedef SkPngCodec INHERITED;
765 };
766
767 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead er) {
768 SkAutoTDelete<SkStream> streamDeleter(stream);
769 png_structp png_ptr;
770 png_infop info_ptr;
771 SkImageInfo imageInfo;
772 int bitDepth;
773 int numberPasses;
774
775 if (!read_header(stream, chunkReader, &png_ptr, &info_ptr, &imageInfo, &bitD epth,
776 &numberPasses)) {
777 return nullptr;
778 }
779
780 if (1 == numberPasses) {
781 return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), chunk Reader,
782 png_ptr, info_ptr, bitDepth);
783 }
784
785 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), chunkReader,
786 png_ptr, info_ptr, bitDepth, numbe rPasses);
787 }
OLDNEW
« no previous file with comments | « src/codec/SkCodec_libpng.h ('k') | src/codec/SkIcoCodec.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698