blob: 60923356c18fb3f7f1bf9a6a754cbe2f2d67064a [file] [log] [blame]
scroggof24f2242015-03-03 08:59:20 -08001/*
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"
msarett74114382015-03-16 11:55:18 -07009#include "SkCodecPriv.h"
scroggof24f2242015-03-03 08:59:20 -080010#include "SkColorPriv.h"
11#include "SkColorTable.h"
12#include "SkBitmap.h"
13#include "SkMath.h"
scroggo05245902015-03-25 11:11:52 -070014#include "SkScanlineDecoder.h"
scroggof24f2242015-03-03 08:59:20 -080015#include "SkSize.h"
16#include "SkStream.h"
17#include "SkSwizzler.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 NULL
30#endif
31
32#ifndef png_bytepp_NULL
33 #define png_bytepp_NULL NULL
34#endif
35
36#ifndef int_p_NULL
37 #define int_p_NULL NULL
38#endif
39
40#ifndef png_flush_ptr_NULL
41 #define png_flush_ptr_NULL NULL
42#endif
43
44///////////////////////////////////////////////////////////////////////////////
45// Callback functions
46///////////////////////////////////////////////////////////////////////////////
47
48static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
49 SkDebugf("------ png error %s\n", msg);
50 longjmp(png_jmpbuf(png_ptr), 1);
51}
52
53static void sk_read_fn(png_structp png_ptr, png_bytep data,
54 png_size_t length) {
55 SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr));
56 const size_t bytes = stream->read(data, length);
57 if (bytes != length) {
58 // FIXME: We want to report the fact that the stream was truncated.
59 // One way to do that might be to pass a enum to longjmp so setjmp can
60 // specify the failure.
61 png_error(png_ptr, "Read Error!");
62 }
63}
64
65///////////////////////////////////////////////////////////////////////////////
66// Helpers
67///////////////////////////////////////////////////////////////////////////////
68
69class AutoCleanPng : public SkNoncopyable {
70public:
71 AutoCleanPng(png_structp png_ptr)
72 : fPng_ptr(png_ptr)
73 , fInfo_ptr(NULL) {}
74
75 ~AutoCleanPng() {
76 // fInfo_ptr will never be non-NULL unless fPng_ptr is.
77 if (fPng_ptr) {
78 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : NULL;
79 png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL);
80 }
81 }
82
83 void setInfoPtr(png_infop info_ptr) {
84 SkASSERT(NULL == fInfo_ptr);
85 fInfo_ptr = info_ptr;
86 }
87
88 void detach() {
89 fPng_ptr = NULL;
90 fInfo_ptr = NULL;
91 }
92
93private:
94 png_structp fPng_ptr;
95 png_infop fInfo_ptr;
96};
97#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
98
99// call only if color_type is PALETTE. Returns true if the ctable has alpha
100static bool has_transparency_in_palette(png_structp png_ptr,
101 png_infop info_ptr) {
102 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
103 return false;
104 }
105
106 png_bytep trans;
107 int num_trans;
108 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
109 return num_trans > 0;
110}
111
112// Method for coverting to either an SkPMColor or a similarly packed
113// unpremultiplied color.
114typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
115
116// Note: SkColorTable claims to store SkPMColors, which is not necessarily
117// the case here.
scroggo05245902015-03-25 11:11:52 -0700118bool SkPngCodec::decodePalette(bool premultiply) {
scroggof24f2242015-03-03 08:59:20 -0800119 int numPalette;
120 png_colorp palette;
121 png_bytep trans;
122
scroggo05245902015-03-25 11:11:52 -0700123 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) {
124 return false;
scroggof24f2242015-03-03 08:59:20 -0800125 }
126
127 /* BUGGY IMAGE WORKAROUND
128
129 We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
130 which is a problem since we use the byte as an index. To work around this we grow
131 the colortable by 1 (if its < 256) and duplicate the last color into that slot.
132 */
133 const int colorCount = numPalette + (numPalette < 256);
134 // Note: These are not necessarily SkPMColors.
135 SkPMColor colorStorage[256]; // worst-case storage
136 SkPMColor* colorPtr = colorStorage;
137
138 int numTrans;
scroggo05245902015-03-25 11:11:52 -0700139 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
140 png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, NULL);
scroggof24f2242015-03-03 08:59:20 -0800141 } else {
142 numTrans = 0;
143 }
144
145 // check for bad images that might make us crash
146 if (numTrans > numPalette) {
147 numTrans = numPalette;
148 }
149
150 int index = 0;
151 int transLessThanFF = 0;
152
153 // Choose which function to use to create the color table. If the final destination's
154 // colortype is unpremultiplied, the color table will store unpremultiplied colors.
155 PackColorProc proc;
156 if (premultiply) {
157 proc = &SkPreMultiplyARGB;
158 } else {
159 proc = &SkPackARGB32NoCheck;
160 }
161 for (; index < numTrans; index++) {
162 transLessThanFF |= (int)*trans - 0xFF;
163 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
164 palette++;
165 }
166
scroggo05245902015-03-25 11:11:52 -0700167 fReallyHasAlpha = transLessThanFF < 0;
scroggof24f2242015-03-03 08:59:20 -0800168
169 for (; index < numPalette; index++) {
170 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
171 palette++;
172 }
173
174 // see BUGGY IMAGE WORKAROUND comment above
175 if (numPalette < 256) {
176 *colorPtr = colorPtr[-1];
177 }
178
scroggo05245902015-03-25 11:11:52 -0700179 fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorStorage, colorCount)));
180 return true;
scroggof24f2242015-03-03 08:59:20 -0800181}
182
183///////////////////////////////////////////////////////////////////////////////
184// Creation
185///////////////////////////////////////////////////////////////////////////////
186
187#define PNG_BYTES_TO_CHECK 4
188
189bool SkPngCodec::IsPng(SkStream* stream) {
190 char buf[PNG_BYTES_TO_CHECK];
191 if (stream->read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) {
192 return false;
193 }
194 if (png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
195 return false;
196 }
197 return true;
198}
199
200SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
201 // The image is known to be a PNG. Decode enough to know the SkImageInfo.
202 // FIXME: Allow silencing warnings.
203 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
204 sk_error_fn, NULL);
205 if (!png_ptr) {
206 return NULL;
207 }
208
209 AutoCleanPng autoClean(png_ptr);
210
211 png_infop info_ptr = png_create_info_struct(png_ptr);
212 if (info_ptr == NULL) {
213 return NULL;
214 }
215
216 autoClean.setInfoPtr(info_ptr);
217
218 // FIXME: Could we use the return value of setjmp to specify the type of
219 // error?
220 if (setjmp(png_jmpbuf(png_ptr))) {
221 return NULL;
222 }
223
224 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn);
225
226 // FIXME: This is where the old code hooks up the Peeker. Does it need to
227 // be set this early? (i.e. where are the user chunks? early in the stream,
228 // potentially?)
229 // If it does, we need to figure out a way to set it here.
230
231 // The call to png_read_info() gives us all of the information from the
232 // PNG file before the first IDAT (image data chunk).
233 png_read_info(png_ptr, info_ptr);
234 png_uint_32 origWidth, origHeight;
235 int bitDepth, colorType;
236 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
237 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
238
239 // sanity check for size
240 {
241 int64_t size = sk_64_mul(origWidth, origHeight);
242 // now check that if we are 4-bytes per pixel, we also don't overflow
243 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
244 return NULL;
245 }
246 }
247
248 // Tell libpng to strip 16 bit/color files down to 8 bits/color
249 if (bitDepth == 16) {
250 png_set_strip_16(png_ptr);
251 }
252#ifdef PNG_READ_PACK_SUPPORTED
253 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
254 // byte into separate bytes (useful for paletted and grayscale images).
255 if (bitDepth < 8) {
256 png_set_packing(png_ptr);
257 }
258#endif
259 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
260 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
261 png_set_expand_gray_1_2_4_to_8(png_ptr);
262 }
263
264
265 // Now determine the default SkColorType and SkAlphaType.
266 SkColorType skColorType;
267 SkAlphaType skAlphaType;
268 switch (colorType) {
269 case PNG_COLOR_TYPE_PALETTE:
270 // Technically, this is true of the data, but I don't think we want
271 // to support it.
272 // skColorType = kIndex8_SkColorType;
273 skColorType = kN32_SkColorType;
274 skAlphaType = has_transparency_in_palette(png_ptr, info_ptr) ?
275 kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
276 break;
277 case PNG_COLOR_TYPE_GRAY:
278 if (false) {
279 // FIXME: Is this the wrong default behavior? This means if the
280 // caller supplies the info we gave them, they'll get Alpha 8.
281 skColorType = kAlpha_8_SkColorType;
282 // FIXME: Strangely, the canonical type for Alpha 8 is Premul.
283 skAlphaType = kPremul_SkAlphaType;
284 } else {
285 skColorType = kN32_SkColorType;
286 skAlphaType = kOpaque_SkAlphaType;
287 }
288 break;
289 default:
290 // Note: This *almost* mimics the code in SkImageDecoder_libpng.
291 // has_transparency_in_palette makes an additional check - whether
292 // numTrans is greater than 0. Why does the other code not make that
293 // check?
294 if (has_transparency_in_palette(png_ptr, info_ptr)
295 || PNG_COLOR_TYPE_RGB_ALPHA == colorType
296 || PNG_COLOR_TYPE_GRAY_ALPHA == colorType)
297 {
298 skAlphaType = kUnpremul_SkAlphaType;
299 } else {
300 skAlphaType = kOpaque_SkAlphaType;
301 }
302 skColorType = kN32_SkColorType;
303 break;
304 }
305
306 {
307 // FIXME: Again, this block needs to go into onGetPixels.
308 bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType && skColorType != kAlpha_8_SkColorType;
309
310 // Unless the user is requesting A8, convert a grayscale image into RGB.
311 // GRAY_ALPHA will always be converted to RGB
312 if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
313 png_set_gray_to_rgb(png_ptr);
314 }
315
316 // Add filler (or alpha) byte (after each RGB triplet) if necessary.
317 // FIXME: It seems like we could just use RGB as the SrcConfig here.
318 if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) {
319 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
320 }
321 }
322
323 // FIXME: Also need to check for sRGB (skbug.com/3471).
324
325 SkImageInfo info = SkImageInfo::Make(origWidth, origHeight, skColorType,
326 skAlphaType);
327 SkCodec* codec = SkNEW_ARGS(SkPngCodec, (info, stream, png_ptr, info_ptr));
328 autoClean.detach();
329 return codec;
330}
331
scroggo05245902015-03-25 11:11:52 -0700332#define INVALID_NUMBER_PASSES -1
scroggof24f2242015-03-03 08:59:20 -0800333SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
334 png_structp png_ptr, png_infop info_ptr)
335 : INHERITED(info, stream)
336 , fPng_ptr(png_ptr)
scroggo05245902015-03-25 11:11:52 -0700337 , fInfo_ptr(info_ptr)
338 , fSrcConfig(SkSwizzler::kUnknown)
339 , fNumberPasses(INVALID_NUMBER_PASSES)
340 , fReallyHasAlpha(false)
341{}
scroggof24f2242015-03-03 08:59:20 -0800342
343SkPngCodec::~SkPngCodec() {
344 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
345}
346
347///////////////////////////////////////////////////////////////////////////////
348// Getting the pixels
349///////////////////////////////////////////////////////////////////////////////
350
msaretteed039b2015-03-18 11:11:19 -0700351static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
scroggof24f2242015-03-03 08:59:20 -0800352 // TODO: Support other conversions
msaretteed039b2015-03-18 11:11:19 -0700353 if (dst.colorType() != src.colorType()) {
scroggof24f2242015-03-03 08:59:20 -0800354 return false;
355 }
msaretteed039b2015-03-18 11:11:19 -0700356 if (dst.profileType() != src.profileType()) {
scroggof24f2242015-03-03 08:59:20 -0800357 return false;
358 }
msaretteed039b2015-03-18 11:11:19 -0700359 if (dst.alphaType() == src.alphaType()) {
scroggof24f2242015-03-03 08:59:20 -0800360 return true;
361 }
msaretteed039b2015-03-18 11:11:19 -0700362 return kPremul_SkAlphaType == dst.alphaType() &&
363 kUnpremul_SkAlphaType == src.alphaType();
scroggof24f2242015-03-03 08:59:20 -0800364}
365
scroggo05245902015-03-25 11:11:52 -0700366SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
367 void* dst, size_t rowBytes,
368 const Options& options) {
scroggof24f2242015-03-03 08:59:20 -0800369 // FIXME: Could we use the return value of setjmp to specify the type of
370 // error?
371 if (setjmp(png_jmpbuf(fPng_ptr))) {
372 SkDebugf("setjmp long jump!\n");
373 return kInvalidInput;
374 }
375
376 // FIXME: We already retrieved this information. Store it in SkPngCodec?
377 png_uint_32 origWidth, origHeight;
378 int bitDepth, pngColorType, interlaceType;
379 png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth,
380 &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);
381
scroggo05245902015-03-25 11:11:52 -0700382 fNumberPasses = (interlaceType != PNG_INTERLACE_NONE) ?
scroggof24f2242015-03-03 08:59:20 -0800383 png_set_interlace_handling(fPng_ptr) : 1;
384
scroggo05245902015-03-25 11:11:52 -0700385 // Set to the default before calling decodePalette, which may change it.
386 fReallyHasAlpha = false;
scroggof24f2242015-03-03 08:59:20 -0800387 if (PNG_COLOR_TYPE_PALETTE == pngColorType) {
scroggo05245902015-03-25 11:11:52 -0700388 fSrcConfig = SkSwizzler::kIndex;
389 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType())) {
scroggof24f2242015-03-03 08:59:20 -0800390 return kInvalidInput;
391 }
scroggof24f2242015-03-03 08:59:20 -0800392 } else if (kAlpha_8_SkColorType == requestedInfo.colorType()) {
393 // Note: we check the destination, since otherwise we would have
394 // told png to upscale.
395 SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
scroggo05245902015-03-25 11:11:52 -0700396 fSrcConfig = SkSwizzler::kGray;
msarett9bde9182015-03-25 05:27:48 -0700397 } else if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
scroggo05245902015-03-25 11:11:52 -0700398 fSrcConfig = SkSwizzler::kRGBX;
scroggof24f2242015-03-03 08:59:20 -0800399 } else {
scroggo05245902015-03-25 11:11:52 -0700400 fSrcConfig = SkSwizzler::kRGBA;
scroggof24f2242015-03-03 08:59:20 -0800401 }
scroggo05245902015-03-25 11:11:52 -0700402 const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL;
403 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
404 dst, rowBytes, options.fZeroInitialized));
405 if (!fSwizzler) {
scroggof24f2242015-03-03 08:59:20 -0800406 // FIXME: CreateSwizzler could fail for another reason.
407 return kUnimplemented;
408 }
409
410 // FIXME: Here is where we should likely insert some of the modifications
411 // made in the factory.
412 png_read_update_info(fPng_ptr, fInfo_ptr);
413
scroggo05245902015-03-25 11:11:52 -0700414 return kSuccess;
415}
416
417SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
418 size_t rowBytes, const Options& options,
419 SkPMColor ctable[], int* ctableCount) {
420 if (!this->rewindIfNeeded()) {
421 return kCouldNotRewind;
422 }
423 if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
424 return kInvalidScale;
425 }
426 if (!conversion_possible(requestedInfo, this->getInfo())) {
427 return kInvalidConversion;
428 }
429
430 const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes,
431 options);
432 if (result != kSuccess) {
433 return result;
434 }
435
436 // FIXME: Could we use the return value of setjmp to specify the type of
437 // error?
438 if (setjmp(png_jmpbuf(fPng_ptr))) {
439 SkDebugf("setjmp long jump!\n");
440 return kInvalidInput;
441 }
442
443 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
444 SkAutoMalloc storage;
445 if (fNumberPasses > 1) {
scroggof24f2242015-03-03 08:59:20 -0800446 const int width = requestedInfo.width();
447 const int height = requestedInfo.height();
scroggo05245902015-03-25 11:11:52 -0700448 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
scroggof24f2242015-03-03 08:59:20 -0800449 const size_t rowBytes = width * bpp;
450
451 storage.reset(width * height * bpp);
452 uint8_t* const base = static_cast<uint8_t*>(storage.get());
453
scroggo05245902015-03-25 11:11:52 -0700454 for (int i = 0; i < fNumberPasses; i++) {
scroggof24f2242015-03-03 08:59:20 -0800455 uint8_t* row = base;
456 for (int y = 0; y < height; y++) {
457 uint8_t* bmRow = row;
458 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
459 row += rowBytes;
460 }
461 }
462
463 // Now swizzle it.
464 uint8_t* row = base;
465 for (int y = 0; y < height; y++) {
scroggo05245902015-03-25 11:11:52 -0700466 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(row));
scroggof24f2242015-03-03 08:59:20 -0800467 row += rowBytes;
468 }
469 } else {
scroggo05245902015-03-25 11:11:52 -0700470 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
scroggof24f2242015-03-03 08:59:20 -0800471 uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
472 for (int y = 0; y < requestedInfo.height(); y++) {
473 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
scroggo05245902015-03-25 11:11:52 -0700474 fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(srcRow));
scroggof24f2242015-03-03 08:59:20 -0800475 }
476 }
477
scroggo05245902015-03-25 11:11:52 -0700478 // FIXME: do we need substituteTranspColor? Note that we cannot do it for
479 // scanline decoding, but we could do it here. Alternatively, we could do
480 // it as we go, instead of in post-processing like SkPNGImageDecoder.
scroggof24f2242015-03-03 08:59:20 -0800481
scroggo05245902015-03-25 11:11:52 -0700482 this->finish();
scroggof24f2242015-03-03 08:59:20 -0800483 return kSuccess;
484}
scroggo05245902015-03-25 11:11:52 -0700485
486void SkPngCodec::finish() {
487 if (setjmp(png_jmpbuf(fPng_ptr))) {
488 // We've already read all the scanlines. This is a success.
489 return;
490 }
491 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
492 png_read_end(fPng_ptr, fInfo_ptr);
493}
494
495class SkPngScanlineDecoder : public SkScanlineDecoder {
496public:
497 SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
498 : INHERITED(dstInfo)
499 , fCodec(codec)
500 , fHasAlpha(false)
501 {
502 fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
503 fSrcRow = static_cast<uint8_t*>(fStorage.get());
504 }
505
506 SkImageGenerator::Result onGetScanlines(void* dst, int count, size_t rowBytes) SK_OVERRIDE {
507 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
508 SkDebugf("setjmp long jump!\n");
509 return SkImageGenerator::kInvalidInput;
510 }
511
512 for (int i = 0; i < count; i++) {
513 png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
514 fCodec->fSwizzler->setDstRow(dst);
515 fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(fSrcRow));
516 dst = SkTAddOffset<void>(dst, rowBytes);
517 }
518 return SkImageGenerator::kSuccess;
519 }
520
521 SkImageGenerator::Result onSkipScanlines(int count) SK_OVERRIDE {
522 // FIXME: Could we use the return value of setjmp to specify the type of
523 // error?
524 if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
525 SkDebugf("setjmp long jump!\n");
526 return SkImageGenerator::kInvalidInput;
527 }
528
529 png_read_rows(fCodec->fPng_ptr, png_bytepp_NULL, png_bytepp_NULL, count);
530 return SkImageGenerator::kSuccess;
531 }
532
533 void onFinish() SK_OVERRIDE {
534 fCodec->finish();
535 }
536
537 bool onReallyHasAlpha() const SK_OVERRIDE { return fHasAlpha; }
538
539private:
540 SkPngCodec* fCodec; // Unowned.
541 bool fHasAlpha;
542 SkAutoMalloc fStorage;
543 uint8_t* fSrcRow;
544
545 typedef SkScanlineDecoder INHERITED;
546};
547
548SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo) {
549 // Check to see if scaling was requested.
550 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
551 return NULL;
552 }
553
554 if (!conversion_possible(dstInfo, this->getInfo())) {
555 SkDebugf("no conversion possible\n");
556 return NULL;
557 }
558
559 // Note: We set dst to NULL since we do not know it yet. rowBytes is not needed,
560 // since we'll be manually updating the dstRow, but the SkSwizzler requires it to
561 // be at least dstInfo.minRowBytes.
562 Options opts;
563 // FIXME: Pass this in to getScanlineDecoder?
564 opts.fZeroInitialized = kNo_ZeroInitialized;
565 if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), opts) != kSuccess) {
566 SkDebugf("failed to initialize the swizzler.\n");
567 return NULL;
568 }
569
570 SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
571 if (fNumberPasses > 1) {
572 // We cannot efficiently do scanline decoding.
573 return NULL;
574 }
575
576 return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, this));
577}
578