gfx/skia/trunk/src/images/SkImageDecoder_libpng.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1
michael@0 2 /*
michael@0 3 * Copyright 2006 The Android Open Source Project
michael@0 4 *
michael@0 5 * Use of this source code is governed by a BSD-style license that can be
michael@0 6 * found in the LICENSE file.
michael@0 7 */
michael@0 8
michael@0 9
michael@0 10 #include "SkImageDecoder.h"
michael@0 11 #include "SkImageEncoder.h"
michael@0 12 #include "SkColor.h"
michael@0 13 #include "SkColorPriv.h"
michael@0 14 #include "SkDither.h"
michael@0 15 #include "SkMath.h"
michael@0 16 #include "SkRTConf.h"
michael@0 17 #include "SkScaledBitmapSampler.h"
michael@0 18 #include "SkStream.h"
michael@0 19 #include "SkTemplates.h"
michael@0 20 #include "SkUtils.h"
michael@0 21 #include "transform_scanline.h"
michael@0 22 extern "C" {
michael@0 23 #include "png.h"
michael@0 24 }
michael@0 25
michael@0 26 /* These were dropped in libpng >= 1.4 */
michael@0 27 #ifndef png_infopp_NULL
michael@0 28 #define png_infopp_NULL NULL
michael@0 29 #endif
michael@0 30
michael@0 31 #ifndef png_bytepp_NULL
michael@0 32 #define png_bytepp_NULL NULL
michael@0 33 #endif
michael@0 34
michael@0 35 #ifndef int_p_NULL
michael@0 36 #define int_p_NULL NULL
michael@0 37 #endif
michael@0 38
michael@0 39 #ifndef png_flush_ptr_NULL
michael@0 40 #define png_flush_ptr_NULL NULL
michael@0 41 #endif
michael@0 42
michael@0 43 #if defined(SK_DEBUG)
michael@0 44 #define DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS false
michael@0 45 #else // !defined(SK_DEBUG)
michael@0 46 #define DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS true
michael@0 47 #endif // defined(SK_DEBUG)
michael@0 48 SK_CONF_DECLARE(bool, c_suppressPNGImageDecoderWarnings,
michael@0 49 "images.png.suppressDecoderWarnings",
michael@0 50 DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS,
michael@0 51 "Suppress most PNG warnings when calling image decode "
michael@0 52 "functions.");
michael@0 53
michael@0 54
michael@0 55
michael@0 56 class SkPNGImageIndex {
michael@0 57 public:
michael@0 58 SkPNGImageIndex(SkStreamRewindable* stream, png_structp png_ptr, png_infop info_ptr)
michael@0 59 : fStream(stream)
michael@0 60 , fPng_ptr(png_ptr)
michael@0 61 , fInfo_ptr(info_ptr)
michael@0 62 , fConfig(SkBitmap::kNo_Config) {
michael@0 63 SkASSERT(stream != NULL);
michael@0 64 stream->ref();
michael@0 65 }
michael@0 66 ~SkPNGImageIndex() {
michael@0 67 if (NULL != fPng_ptr) {
michael@0 68 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
michael@0 69 }
michael@0 70 }
michael@0 71
michael@0 72 SkAutoTUnref<SkStreamRewindable> fStream;
michael@0 73 png_structp fPng_ptr;
michael@0 74 png_infop fInfo_ptr;
michael@0 75 SkBitmap::Config fConfig;
michael@0 76 };
michael@0 77
michael@0 78 class SkPNGImageDecoder : public SkImageDecoder {
michael@0 79 public:
michael@0 80 SkPNGImageDecoder() {
michael@0 81 fImageIndex = NULL;
michael@0 82 }
michael@0 83 virtual Format getFormat() const SK_OVERRIDE {
michael@0 84 return kPNG_Format;
michael@0 85 }
michael@0 86
michael@0 87 virtual ~SkPNGImageDecoder() {
michael@0 88 SkDELETE(fImageIndex);
michael@0 89 }
michael@0 90
michael@0 91 protected:
michael@0 92 #ifdef SK_BUILD_FOR_ANDROID
michael@0 93 virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
michael@0 94 virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& region) SK_OVERRIDE;
michael@0 95 #endif
michael@0 96 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
michael@0 97
michael@0 98 private:
michael@0 99 SkPNGImageIndex* fImageIndex;
michael@0 100
michael@0 101 bool onDecodeInit(SkStream* stream, png_structp *png_ptrp, png_infop *info_ptrp);
michael@0 102 bool decodePalette(png_structp png_ptr, png_infop info_ptr,
michael@0 103 bool * SK_RESTRICT hasAlphap, bool *reallyHasAlphap,
michael@0 104 SkColorTable **colorTablep);
michael@0 105 bool getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
michael@0 106 SkBitmap::Config *config, bool *hasAlpha,
michael@0 107 SkPMColor *theTranspColor);
michael@0 108
michael@0 109 typedef SkImageDecoder INHERITED;
michael@0 110 };
michael@0 111
michael@0 112 #ifndef png_jmpbuf
michael@0 113 # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
michael@0 114 #endif
michael@0 115
michael@0 116 #define PNG_BYTES_TO_CHECK 4
michael@0 117
michael@0 118 /* Automatically clean up after throwing an exception */
michael@0 119 struct PNGAutoClean {
michael@0 120 PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {}
michael@0 121 ~PNGAutoClean() {
michael@0 122 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
michael@0 123 }
michael@0 124 private:
michael@0 125 png_structp png_ptr;
michael@0 126 png_infop info_ptr;
michael@0 127 };
michael@0 128
michael@0 129 static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
michael@0 130 SkStream* sk_stream = (SkStream*) png_get_io_ptr(png_ptr);
michael@0 131 size_t bytes = sk_stream->read(data, length);
michael@0 132 if (bytes != length) {
michael@0 133 png_error(png_ptr, "Read Error!");
michael@0 134 }
michael@0 135 }
michael@0 136
michael@0 137 #ifdef SK_BUILD_FOR_ANDROID
michael@0 138 static void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) {
michael@0 139 SkStreamRewindable* sk_stream = (SkStreamRewindable*) png_get_io_ptr(png_ptr);
michael@0 140 if (!sk_stream->rewind()) {
michael@0 141 png_error(png_ptr, "Failed to rewind stream!");
michael@0 142 }
michael@0 143 (void)sk_stream->skip(offset);
michael@0 144 }
michael@0 145 #endif
michael@0 146
michael@0 147 static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
michael@0 148 SkImageDecoder::Peeker* peeker =
michael@0 149 (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr);
michael@0 150 // peek() returning true means continue decoding
michael@0 151 return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ?
michael@0 152 1 : -1;
michael@0 153 }
michael@0 154
michael@0 155 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
michael@0 156 SkDEBUGF(("------ png error %s\n", msg));
michael@0 157 longjmp(png_jmpbuf(png_ptr), 1);
michael@0 158 }
michael@0 159
michael@0 160 static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) {
michael@0 161 for (int i = 0; i < count; i++) {
michael@0 162 uint8_t* tmp = storage;
michael@0 163 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
michael@0 164 }
michael@0 165 }
michael@0 166
michael@0 167 static bool pos_le(int value, int max) {
michael@0 168 return value > 0 && value <= max;
michael@0 169 }
michael@0 170
michael@0 171 static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) {
michael@0 172 SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config);
michael@0 173
michael@0 174 bool reallyHasAlpha = false;
michael@0 175
michael@0 176 for (int y = bm->height() - 1; y >= 0; --y) {
michael@0 177 SkPMColor* p = bm->getAddr32(0, y);
michael@0 178 for (int x = bm->width() - 1; x >= 0; --x) {
michael@0 179 if (match == *p) {
michael@0 180 *p = 0;
michael@0 181 reallyHasAlpha = true;
michael@0 182 }
michael@0 183 p += 1;
michael@0 184 }
michael@0 185 }
michael@0 186 return reallyHasAlpha;
michael@0 187 }
michael@0 188
michael@0 189 static bool canUpscalePaletteToConfig(SkBitmap::Config dstConfig,
michael@0 190 bool srcHasAlpha) {
michael@0 191 switch (dstConfig) {
michael@0 192 case SkBitmap::kARGB_8888_Config:
michael@0 193 case SkBitmap::kARGB_4444_Config:
michael@0 194 return true;
michael@0 195 case SkBitmap::kRGB_565_Config:
michael@0 196 // only return true if the src is opaque (since 565 is opaque)
michael@0 197 return !srcHasAlpha;
michael@0 198 default:
michael@0 199 return false;
michael@0 200 }
michael@0 201 }
michael@0 202
michael@0 203 // call only if color_type is PALETTE. Returns true if the ctable has alpha
michael@0 204 static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
michael@0 205 png_bytep trans;
michael@0 206 int num_trans;
michael@0 207
michael@0 208 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
michael@0 209 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
michael@0 210 return num_trans > 0;
michael@0 211 }
michael@0 212 return false;
michael@0 213 }
michael@0 214
michael@0 215 void do_nothing_warning_fn(png_structp, png_const_charp) {
michael@0 216 /* do nothing */
michael@0 217 }
michael@0 218
michael@0 219 bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp,
michael@0 220 png_infop *info_ptrp) {
michael@0 221 /* Create and initialize the png_struct with the desired error handler
michael@0 222 * functions. If you want to use the default stderr and longjump method,
michael@0 223 * you can supply NULL for the last three parameters. We also supply the
michael@0 224 * the compiler header file version, so that we know if the application
michael@0 225 * was compiled with a compatible version of the library. */
michael@0 226
michael@0 227 png_error_ptr user_warning_fn =
michael@0 228 (c_suppressPNGImageDecoderWarnings) ? (&do_nothing_warning_fn) : NULL;
michael@0 229 /* NULL means to leave as default library behavior. */
michael@0 230 /* c_suppressPNGImageDecoderWarnings default depends on SK_DEBUG. */
michael@0 231 /* To suppress warnings with a SK_DEBUG binary, set the
michael@0 232 * environment variable "skia_images_png_suppressDecoderWarnings"
michael@0 233 * to "true". Inside a program that links to skia:
michael@0 234 * SK_CONF_SET("images.png.suppressDecoderWarnings", true); */
michael@0 235
michael@0 236 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
michael@0 237 NULL, sk_error_fn, user_warning_fn);
michael@0 238 // png_voidp user_error_ptr, user_error_fn, user_warning_fn);
michael@0 239 if (png_ptr == NULL) {
michael@0 240 return false;
michael@0 241 }
michael@0 242
michael@0 243 *png_ptrp = png_ptr;
michael@0 244
michael@0 245 /* Allocate/initialize the memory for image information. */
michael@0 246 png_infop info_ptr = png_create_info_struct(png_ptr);
michael@0 247 if (info_ptr == NULL) {
michael@0 248 png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
michael@0 249 return false;
michael@0 250 }
michael@0 251 *info_ptrp = info_ptr;
michael@0 252
michael@0 253 /* Set error handling if you are using the setjmp/longjmp method (this is
michael@0 254 * the normal method of doing things with libpng). REQUIRED unless you
michael@0 255 * set up your own error handlers in the png_create_read_struct() earlier.
michael@0 256 */
michael@0 257 if (setjmp(png_jmpbuf(png_ptr))) {
michael@0 258 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
michael@0 259 return false;
michael@0 260 }
michael@0 261
michael@0 262 /* If you are using replacement read functions, instead of calling
michael@0 263 * png_init_io() here you would call:
michael@0 264 */
michael@0 265 png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
michael@0 266 #ifdef SK_BUILD_FOR_ANDROID
michael@0 267 png_set_seek_fn(png_ptr, sk_seek_fn);
michael@0 268 #endif
michael@0 269 /* where user_io_ptr is a structure you want available to the callbacks */
michael@0 270 /* If we have already read some of the signature */
michael@0 271 // png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
michael@0 272
michael@0 273 // hookup our peeker so we can see any user-chunks the caller may be interested in
michael@0 274 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
michael@0 275 if (this->getPeeker()) {
michael@0 276 png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);
michael@0 277 }
michael@0 278
michael@0 279 /* The call to png_read_info() gives us all of the information from the
michael@0 280 * PNG file before the first IDAT (image data chunk). */
michael@0 281 png_read_info(png_ptr, info_ptr);
michael@0 282 png_uint_32 origWidth, origHeight;
michael@0 283 int bitDepth, colorType;
michael@0 284 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
michael@0 285 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
michael@0 286
michael@0 287 /* tell libpng to strip 16 bit/color files down to 8 bits/color */
michael@0 288 if (bitDepth == 16) {
michael@0 289 png_set_strip_16(png_ptr);
michael@0 290 }
michael@0 291 /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
michael@0 292 * byte into separate bytes (useful for paletted and grayscale images). */
michael@0 293 if (bitDepth < 8) {
michael@0 294 png_set_packing(png_ptr);
michael@0 295 }
michael@0 296 /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
michael@0 297 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
michael@0 298 png_set_expand_gray_1_2_4_to_8(png_ptr);
michael@0 299 }
michael@0 300
michael@0 301 return true;
michael@0 302 }
michael@0 303
michael@0 304 bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
michael@0 305 Mode mode) {
michael@0 306 png_structp png_ptr;
michael@0 307 png_infop info_ptr;
michael@0 308
michael@0 309 if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
michael@0 310 return false;
michael@0 311 }
michael@0 312
michael@0 313 PNGAutoClean autoClean(png_ptr, info_ptr);
michael@0 314
michael@0 315 if (setjmp(png_jmpbuf(png_ptr))) {
michael@0 316 return false;
michael@0 317 }
michael@0 318
michael@0 319 png_uint_32 origWidth, origHeight;
michael@0 320 int bitDepth, colorType, interlaceType;
michael@0 321 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
michael@0 322 &colorType, &interlaceType, int_p_NULL, int_p_NULL);
michael@0 323
michael@0 324 SkBitmap::Config config;
michael@0 325 bool hasAlpha = false;
michael@0 326 SkPMColor theTranspColor = 0; // 0 tells us not to try to match
michael@0 327
michael@0 328 if (!this->getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &theTranspColor)) {
michael@0 329 return false;
michael@0 330 }
michael@0 331
michael@0 332 const int sampleSize = this->getSampleSize();
michael@0 333 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
michael@0 334 decodedBitmap->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
michael@0 335
michael@0 336 // we should communicate alphaType, even if we early-return in bounds-only-mode.
michael@0 337 if (this->getRequireUnpremultipliedColors()) {
michael@0 338 decodedBitmap->setAlphaType(kUnpremul_SkAlphaType);
michael@0 339 }
michael@0 340
michael@0 341 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
michael@0 342 return true;
michael@0 343 }
michael@0 344
michael@0 345 // from here down we are concerned with colortables and pixels
michael@0 346
michael@0 347 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
michael@0 348 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
michael@0 349 // draw lots faster if we can flag the bitmap has being opaque
michael@0 350 bool reallyHasAlpha = false;
michael@0 351 SkColorTable* colorTable = NULL;
michael@0 352
michael@0 353 if (colorType == PNG_COLOR_TYPE_PALETTE) {
michael@0 354 decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
michael@0 355 }
michael@0 356
michael@0 357 SkAutoUnref aur(colorTable);
michael@0 358
michael@0 359 if (!this->allocPixelRef(decodedBitmap,
michael@0 360 SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
michael@0 361 return false;
michael@0 362 }
michael@0 363
michael@0 364 SkAutoLockPixels alp(*decodedBitmap);
michael@0 365
michael@0 366 /* Turn on interlace handling. REQUIRED if you are not using
michael@0 367 * png_read_image(). To see how to handle interlacing passes,
michael@0 368 * see the png_read_row() method below:
michael@0 369 */
michael@0 370 const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ?
michael@0 371 png_set_interlace_handling(png_ptr) : 1;
michael@0 372
michael@0 373 /* Optional call to gamma correct and add the background to the palette
michael@0 374 * and update info structure. REQUIRED if you are expecting libpng to
michael@0 375 * update the palette for you (ie you selected such a transform above).
michael@0 376 */
michael@0 377 png_read_update_info(png_ptr, info_ptr);
michael@0 378
michael@0 379 if ((SkBitmap::kA8_Config == config || SkBitmap::kIndex8_Config == config)
michael@0 380 && 1 == sampleSize) {
michael@0 381 if (SkBitmap::kA8_Config == config) {
michael@0 382 // For an A8 bitmap, we assume there is an alpha for speed. It is
michael@0 383 // possible the bitmap is opaque, but that is an unlikely use case
michael@0 384 // since it would not be very interesting.
michael@0 385 reallyHasAlpha = true;
michael@0 386 // A8 is only allowed if the original was GRAY.
michael@0 387 SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
michael@0 388 }
michael@0 389 for (int i = 0; i < number_passes; i++) {
michael@0 390 for (png_uint_32 y = 0; y < origHeight; y++) {
michael@0 391 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);
michael@0 392 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
michael@0 393 }
michael@0 394 }
michael@0 395 } else {
michael@0 396 SkScaledBitmapSampler::SrcConfig sc;
michael@0 397 int srcBytesPerPixel = 4;
michael@0 398
michael@0 399 if (colorTable != NULL) {
michael@0 400 sc = SkScaledBitmapSampler::kIndex;
michael@0 401 srcBytesPerPixel = 1;
michael@0 402 } else if (SkBitmap::kA8_Config == config) {
michael@0 403 // A8 is only allowed if the original was GRAY.
michael@0 404 SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
michael@0 405 sc = SkScaledBitmapSampler::kGray;
michael@0 406 srcBytesPerPixel = 1;
michael@0 407 } else if (hasAlpha) {
michael@0 408 sc = SkScaledBitmapSampler::kRGBA;
michael@0 409 } else {
michael@0 410 sc = SkScaledBitmapSampler::kRGBX;
michael@0 411 }
michael@0 412
michael@0 413 /* We have to pass the colortable explicitly, since we may have one
michael@0 414 even if our decodedBitmap doesn't, due to the request that we
michael@0 415 upscale png's palette to a direct model
michael@0 416 */
michael@0 417 SkAutoLockColors ctLock(colorTable);
michael@0 418 if (!sampler.begin(decodedBitmap, sc, *this, ctLock.colors())) {
michael@0 419 return false;
michael@0 420 }
michael@0 421 const int height = decodedBitmap->height();
michael@0 422
michael@0 423 if (number_passes > 1) {
michael@0 424 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
michael@0 425 uint8_t* base = (uint8_t*)storage.get();
michael@0 426 size_t rowBytes = origWidth * srcBytesPerPixel;
michael@0 427
michael@0 428 for (int i = 0; i < number_passes; i++) {
michael@0 429 uint8_t* row = base;
michael@0 430 for (png_uint_32 y = 0; y < origHeight; y++) {
michael@0 431 uint8_t* bmRow = row;
michael@0 432 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
michael@0 433 row += rowBytes;
michael@0 434 }
michael@0 435 }
michael@0 436 // now sample it
michael@0 437 base += sampler.srcY0() * rowBytes;
michael@0 438 for (int y = 0; y < height; y++) {
michael@0 439 reallyHasAlpha |= sampler.next(base);
michael@0 440 base += sampler.srcDY() * rowBytes;
michael@0 441 }
michael@0 442 } else {
michael@0 443 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
michael@0 444 uint8_t* srcRow = (uint8_t*)storage.get();
michael@0 445 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
michael@0 446
michael@0 447 for (int y = 0; y < height; y++) {
michael@0 448 uint8_t* tmp = srcRow;
michael@0 449 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
michael@0 450 reallyHasAlpha |= sampler.next(srcRow);
michael@0 451 if (y < height - 1) {
michael@0 452 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
michael@0 453 }
michael@0 454 }
michael@0 455
michael@0 456 // skip the rest of the rows (if any)
michael@0 457 png_uint_32 read = (height - 1) * sampler.srcDY() +
michael@0 458 sampler.srcY0() + 1;
michael@0 459 SkASSERT(read <= origHeight);
michael@0 460 skip_src_rows(png_ptr, srcRow, origHeight - read);
michael@0 461 }
michael@0 462 }
michael@0 463
michael@0 464 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
michael@0 465 png_read_end(png_ptr, info_ptr);
michael@0 466
michael@0 467 if (0 != theTranspColor) {
michael@0 468 reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
michael@0 469 }
michael@0 470 if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {
michael@0 471 switch (decodedBitmap->config()) {
michael@0 472 case SkBitmap::kIndex8_Config:
michael@0 473 // Fall through.
michael@0 474 case SkBitmap::kARGB_4444_Config:
michael@0 475 // We have chosen not to support unpremul for these configs.
michael@0 476 return false;
michael@0 477 default: {
michael@0 478 // Fall through to finish the decode. This config either
michael@0 479 // supports unpremul or it is irrelevant because it has no
michael@0 480 // alpha (or only alpha).
michael@0 481 // These brackets prevent a warning.
michael@0 482 }
michael@0 483 }
michael@0 484 }
michael@0 485
michael@0 486 if (!reallyHasAlpha) {
michael@0 487 decodedBitmap->setAlphaType(kOpaque_SkAlphaType);
michael@0 488 }
michael@0 489 return true;
michael@0 490 }
michael@0 491
michael@0 492
michael@0 493
michael@0 494 bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
michael@0 495 SkBitmap::Config* SK_RESTRICT configp,
michael@0 496 bool* SK_RESTRICT hasAlphap,
michael@0 497 SkPMColor* SK_RESTRICT theTranspColorp) {
michael@0 498 png_uint_32 origWidth, origHeight;
michael@0 499 int bitDepth, colorType;
michael@0 500 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
michael@0 501 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
michael@0 502
michael@0 503 // check for sBIT chunk data, in case we should disable dithering because
michael@0 504 // our data is not truely 8bits per component
michael@0 505 png_color_8p sig_bit;
michael@0 506 if (this->getDitherImage() && png_get_sBIT(png_ptr, info_ptr, &sig_bit)) {
michael@0 507 #if 0
michael@0 508 SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green,
michael@0 509 sig_bit->blue, sig_bit->alpha);
michael@0 510 #endif
michael@0 511 // 0 seems to indicate no information available
michael@0 512 if (pos_le(sig_bit->red, SK_R16_BITS) &&
michael@0 513 pos_le(sig_bit->green, SK_G16_BITS) &&
michael@0 514 pos_le(sig_bit->blue, SK_B16_BITS)) {
michael@0 515 this->setDitherImage(false);
michael@0 516 }
michael@0 517 }
michael@0 518
michael@0 519 if (colorType == PNG_COLOR_TYPE_PALETTE) {
michael@0 520 bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
michael@0 521 *configp = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
michael@0 522 // now see if we can upscale to their requested config
michael@0 523 if (!canUpscalePaletteToConfig(*configp, paletteHasAlpha)) {
michael@0 524 *configp = SkBitmap::kIndex8_Config;
michael@0 525 }
michael@0 526 } else {
michael@0 527 png_color_16p transpColor = NULL;
michael@0 528 int numTransp = 0;
michael@0 529
michael@0 530 png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor);
michael@0 531
michael@0 532 bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
michael@0 533
michael@0 534 if (valid && numTransp == 1 && transpColor != NULL) {
michael@0 535 /* Compute our transparent color, which we'll match against later.
michael@0 536 We don't really handle 16bit components properly here, since we
michael@0 537 do our compare *after* the values have been knocked down to 8bit
michael@0 538 which means we will find more matches than we should. The real
michael@0 539 fix seems to be to see the actual 16bit components, do the
michael@0 540 compare, and then knock it down to 8bits ourselves.
michael@0 541 */
michael@0 542 if (colorType & PNG_COLOR_MASK_COLOR) {
michael@0 543 if (16 == bitDepth) {
michael@0 544 *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8,
michael@0 545 transpColor->green >> 8,
michael@0 546 transpColor->blue >> 8);
michael@0 547 } else {
michael@0 548 /* We apply the mask because in a very small
michael@0 549 number of corrupt PNGs, (transpColor->red > 255)
michael@0 550 and (bitDepth == 8), for certain versions of libpng. */
michael@0 551 *theTranspColorp = SkPackARGB32(0xFF,
michael@0 552 0xFF & (transpColor->red),
michael@0 553 0xFF & (transpColor->green),
michael@0 554 0xFF & (transpColor->blue));
michael@0 555 }
michael@0 556 } else { // gray
michael@0 557 if (16 == bitDepth) {
michael@0 558 *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8,
michael@0 559 transpColor->gray >> 8,
michael@0 560 transpColor->gray >> 8);
michael@0 561 } else {
michael@0 562 /* We apply the mask because in a very small
michael@0 563 number of corrupt PNGs, (transpColor->red >
michael@0 564 255) and (bitDepth == 8), for certain versions
michael@0 565 of libpng. For safety we assume the same could
michael@0 566 happen with a grayscale PNG. */
michael@0 567 *theTranspColorp = SkPackARGB32(0xFF,
michael@0 568 0xFF & (transpColor->gray),
michael@0 569 0xFF & (transpColor->gray),
michael@0 570 0xFF & (transpColor->gray));
michael@0 571 }
michael@0 572 }
michael@0 573 }
michael@0 574
michael@0 575 if (valid ||
michael@0 576 PNG_COLOR_TYPE_RGB_ALPHA == colorType ||
michael@0 577 PNG_COLOR_TYPE_GRAY_ALPHA == colorType) {
michael@0 578 *hasAlphap = true;
michael@0 579 }
michael@0 580
michael@0 581 SrcDepth srcDepth = k32Bit_SrcDepth;
michael@0 582 if (PNG_COLOR_TYPE_GRAY == colorType) {
michael@0 583 srcDepth = k8BitGray_SrcDepth;
michael@0 584 // Remove this assert, which fails on desk_pokemonwiki.skp
michael@0 585 //SkASSERT(!*hasAlphap);
michael@0 586 }
michael@0 587
michael@0 588 *configp = this->getPrefConfig(srcDepth, *hasAlphap);
michael@0 589 // now match the request against our capabilities
michael@0 590 if (*hasAlphap) {
michael@0 591 if (*configp != SkBitmap::kARGB_4444_Config) {
michael@0 592 *configp = SkBitmap::kARGB_8888_Config;
michael@0 593 }
michael@0 594 } else {
michael@0 595 if (SkBitmap::kA8_Config == *configp) {
michael@0 596 if (k8BitGray_SrcDepth != srcDepth) {
michael@0 597 // Converting a non grayscale image to A8 is not currently supported.
michael@0 598 *configp = SkBitmap::kARGB_8888_Config;
michael@0 599 }
michael@0 600 } else if (*configp != SkBitmap::kRGB_565_Config &&
michael@0 601 *configp != SkBitmap::kARGB_4444_Config) {
michael@0 602 *configp = SkBitmap::kARGB_8888_Config;
michael@0 603 }
michael@0 604 }
michael@0 605 }
michael@0 606
michael@0 607 // sanity check for size
michael@0 608 {
michael@0 609 int64_t size = sk_64_mul(origWidth, origHeight);
michael@0 610 // now check that if we are 4-bytes per pixel, we also don't overflow
michael@0 611 if (size < 0 || size > (0x7FFFFFFF >> 2)) {
michael@0 612 return false;
michael@0 613 }
michael@0 614 }
michael@0 615
michael@0 616 if (!this->chooseFromOneChoice(*configp, origWidth, origHeight)) {
michael@0 617 return false;
michael@0 618 }
michael@0 619
michael@0 620 // If the image has alpha and the decoder wants unpremultiplied
michael@0 621 // colors, the only supported config is 8888.
michael@0 622 if (this->getRequireUnpremultipliedColors() && *hasAlphap) {
michael@0 623 *configp = SkBitmap::kARGB_8888_Config;
michael@0 624 }
michael@0 625
michael@0 626 if (fImageIndex != NULL) {
michael@0 627 if (SkBitmap::kNo_Config == fImageIndex->fConfig) {
michael@0 628 // This is the first time for this subset decode. From now on,
michael@0 629 // all decodes must be in the same config.
michael@0 630 fImageIndex->fConfig = *configp;
michael@0 631 } else if (fImageIndex->fConfig != *configp) {
michael@0 632 // Requesting a different config for a subsequent decode is not
michael@0 633 // supported. Report failure before we make changes to png_ptr.
michael@0 634 return false;
michael@0 635 }
michael@0 636 }
michael@0 637
michael@0 638 bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType
michael@0 639 && *configp != SkBitmap::kA8_Config;
michael@0 640
michael@0 641 // Unless the user is requesting A8, convert a grayscale image into RGB.
michael@0 642 // GRAY_ALPHA will always be converted to RGB
michael@0 643 if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
michael@0 644 png_set_gray_to_rgb(png_ptr);
michael@0 645 }
michael@0 646
michael@0 647 // Add filler (or alpha) byte (after each RGB triplet) if necessary.
michael@0 648 if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) {
michael@0 649 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
michael@0 650 }
michael@0 651
michael@0 652 return true;
michael@0 653 }
michael@0 654
michael@0 655 typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
michael@0 656
michael@0 657 bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
michael@0 658 bool *hasAlphap, bool *reallyHasAlphap,
michael@0 659 SkColorTable **colorTablep) {
michael@0 660 int numPalette;
michael@0 661 png_colorp palette;
michael@0 662 png_bytep trans;
michael@0 663 int numTrans;
michael@0 664
michael@0 665 png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
michael@0 666
michael@0 667 /* BUGGY IMAGE WORKAROUND
michael@0 668
michael@0 669 We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
michael@0 670 which is a problem since we use the byte as an index. To work around this we grow
michael@0 671 the colortable by 1 (if its < 256) and duplicate the last color into that slot.
michael@0 672 */
michael@0 673 int colorCount = numPalette + (numPalette < 256);
michael@0 674 SkPMColor colorStorage[256]; // worst-case storage
michael@0 675 SkPMColor* colorPtr = colorStorage;
michael@0 676
michael@0 677 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
michael@0 678 png_get_tRNS(png_ptr, info_ptr, &trans, &numTrans, NULL);
michael@0 679 *hasAlphap = (numTrans > 0);
michael@0 680 } else {
michael@0 681 numTrans = 0;
michael@0 682 }
michael@0 683
michael@0 684 // check for bad images that might make us crash
michael@0 685 if (numTrans > numPalette) {
michael@0 686 numTrans = numPalette;
michael@0 687 }
michael@0 688
michael@0 689 int index = 0;
michael@0 690 int transLessThanFF = 0;
michael@0 691
michael@0 692 // Choose which function to use to create the color table. If the final destination's
michael@0 693 // config is unpremultiplied, the color table will store unpremultiplied colors.
michael@0 694 PackColorProc proc;
michael@0 695 if (this->getRequireUnpremultipliedColors()) {
michael@0 696 proc = &SkPackARGB32NoCheck;
michael@0 697 } else {
michael@0 698 proc = &SkPreMultiplyARGB;
michael@0 699 }
michael@0 700 for (; index < numTrans; index++) {
michael@0 701 transLessThanFF |= (int)*trans - 0xFF;
michael@0 702 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
michael@0 703 palette++;
michael@0 704 }
michael@0 705 bool reallyHasAlpha = (transLessThanFF < 0);
michael@0 706
michael@0 707 for (; index < numPalette; index++) {
michael@0 708 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
michael@0 709 palette++;
michael@0 710 }
michael@0 711
michael@0 712 // see BUGGY IMAGE WORKAROUND comment above
michael@0 713 if (numPalette < 256) {
michael@0 714 *colorPtr = colorPtr[-1];
michael@0 715 }
michael@0 716
michael@0 717 SkAlphaType alphaType = kOpaque_SkAlphaType;
michael@0 718 if (reallyHasAlpha) {
michael@0 719 if (this->getRequireUnpremultipliedColors()) {
michael@0 720 alphaType = kUnpremul_SkAlphaType;
michael@0 721 } else {
michael@0 722 alphaType = kPremul_SkAlphaType;
michael@0 723 }
michael@0 724 }
michael@0 725
michael@0 726 *colorTablep = SkNEW_ARGS(SkColorTable,
michael@0 727 (colorStorage, colorCount, alphaType));
michael@0 728 *reallyHasAlphap = reallyHasAlpha;
michael@0 729 return true;
michael@0 730 }
michael@0 731
michael@0 732 #ifdef SK_BUILD_FOR_ANDROID
michael@0 733
michael@0 734 bool SkPNGImageDecoder::onBuildTileIndex(SkStreamRewindable* sk_stream, int *width, int *height) {
michael@0 735 png_structp png_ptr;
michael@0 736 png_infop info_ptr;
michael@0 737
michael@0 738 if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
michael@0 739 return false;
michael@0 740 }
michael@0 741
michael@0 742 if (setjmp(png_jmpbuf(png_ptr)) != 0) {
michael@0 743 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
michael@0 744 return false;
michael@0 745 }
michael@0 746
michael@0 747 png_uint_32 origWidth, origHeight;
michael@0 748 int bitDepth, colorType;
michael@0 749 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
michael@0 750 &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
michael@0 751
michael@0 752 *width = origWidth;
michael@0 753 *height = origHeight;
michael@0 754
michael@0 755 png_build_index(png_ptr);
michael@0 756
michael@0 757 if (fImageIndex) {
michael@0 758 SkDELETE(fImageIndex);
michael@0 759 }
michael@0 760 fImageIndex = SkNEW_ARGS(SkPNGImageIndex, (sk_stream, png_ptr, info_ptr));
michael@0 761
michael@0 762 return true;
michael@0 763 }
michael@0 764
michael@0 765 bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
michael@0 766 if (NULL == fImageIndex) {
michael@0 767 return false;
michael@0 768 }
michael@0 769
michael@0 770 png_structp png_ptr = fImageIndex->fPng_ptr;
michael@0 771 png_infop info_ptr = fImageIndex->fInfo_ptr;
michael@0 772 if (setjmp(png_jmpbuf(png_ptr))) {
michael@0 773 return false;
michael@0 774 }
michael@0 775
michael@0 776 png_uint_32 origWidth, origHeight;
michael@0 777 int bitDepth, colorType, interlaceType;
michael@0 778 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
michael@0 779 &colorType, &interlaceType, int_p_NULL, int_p_NULL);
michael@0 780
michael@0 781 SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
michael@0 782
michael@0 783 if (!rect.intersect(region)) {
michael@0 784 // If the requested region is entirely outside the image, just
michael@0 785 // returns false
michael@0 786 return false;
michael@0 787 }
michael@0 788
michael@0 789 SkBitmap::Config config;
michael@0 790 bool hasAlpha = false;
michael@0 791 SkPMColor theTranspColor = 0; // 0 tells us not to try to match
michael@0 792
michael@0 793 if (!this->getBitmapConfig(png_ptr, info_ptr, &config, &hasAlpha, &theTranspColor)) {
michael@0 794 return false;
michael@0 795 }
michael@0 796
michael@0 797 const int sampleSize = this->getSampleSize();
michael@0 798 SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
michael@0 799
michael@0 800 SkBitmap decodedBitmap;
michael@0 801 decodedBitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
michael@0 802
michael@0 803 // from here down we are concerned with colortables and pixels
michael@0 804
michael@0 805 // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
michael@0 806 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
michael@0 807 // draw lots faster if we can flag the bitmap has being opaque
michael@0 808 bool reallyHasAlpha = false;
michael@0 809 SkColorTable* colorTable = NULL;
michael@0 810
michael@0 811 if (colorType == PNG_COLOR_TYPE_PALETTE) {
michael@0 812 decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
michael@0 813 }
michael@0 814
michael@0 815 SkAutoUnref aur(colorTable);
michael@0 816
michael@0 817 // Check ahead of time if the swap(dest, src) is possible.
michael@0 818 // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
michael@0 819 // If no, then we will use alloc to allocate pixels to prevent garbage collection.
michael@0 820 int w = rect.width() / sampleSize;
michael@0 821 int h = rect.height() / sampleSize;
michael@0 822 const bool swapOnly = (rect == region) && (w == decodedBitmap.width()) &&
michael@0 823 (h == decodedBitmap.height()) && bm->isNull();
michael@0 824 const bool needColorTable = SkBitmap::kIndex8_Config == config;
michael@0 825 if (swapOnly) {
michael@0 826 if (!this->allocPixelRef(&decodedBitmap, needColorTable ? colorTable : NULL)) {
michael@0 827 return false;
michael@0 828 }
michael@0 829 } else {
michael@0 830 if (!decodedBitmap.allocPixels(NULL, needColorTable ? colorTable : NULL)) {
michael@0 831 return false;
michael@0 832 }
michael@0 833 }
michael@0 834 SkAutoLockPixels alp(decodedBitmap);
michael@0 835
michael@0 836 /* Turn on interlace handling. REQUIRED if you are not using
michael@0 837 * png_read_image(). To see how to handle interlacing passes,
michael@0 838 * see the png_read_row() method below:
michael@0 839 */
michael@0 840 const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ?
michael@0 841 png_set_interlace_handling(png_ptr) : 1;
michael@0 842
michael@0 843 /* Optional call to gamma correct and add the background to the palette
michael@0 844 * and update info structure. REQUIRED if you are expecting libpng to
michael@0 845 * update the palette for you (ie you selected such a transform above).
michael@0 846 */
michael@0 847
michael@0 848 // Direct access to png_ptr fields is deprecated in libpng > 1.2.
michael@0 849 #if defined(PNG_1_0_X) || defined (PNG_1_2_X)
michael@0 850 png_ptr->pass = 0;
michael@0 851 #else
michael@0 852 // FIXME: This sets pass as desired, but also sets iwidth. Is that ok?
michael@0 853 png_set_interlaced_pass(png_ptr, 0);
michael@0 854 #endif
michael@0 855 png_read_update_info(png_ptr, info_ptr);
michael@0 856
michael@0 857 int actualTop = rect.fTop;
michael@0 858
michael@0 859 if ((SkBitmap::kA8_Config == config || SkBitmap::kIndex8_Config == config)
michael@0 860 && 1 == sampleSize) {
michael@0 861 if (SkBitmap::kA8_Config == config) {
michael@0 862 // For an A8 bitmap, we assume there is an alpha for speed. It is
michael@0 863 // possible the bitmap is opaque, but that is an unlikely use case
michael@0 864 // since it would not be very interesting.
michael@0 865 reallyHasAlpha = true;
michael@0 866 // A8 is only allowed if the original was GRAY.
michael@0 867 SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
michael@0 868 }
michael@0 869
michael@0 870 for (int i = 0; i < number_passes; i++) {
michael@0 871 png_configure_decoder(png_ptr, &actualTop, i);
michael@0 872 for (int j = 0; j < rect.fTop - actualTop; j++) {
michael@0 873 uint8_t* bmRow = decodedBitmap.getAddr8(0, 0);
michael@0 874 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
michael@0 875 }
michael@0 876 png_uint_32 bitmapHeight = (png_uint_32) decodedBitmap.height();
michael@0 877 for (png_uint_32 y = 0; y < bitmapHeight; y++) {
michael@0 878 uint8_t* bmRow = decodedBitmap.getAddr8(0, y);
michael@0 879 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
michael@0 880 }
michael@0 881 }
michael@0 882 } else {
michael@0 883 SkScaledBitmapSampler::SrcConfig sc;
michael@0 884 int srcBytesPerPixel = 4;
michael@0 885
michael@0 886 if (colorTable != NULL) {
michael@0 887 sc = SkScaledBitmapSampler::kIndex;
michael@0 888 srcBytesPerPixel = 1;
michael@0 889 } else if (SkBitmap::kA8_Config == config) {
michael@0 890 // A8 is only allowed if the original was GRAY.
michael@0 891 SkASSERT(PNG_COLOR_TYPE_GRAY == colorType);
michael@0 892 sc = SkScaledBitmapSampler::kGray;
michael@0 893 srcBytesPerPixel = 1;
michael@0 894 } else if (hasAlpha) {
michael@0 895 sc = SkScaledBitmapSampler::kRGBA;
michael@0 896 } else {
michael@0 897 sc = SkScaledBitmapSampler::kRGBX;
michael@0 898 }
michael@0 899
michael@0 900 /* We have to pass the colortable explicitly, since we may have one
michael@0 901 even if our decodedBitmap doesn't, due to the request that we
michael@0 902 upscale png's palette to a direct model
michael@0 903 */
michael@0 904 SkAutoLockColors ctLock(colorTable);
michael@0 905 if (!sampler.begin(&decodedBitmap, sc, *this, ctLock.colors())) {
michael@0 906 return false;
michael@0 907 }
michael@0 908 const int height = decodedBitmap.height();
michael@0 909
michael@0 910 if (number_passes > 1) {
michael@0 911 SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
michael@0 912 uint8_t* base = (uint8_t*)storage.get();
michael@0 913 size_t rb = origWidth * srcBytesPerPixel;
michael@0 914
michael@0 915 for (int i = 0; i < number_passes; i++) {
michael@0 916 png_configure_decoder(png_ptr, &actualTop, i);
michael@0 917 for (int j = 0; j < rect.fTop - actualTop; j++) {
michael@0 918 uint8_t* bmRow = (uint8_t*)decodedBitmap.getPixels();
michael@0 919 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
michael@0 920 }
michael@0 921 uint8_t* row = base;
michael@0 922 for (int32_t y = 0; y < rect.height(); y++) {
michael@0 923 uint8_t* bmRow = row;
michael@0 924 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
michael@0 925 row += rb;
michael@0 926 }
michael@0 927 }
michael@0 928 // now sample it
michael@0 929 base += sampler.srcY0() * rb;
michael@0 930 for (int y = 0; y < height; y++) {
michael@0 931 reallyHasAlpha |= sampler.next(base);
michael@0 932 base += sampler.srcDY() * rb;
michael@0 933 }
michael@0 934 } else {
michael@0 935 SkAutoMalloc storage(origWidth * srcBytesPerPixel);
michael@0 936 uint8_t* srcRow = (uint8_t*)storage.get();
michael@0 937
michael@0 938 png_configure_decoder(png_ptr, &actualTop, 0);
michael@0 939 skip_src_rows(png_ptr, srcRow, sampler.srcY0());
michael@0 940
michael@0 941 for (int i = 0; i < rect.fTop - actualTop; i++) {
michael@0 942 uint8_t* bmRow = (uint8_t*)decodedBitmap.getPixels();
michael@0 943 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
michael@0 944 }
michael@0 945 for (int y = 0; y < height; y++) {
michael@0 946 uint8_t* tmp = srcRow;
michael@0 947 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
michael@0 948 reallyHasAlpha |= sampler.next(srcRow);
michael@0 949 if (y < height - 1) {
michael@0 950 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
michael@0 951 }
michael@0 952 }
michael@0 953 }
michael@0 954 }
michael@0 955
michael@0 956 if (0 != theTranspColor) {
michael@0 957 reallyHasAlpha |= substituteTranspColor(&decodedBitmap, theTranspColor);
michael@0 958 }
michael@0 959 if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {
michael@0 960 switch (decodedBitmap.config()) {
michael@0 961 case SkBitmap::kIndex8_Config:
michael@0 962 // Fall through.
michael@0 963 case SkBitmap::kARGB_4444_Config:
michael@0 964 // We have chosen not to support unpremul for these configs.
michael@0 965 return false;
michael@0 966 default: {
michael@0 967 // Fall through to finish the decode. This config either
michael@0 968 // supports unpremul or it is irrelevant because it has no
michael@0 969 // alpha (or only alpha).
michael@0 970 // These brackets prevent a warning.
michael@0 971 }
michael@0 972 }
michael@0 973 }
michael@0 974 SkAlphaType alphaType = kOpaque_SkAlphaType;
michael@0 975 if (reallyHasAlpha) {
michael@0 976 if (this->getRequireUnpremultipliedColors()) {
michael@0 977 alphaType = kUnpremul_SkAlphaType;
michael@0 978 } else {
michael@0 979 alphaType = kPremul_SkAlphaType;
michael@0 980 }
michael@0 981 }
michael@0 982 decodedBitmap.setAlphaType(alphaType);
michael@0 983
michael@0 984 if (swapOnly) {
michael@0 985 bm->swap(decodedBitmap);
michael@0 986 return true;
michael@0 987 }
michael@0 988 return this->cropBitmap(bm, &decodedBitmap, sampleSize, region.x(), region.y(),
michael@0 989 region.width(), region.height(), 0, rect.y());
michael@0 990 }
michael@0 991 #endif
michael@0 992
michael@0 993 ///////////////////////////////////////////////////////////////////////////////
michael@0 994
michael@0 995 #include "SkColorPriv.h"
michael@0 996 #include "SkUnPreMultiply.h"
michael@0 997
michael@0 998 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
michael@0 999 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
michael@0 1000 if (!sk_stream->write(data, len)) {
michael@0 1001 png_error(png_ptr, "sk_write_fn Error!");
michael@0 1002 }
michael@0 1003 }
michael@0 1004
michael@0 1005 static transform_scanline_proc choose_proc(SkBitmap::Config config,
michael@0 1006 bool hasAlpha) {
michael@0 1007 // we don't care about search on alpha if we're kIndex8, since only the
michael@0 1008 // colortable packing cares about that distinction, not the pixels
michael@0 1009 if (SkBitmap::kIndex8_Config == config) {
michael@0 1010 hasAlpha = false; // we store false in the table entries for kIndex8
michael@0 1011 }
michael@0 1012
michael@0 1013 static const struct {
michael@0 1014 SkBitmap::Config fConfig;
michael@0 1015 bool fHasAlpha;
michael@0 1016 transform_scanline_proc fProc;
michael@0 1017 } gMap[] = {
michael@0 1018 { SkBitmap::kRGB_565_Config, false, transform_scanline_565 },
michael@0 1019 { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 },
michael@0 1020 { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 },
michael@0 1021 { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 },
michael@0 1022 { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 },
michael@0 1023 { SkBitmap::kIndex8_Config, false, transform_scanline_memcpy },
michael@0 1024 };
michael@0 1025
michael@0 1026 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
michael@0 1027 if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) {
michael@0 1028 return gMap[i].fProc;
michael@0 1029 }
michael@0 1030 }
michael@0 1031 sk_throw();
michael@0 1032 return NULL;
michael@0 1033 }
michael@0 1034
michael@0 1035 // return the minimum legal bitdepth (by png standards) for this many colortable
michael@0 1036 // entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
michael@0 1037 // we can use fewer bits per in png
michael@0 1038 static int computeBitDepth(int colorCount) {
michael@0 1039 #if 0
michael@0 1040 int bits = SkNextLog2(colorCount);
michael@0 1041 SkASSERT(bits >= 1 && bits <= 8);
michael@0 1042 // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8)
michael@0 1043 return SkNextPow2(bits);
michael@0 1044 #else
michael@0 1045 // for the moment, we don't know how to pack bitdepth < 8
michael@0 1046 return 8;
michael@0 1047 #endif
michael@0 1048 }
michael@0 1049
michael@0 1050 /* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
michael@0 1051 pack trans[] and return the number of trans[] entries written. If hasAlpha
michael@0 1052 is false, the return value will always be 0.
michael@0 1053
michael@0 1054 Note: this routine takes care of unpremultiplying the RGB values when we
michael@0 1055 have alpha in the colortable, since png doesn't support premul colors
michael@0 1056 */
michael@0 1057 static inline int pack_palette(SkColorTable* ctable,
michael@0 1058 png_color* SK_RESTRICT palette,
michael@0 1059 png_byte* SK_RESTRICT trans, bool hasAlpha) {
michael@0 1060 SkAutoLockColors alc(ctable);
michael@0 1061 const SkPMColor* SK_RESTRICT colors = alc.colors();
michael@0 1062 const int ctCount = ctable->count();
michael@0 1063 int i, num_trans = 0;
michael@0 1064
michael@0 1065 if (hasAlpha) {
michael@0 1066 /* first see if we have some number of fully opaque at the end of the
michael@0 1067 ctable. PNG allows num_trans < num_palette, but all of the trans
michael@0 1068 entries must come first in the palette. If I was smarter, I'd
michael@0 1069 reorder the indices and ctable so that all non-opaque colors came
michael@0 1070 first in the palette. But, since that would slow down the encode,
michael@0 1071 I'm leaving the indices and ctable order as is, and just looking
michael@0 1072 at the tail of the ctable for opaqueness.
michael@0 1073 */
michael@0 1074 num_trans = ctCount;
michael@0 1075 for (i = ctCount - 1; i >= 0; --i) {
michael@0 1076 if (SkGetPackedA32(colors[i]) != 0xFF) {
michael@0 1077 break;
michael@0 1078 }
michael@0 1079 num_trans -= 1;
michael@0 1080 }
michael@0 1081
michael@0 1082 const SkUnPreMultiply::Scale* SK_RESTRICT table =
michael@0 1083 SkUnPreMultiply::GetScaleTable();
michael@0 1084
michael@0 1085 for (i = 0; i < num_trans; i++) {
michael@0 1086 const SkPMColor c = *colors++;
michael@0 1087 const unsigned a = SkGetPackedA32(c);
michael@0 1088 const SkUnPreMultiply::Scale s = table[a];
michael@0 1089 trans[i] = a;
michael@0 1090 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
michael@0 1091 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
michael@0 1092 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
michael@0 1093 }
michael@0 1094 // now fall out of this if-block to use common code for the trailing
michael@0 1095 // opaque entries
michael@0 1096 }
michael@0 1097
michael@0 1098 // these (remaining) entries are opaque
michael@0 1099 for (i = num_trans; i < ctCount; i++) {
michael@0 1100 SkPMColor c = *colors++;
michael@0 1101 palette[i].red = SkGetPackedR32(c);
michael@0 1102 palette[i].green = SkGetPackedG32(c);
michael@0 1103 palette[i].blue = SkGetPackedB32(c);
michael@0 1104 }
michael@0 1105 return num_trans;
michael@0 1106 }
michael@0 1107
michael@0 1108 class SkPNGImageEncoder : public SkImageEncoder {
michael@0 1109 protected:
michael@0 1110 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
michael@0 1111 private:
michael@0 1112 bool doEncode(SkWStream* stream, const SkBitmap& bm,
michael@0 1113 const bool& hasAlpha, int colorType,
michael@0 1114 int bitDepth, SkBitmap::Config config,
michael@0 1115 png_color_8& sig_bit);
michael@0 1116
michael@0 1117 typedef SkImageEncoder INHERITED;
michael@0 1118 };
michael@0 1119
michael@0 1120 bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap,
michael@0 1121 int /*quality*/) {
michael@0 1122 SkBitmap::Config config = bitmap.config();
michael@0 1123
michael@0 1124 const bool hasAlpha = !bitmap.isOpaque();
michael@0 1125 int colorType = PNG_COLOR_MASK_COLOR;
michael@0 1126 int bitDepth = 8; // default for color
michael@0 1127 png_color_8 sig_bit;
michael@0 1128
michael@0 1129 switch (config) {
michael@0 1130 case SkBitmap::kIndex8_Config:
michael@0 1131 colorType |= PNG_COLOR_MASK_PALETTE;
michael@0 1132 // fall through to the ARGB_8888 case
michael@0 1133 case SkBitmap::kARGB_8888_Config:
michael@0 1134 sig_bit.red = 8;
michael@0 1135 sig_bit.green = 8;
michael@0 1136 sig_bit.blue = 8;
michael@0 1137 sig_bit.alpha = 8;
michael@0 1138 break;
michael@0 1139 case SkBitmap::kARGB_4444_Config:
michael@0 1140 sig_bit.red = 4;
michael@0 1141 sig_bit.green = 4;
michael@0 1142 sig_bit.blue = 4;
michael@0 1143 sig_bit.alpha = 4;
michael@0 1144 break;
michael@0 1145 case SkBitmap::kRGB_565_Config:
michael@0 1146 sig_bit.red = 5;
michael@0 1147 sig_bit.green = 6;
michael@0 1148 sig_bit.blue = 5;
michael@0 1149 sig_bit.alpha = 0;
michael@0 1150 break;
michael@0 1151 default:
michael@0 1152 return false;
michael@0 1153 }
michael@0 1154
michael@0 1155 if (hasAlpha) {
michael@0 1156 // don't specify alpha if we're a palette, even if our ctable has alpha
michael@0 1157 if (!(colorType & PNG_COLOR_MASK_PALETTE)) {
michael@0 1158 colorType |= PNG_COLOR_MASK_ALPHA;
michael@0 1159 }
michael@0 1160 } else {
michael@0 1161 sig_bit.alpha = 0;
michael@0 1162 }
michael@0 1163
michael@0 1164 SkAutoLockPixels alp(bitmap);
michael@0 1165 // readyToDraw checks for pixels (and colortable if that is required)
michael@0 1166 if (!bitmap.readyToDraw()) {
michael@0 1167 return false;
michael@0 1168 }
michael@0 1169
michael@0 1170 // we must do this after we have locked the pixels
michael@0 1171 SkColorTable* ctable = bitmap.getColorTable();
michael@0 1172 if (NULL != ctable) {
michael@0 1173 if (ctable->count() == 0) {
michael@0 1174 return false;
michael@0 1175 }
michael@0 1176 // check if we can store in fewer than 8 bits
michael@0 1177 bitDepth = computeBitDepth(ctable->count());
michael@0 1178 }
michael@0 1179
michael@0 1180 return doEncode(stream, bitmap, hasAlpha, colorType,
michael@0 1181 bitDepth, config, sig_bit);
michael@0 1182 }
michael@0 1183
michael@0 1184 bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
michael@0 1185 const bool& hasAlpha, int colorType,
michael@0 1186 int bitDepth, SkBitmap::Config config,
michael@0 1187 png_color_8& sig_bit) {
michael@0 1188
michael@0 1189 png_structp png_ptr;
michael@0 1190 png_infop info_ptr;
michael@0 1191
michael@0 1192 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,
michael@0 1193 NULL);
michael@0 1194 if (NULL == png_ptr) {
michael@0 1195 return false;
michael@0 1196 }
michael@0 1197
michael@0 1198 info_ptr = png_create_info_struct(png_ptr);
michael@0 1199 if (NULL == info_ptr) {
michael@0 1200 png_destroy_write_struct(&png_ptr, png_infopp_NULL);
michael@0 1201 return false;
michael@0 1202 }
michael@0 1203
michael@0 1204 /* Set error handling. REQUIRED if you aren't supplying your own
michael@0 1205 * error handling functions in the png_create_write_struct() call.
michael@0 1206 */
michael@0 1207 if (setjmp(png_jmpbuf(png_ptr))) {
michael@0 1208 png_destroy_write_struct(&png_ptr, &info_ptr);
michael@0 1209 return false;
michael@0 1210 }
michael@0 1211
michael@0 1212 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);
michael@0 1213
michael@0 1214 /* Set the image information here. Width and height are up to 2^31,
michael@0 1215 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
michael@0 1216 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
michael@0 1217 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
michael@0 1218 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
michael@0 1219 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
michael@0 1220 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
michael@0 1221 */
michael@0 1222
michael@0 1223 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
michael@0 1224 bitDepth, colorType,
michael@0 1225 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
michael@0 1226 PNG_FILTER_TYPE_BASE);
michael@0 1227
michael@0 1228 // set our colortable/trans arrays if needed
michael@0 1229 png_color paletteColors[256];
michael@0 1230 png_byte trans[256];
michael@0 1231 if (SkBitmap::kIndex8_Config == config) {
michael@0 1232 SkColorTable* ct = bitmap.getColorTable();
michael@0 1233 int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);
michael@0 1234 png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());
michael@0 1235 if (numTrans > 0) {
michael@0 1236 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);
michael@0 1237 }
michael@0 1238 }
michael@0 1239
michael@0 1240 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
michael@0 1241 png_write_info(png_ptr, info_ptr);
michael@0 1242
michael@0 1243 const char* srcImage = (const char*)bitmap.getPixels();
michael@0 1244 SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);
michael@0 1245 char* storage = (char*)rowStorage.get();
michael@0 1246 transform_scanline_proc proc = choose_proc(config, hasAlpha);
michael@0 1247
michael@0 1248 for (int y = 0; y < bitmap.height(); y++) {
michael@0 1249 png_bytep row_ptr = (png_bytep)storage;
michael@0 1250 proc(srcImage, bitmap.width(), storage);
michael@0 1251 png_write_rows(png_ptr, &row_ptr, 1);
michael@0 1252 srcImage += bitmap.rowBytes();
michael@0 1253 }
michael@0 1254
michael@0 1255 png_write_end(png_ptr, info_ptr);
michael@0 1256
michael@0 1257 /* clean up after the write, and free any memory allocated */
michael@0 1258 png_destroy_write_struct(&png_ptr, &info_ptr);
michael@0 1259 return true;
michael@0 1260 }
michael@0 1261
michael@0 1262 ///////////////////////////////////////////////////////////////////////////////
michael@0 1263 DEFINE_DECODER_CREATOR(PNGImageDecoder);
michael@0 1264 DEFINE_ENCODER_CREATOR(PNGImageEncoder);
michael@0 1265 ///////////////////////////////////////////////////////////////////////////////
michael@0 1266
michael@0 1267 static bool is_png(SkStreamRewindable* stream) {
michael@0 1268 char buf[PNG_BYTES_TO_CHECK];
michael@0 1269 if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
michael@0 1270 !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
michael@0 1271 return true;
michael@0 1272 }
michael@0 1273 return false;
michael@0 1274 }
michael@0 1275
michael@0 1276 SkImageDecoder* sk_libpng_dfactory(SkStreamRewindable* stream) {
michael@0 1277 if (is_png(stream)) {
michael@0 1278 return SkNEW(SkPNGImageDecoder);
michael@0 1279 }
michael@0 1280 return NULL;
michael@0 1281 }
michael@0 1282
michael@0 1283 static SkImageDecoder::Format get_format_png(SkStreamRewindable* stream) {
michael@0 1284 if (is_png(stream)) {
michael@0 1285 return SkImageDecoder::kPNG_Format;
michael@0 1286 }
michael@0 1287 return SkImageDecoder::kUnknown_Format;
michael@0 1288 }
michael@0 1289
michael@0 1290 SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
michael@0 1291 return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
michael@0 1292 }
michael@0 1293
michael@0 1294 static SkImageDecoder_DecodeReg gDReg(sk_libpng_dfactory);
michael@0 1295 static SkImageDecoder_FormatReg gFormatReg(get_format_png);
michael@0 1296 static SkImageEncoder_EncodeReg gEReg(sk_libpng_efactory);

mercurial