| OLD | NEW |
| (Empty) |
| 1 /* libs/graphics/images/SkImageDecoder_libpng.cpp | |
| 2 ** | |
| 3 ** Copyright 2006, The Android Open Source Project | |
| 4 ** | |
| 5 ** Licensed under the Apache License, Version 2.0 (the "License"); | |
| 6 ** you may not use this file except in compliance with the License. | |
| 7 ** You may obtain a copy of the License at | |
| 8 ** | |
| 9 ** http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 ** | |
| 11 ** Unless required by applicable law or agreed to in writing, software | |
| 12 ** distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 ** See the License for the specific language governing permissions and | |
| 15 ** limitations under the License. | |
| 16 */ | |
| 17 | |
| 18 #include "SkImageDecoder.h" | |
| 19 #include "SkColor.h" | |
| 20 #include "SkColorPriv.h" | |
| 21 #include "SkDither.h" | |
| 22 #include "SkMath.h" | |
| 23 #include "SkScaledBitmapSampler.h" | |
| 24 #include "SkStream.h" | |
| 25 #include "SkTemplates.h" | |
| 26 #include "SkUtils.h" | |
| 27 | |
| 28 extern "C" { | |
| 29 #include "png.h" | |
| 30 } | |
| 31 | |
| 32 class SkPNGImageDecoder : public SkImageDecoder { | |
| 33 public: | |
| 34 virtual Format getFormat() const { | |
| 35 return kPNG_Format; | |
| 36 } | |
| 37 | |
| 38 protected: | |
| 39 virtual bool onDecode(SkStream* stream, SkBitmap* bm, | |
| 40 SkBitmap::Config pref, Mode); | |
| 41 }; | |
| 42 | |
| 43 #ifndef png_jmpbuf | |
| 44 # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) | |
| 45 #endif | |
| 46 | |
| 47 #define PNG_BYTES_TO_CHECK 4 | |
| 48 | |
| 49 /* Automatically clean up after throwing an exception */ | |
| 50 struct PNGAutoClean { | |
| 51 PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {} | |
| 52 ~PNGAutoClean() { | |
| 53 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); | |
| 54 } | |
| 55 private: | |
| 56 png_structp png_ptr; | |
| 57 png_infop info_ptr; | |
| 58 }; | |
| 59 | |
| 60 SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream* stream) { | |
| 61 char buf[PNG_BYTES_TO_CHECK]; | |
| 62 if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK && | |
| 63 !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) { | |
| 64 return SkNEW(SkPNGImageDecoder); | |
| 65 } | |
| 66 return NULL; | |
| 67 } | |
| 68 | |
| 69 static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) { | |
| 70 SkStream* sk_stream = (SkStream*) png_ptr->io_ptr; | |
| 71 size_t bytes = sk_stream->read(data, length); | |
| 72 if (bytes != length) { | |
| 73 png_error(png_ptr, "Read Error!"); | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { | |
| 78 SkImageDecoder::Peeker* peeker = | |
| 79 (SkImageDecoder::Peeker*)png_get_user_chunk_ptr(png_ptr); | |
| 80 // peek() returning true means continue decoding | |
| 81 return peeker->peek((const char*)chunk->name, chunk->data, chunk->size) ? | |
| 82 1 : -1; | |
| 83 } | |
| 84 | |
| 85 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { | |
| 86 #if 0 | |
| 87 SkDebugf("------ png error %s\n", msg); | |
| 88 #endif | |
| 89 longjmp(png_jmpbuf(png_ptr), 1); | |
| 90 } | |
| 91 | |
| 92 static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) { | |
| 93 for (int i = 0; i < count; i++) { | |
| 94 uint8_t* tmp = storage; | |
| 95 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 static bool pos_le(int value, int max) { | |
| 100 return value > 0 && value <= max; | |
| 101 } | |
| 102 | |
| 103 static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) { | |
| 104 SkASSERT(bm->config() == SkBitmap::kARGB_8888_Config); | |
| 105 | |
| 106 bool reallyHasAlpha = false; | |
| 107 | |
| 108 for (int y = bm->height() - 1; y >= 0; --y) { | |
| 109 SkPMColor* p = bm->getAddr32(0, y); | |
| 110 for (int x = bm->width() - 1; x >= 0; --x) { | |
| 111 if (match == *p) { | |
| 112 *p = 0; | |
| 113 reallyHasAlpha = true; | |
| 114 } | |
| 115 p += 1; | |
| 116 } | |
| 117 } | |
| 118 return reallyHasAlpha; | |
| 119 } | |
| 120 | |
| 121 bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, | |
| 122 SkBitmap::Config prefConfig, Mode mode) { | |
| 123 // SkAutoTrace apr("SkPNGImageDecoder::onDecode"); | |
| 124 | |
| 125 /* Create and initialize the png_struct with the desired error handler | |
| 126 * functions. If you want to use the default stderr and longjump method, | |
| 127 * you can supply NULL for the last three parameters. We also supply the | |
| 128 * the compiler header file version, so that we know if the application | |
| 129 * was compiled with a compatible version of the library. */ | |
| 130 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, | |
| 131 NULL, sk_error_fn, NULL); | |
| 132 // png_voidp user_error_ptr, user_error_fn, user_warning_fn); | |
| 133 if (png_ptr == NULL) { | |
| 134 return false; | |
| 135 } | |
| 136 | |
| 137 /* Allocate/initialize the memory for image information. */ | |
| 138 png_infop info_ptr = png_create_info_struct(png_ptr); | |
| 139 if (info_ptr == NULL) { | |
| 140 png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); | |
| 141 return false; | |
| 142 } | |
| 143 | |
| 144 PNGAutoClean autoClean(png_ptr, info_ptr); | |
| 145 | |
| 146 /* Set error handling if you are using the setjmp/longjmp method (this is | |
| 147 * the normal method of doing things with libpng). REQUIRED unless you | |
| 148 * set up your own error handlers in the png_create_read_struct() earlier. | |
| 149 */ | |
| 150 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 151 return false; | |
| 152 } | |
| 153 | |
| 154 /* If you are using replacement read functions, instead of calling | |
| 155 * png_init_io() here you would call: | |
| 156 */ | |
| 157 png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn); | |
| 158 /* where user_io_ptr is a structure you want available to the callbacks */ | |
| 159 /* If we have already read some of the signature */ | |
| 160 // png_set_sig_bytes(png_ptr, 0 /* sig_read */ ); | |
| 161 | |
| 162 // hookup our peeker so we can see any user-chunks the caller may be interes
ted in | |
| 163 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"",
0); | |
| 164 if (this->getPeeker()) { | |
| 165 png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_rea
d_user_chunk); | |
| 166 } | |
| 167 | |
| 168 /* The call to png_read_info() gives us all of the information from the | |
| 169 * PNG file before the first IDAT (image data chunk). */ | |
| 170 png_read_info(png_ptr, info_ptr); | |
| 171 png_uint_32 origWidth, origHeight; | |
| 172 int bit_depth, color_type, interlace_type; | |
| 173 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth, &color_
type, | |
| 174 &interlace_type, int_p_NULL, int_p_NULL); | |
| 175 | |
| 176 /* tell libpng to strip 16 bit/color files down to 8 bits/color */ | |
| 177 if (bit_depth == 16) { | |
| 178 png_set_strip_16(png_ptr); | |
| 179 } | |
| 180 /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single | |
| 181 * byte into separate bytes (useful for paletted and grayscale images). */ | |
| 182 if (bit_depth < 8) { | |
| 183 png_set_packing(png_ptr); | |
| 184 } | |
| 185 /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ | |
| 186 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { | |
| 187 png_set_gray_1_2_4_to_8(png_ptr); | |
| 188 } | |
| 189 | |
| 190 /* Make a grayscale image into RGB. */ | |
| 191 if (color_type == PNG_COLOR_TYPE_GRAY || | |
| 192 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { | |
| 193 png_set_gray_to_rgb(png_ptr); | |
| 194 } | |
| 195 | |
| 196 SkBitmap::Config config; | |
| 197 bool hasAlpha = false; | |
| 198 bool doDither = this->getDitherImage(); | |
| 199 SkPMColor theTranspColor = 0; // 0 tells us not to try to match | |
| 200 | |
| 201 // check for sBIT chunk data, in case we should disable dithering because | |
| 202 // our data is not truely 8bits per component | |
| 203 if (doDither) { | |
| 204 #if 0 | |
| 205 SkDebugf("----- sBIT %d %d %d %d\n", info_ptr->sig_bit.red, | |
| 206 info_ptr->sig_bit.green, info_ptr->sig_bit.blue, | |
| 207 info_ptr->sig_bit.alpha); | |
| 208 #endif | |
| 209 // 0 seems to indicate no information available | |
| 210 if (pos_le(info_ptr->sig_bit.red, SK_R16_BITS) && | |
| 211 pos_le(info_ptr->sig_bit.green, SK_G16_BITS) && | |
| 212 pos_le(info_ptr->sig_bit.blue, SK_B16_BITS)) { | |
| 213 doDither = false; | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 if (color_type == PNG_COLOR_TYPE_PALETTE) { | |
| 218 config = SkBitmap::kIndex8_Config; // defer sniffing for hasAlpha | |
| 219 } else { | |
| 220 png_color_16p transpColor = NULL; | |
| 221 int numTransp = 0; | |
| 222 | |
| 223 png_get_tRNS(png_ptr, info_ptr, NULL, &numTransp, &transpColor); | |
| 224 | |
| 225 bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS); | |
| 226 | |
| 227 if (valid && numTransp == 1 && transpColor != NULL) { | |
| 228 /* Compute our transparent color, which we'll match against later. | |
| 229 We don't really handle 16bit components properly here, since we | |
| 230 do our compare *after* the values have been knocked down to 8bit | |
| 231 which means we will find more matches than we should. The real | |
| 232 fix seems to be to see the actual 16bit components, do the | |
| 233 compare, and then knock it down to 8bits ourselves. | |
| 234 */ | |
| 235 if (color_type & PNG_COLOR_MASK_COLOR) { | |
| 236 if (16 == bit_depth) { | |
| 237 theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8, | |
| 238 transpColor->green >> 8, transpColor->blue >> 8); | |
| 239 } else { | |
| 240 theTranspColor = SkPackARGB32(0xFF, transpColor->red, | |
| 241 transpColor->green, transpColor->blue); | |
| 242 } | |
| 243 } else { // gray | |
| 244 if (16 == bit_depth) { | |
| 245 theTranspColor = SkPackARGB32(0xFF, transpColor->gray >> 8, | |
| 246 transpColor->gray >> 8, transpColor->gray >> 8); | |
| 247 } else { | |
| 248 theTranspColor = SkPackARGB32(0xFF, transpColor->gray, | |
| 249 transpColor->gray, transpColor->gray); | |
| 250 } | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 if (valid || | |
| 255 PNG_COLOR_TYPE_RGB_ALPHA == color_type || | |
| 256 PNG_COLOR_TYPE_GRAY_ALPHA == color_type) { | |
| 257 hasAlpha = true; | |
| 258 config = SkBitmap::kARGB_8888_Config; | |
| 259 } else { // we get to choose the config | |
| 260 config = prefConfig; | |
| 261 if (config == SkBitmap::kNo_Config) { | |
| 262 config = SkImageDecoder::GetDeviceConfig(); | |
| 263 } | |
| 264 if (config != SkBitmap::kRGB_565_Config && | |
| 265 config != SkBitmap::kARGB_4444_Config) { | |
| 266 config = SkBitmap::kARGB_8888_Config; | |
| 267 } | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 if (!this->chooseFromOneChoice(config, origWidth, origHeight)) { | |
| 272 return false; | |
| 273 } | |
| 274 | |
| 275 const int sampleSize = this->getSampleSize(); | |
| 276 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); | |
| 277 | |
| 278 decodedBitmap->setConfig(config, sampler.scaledWidth(), | |
| 279 sampler.scaledHeight(), 0); | |
| 280 if (SkImageDecoder::kDecodeBounds_Mode == mode) { | |
| 281 return true; | |
| 282 } | |
| 283 | |
| 284 // from here down we are concerned with colortables and pixels | |
| 285 | |
| 286 // we track if we actually see a non-opaque pixels, since sometimes a PNG se
ts its colortype | |
| 287 // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We
care, since we | |
| 288 // draw lots faster if we can flag the bitmap has being opaque | |
| 289 bool reallyHasAlpha = false; | |
| 290 | |
| 291 SkColorTable* colorTable = NULL; | |
| 292 | |
| 293 if (color_type == PNG_COLOR_TYPE_PALETTE) { | |
| 294 int num_palette; | |
| 295 png_colorp palette; | |
| 296 png_bytep trans; | |
| 297 int num_trans; | |
| 298 | |
| 299 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); | |
| 300 | |
| 301 /* BUGGY IMAGE WORKAROUND | |
| 302 | |
| 303 We hit some images (e.g. fruit_.png) who contain bytes that are == c
olortable_count | |
| 304 which is a problem since we use the byte as an index. To work around
this we grow | |
| 305 the colortable by 1 (if its < 256) and duplicate the last color into
that slot. | |
| 306 */ | |
| 307 int colorCount = num_palette + (num_palette < 256); | |
| 308 | |
| 309 colorTable = SkNEW_ARGS(SkColorTable, (colorCount)); | |
| 310 | |
| 311 SkPMColor* colorPtr = colorTable->lockColors(); | |
| 312 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | |
| 313 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL); | |
| 314 hasAlpha = (num_trans > 0); | |
| 315 } else { | |
| 316 num_trans = 0; | |
| 317 colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsA
reOpaque_Flag); | |
| 318 } | |
| 319 // check for bad images that might make us crash | |
| 320 if (num_trans > num_palette) { | |
| 321 num_trans = num_palette; | |
| 322 } | |
| 323 | |
| 324 int index = 0; | |
| 325 int transLessThanFF = 0; | |
| 326 | |
| 327 for (; index < num_trans; index++) { | |
| 328 transLessThanFF |= (int)*trans - 0xFF; | |
| 329 *colorPtr++ = SkPreMultiplyARGB(*trans++, palette->red, palette->gre
en, palette->blue); | |
| 330 palette++; | |
| 331 } | |
| 332 reallyHasAlpha |= (transLessThanFF < 0); | |
| 333 | |
| 334 for (; index < num_palette; index++) { | |
| 335 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palet
te->blue); | |
| 336 palette++; | |
| 337 } | |
| 338 | |
| 339 // see BUGGY IMAGE WORKAROUND comment above | |
| 340 if (num_palette < 256) { | |
| 341 *colorPtr = colorPtr[-1]; | |
| 342 } | |
| 343 colorTable->unlockColors(true); | |
| 344 } | |
| 345 | |
| 346 SkAutoUnref aur(colorTable); | |
| 347 | |
| 348 if (!this->allocPixelRef(decodedBitmap, colorTable)) { | |
| 349 return false; | |
| 350 } | |
| 351 | |
| 352 SkAutoLockPixels alp(*decodedBitmap); | |
| 353 | |
| 354 /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ | |
| 355 // if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) | |
| 356 // ; // png_set_swap_alpha(png_ptr); | |
| 357 | |
| 358 /* swap bytes of 16 bit files to least significant byte first */ | |
| 359 // png_set_swap(png_ptr); | |
| 360 | |
| 361 /* Add filler (or alpha) byte (before/after each RGB triplet) */ | |
| 362 if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY) { | |
| 363 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); | |
| 364 } | |
| 365 | |
| 366 /* Turn on interlace handling. REQUIRED if you are not using | |
| 367 * png_read_image(). To see how to handle interlacing passes, | |
| 368 * see the png_read_row() method below: | |
| 369 */ | |
| 370 const int number_passes = interlace_type != PNG_INTERLACE_NONE ? | |
| 371 png_set_interlace_handling(png_ptr) : 1; | |
| 372 | |
| 373 /* Optional call to gamma correct and add the background to the palette | |
| 374 * and update info structure. REQUIRED if you are expecting libpng to | |
| 375 * update the palette for you (ie you selected such a transform above). | |
| 376 */ | |
| 377 png_read_update_info(png_ptr, info_ptr); | |
| 378 | |
| 379 if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) { | |
| 380 for (int i = 0; i < number_passes; i++) { | |
| 381 for (png_uint_32 y = 0; y < origHeight; y++) { | |
| 382 uint8_t* bmRow = decodedBitmap->getAddr8(0, y); | |
| 383 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); | |
| 384 } | |
| 385 } | |
| 386 } else { | |
| 387 SkScaledBitmapSampler::SrcConfig sc; | |
| 388 int srcBytesPerPixel = 4; | |
| 389 | |
| 390 if (SkBitmap::kIndex8_Config == config) { | |
| 391 sc = SkScaledBitmapSampler::kIndex; | |
| 392 srcBytesPerPixel = 1; | |
| 393 } else if (hasAlpha) { | |
| 394 sc = SkScaledBitmapSampler::kRGBA; | |
| 395 } else { | |
| 396 sc = SkScaledBitmapSampler::kRGBX; | |
| 397 } | |
| 398 | |
| 399 SkAutoMalloc storage(origWidth * srcBytesPerPixel); | |
| 400 const int height = decodedBitmap->height(); | |
| 401 | |
| 402 for (int i = 0; i < number_passes; i++) { | |
| 403 if (!sampler.begin(decodedBitmap, sc, doDither)) { | |
| 404 return false; | |
| 405 } | |
| 406 | |
| 407 uint8_t* srcRow = (uint8_t*)storage.get(); | |
| 408 skip_src_rows(png_ptr, srcRow, sampler.srcY0()); | |
| 409 | |
| 410 for (int y = 0; y < height; y++) { | |
| 411 uint8_t* tmp = srcRow; | |
| 412 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); | |
| 413 reallyHasAlpha |= sampler.next(srcRow); | |
| 414 if (y < height - 1) { | |
| 415 skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1); | |
| 416 } | |
| 417 } | |
| 418 | |
| 419 // skip the rest of the rows (if any) | |
| 420 png_uint_32 read = (height - 1) * sampler.srcDY() + | |
| 421 sampler.srcY0() + 1; | |
| 422 SkASSERT(read <= origHeight); | |
| 423 skip_src_rows(png_ptr, srcRow, origHeight - read); | |
| 424 } | |
| 425 | |
| 426 if (hasAlpha && !reallyHasAlpha) { | |
| 427 #if 0 | |
| 428 SkDEBUGF(("Image doesn't really have alpha [%d %d]\n", | |
| 429 origWidth, origHeight)); | |
| 430 #endif | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ | |
| 435 png_read_end(png_ptr, info_ptr); | |
| 436 | |
| 437 if (0 != theTranspColor) { | |
| 438 reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor); | |
| 439 } | |
| 440 decodedBitmap->setIsOpaque(!reallyHasAlpha); | |
| 441 return true; | |
| 442 } | |
| 443 | |
| 444 /////////////////////////////////////////////////////////////////////////////// | |
| 445 | |
| 446 #ifdef SK_SUPPORT_IMAGE_ENCODE | |
| 447 | |
| 448 #include "SkColorPriv.h" | |
| 449 #include "SkUnPreMultiply.h" | |
| 450 | |
| 451 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { | |
| 452 SkWStream* sk_stream = (SkWStream*)png_ptr->io_ptr; | |
| 453 if (!sk_stream->write(data, len)) { | |
| 454 png_error(png_ptr, "sk_write_fn Error!"); | |
| 455 } | |
| 456 } | |
| 457 | |
| 458 typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src, | |
| 459 int width, char* SK_RESTRICT dst); | |
| 460 | |
| 461 static void transform_scanline_565(const char* SK_RESTRICT src, int width, | |
| 462 char* SK_RESTRICT dst) { | |
| 463 const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src; | |
| 464 for (int i = 0; i < width; i++) { | |
| 465 unsigned c = *srcP++; | |
| 466 *dst++ = SkPacked16ToR32(c); | |
| 467 *dst++ = SkPacked16ToG32(c); | |
| 468 *dst++ = SkPacked16ToB32(c); | |
| 469 } | |
| 470 } | |
| 471 | |
| 472 static void transform_scanline_888(const char* SK_RESTRICT src, int width, | |
| 473 char* SK_RESTRICT dst) { | |
| 474 const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src; | |
| 475 for (int i = 0; i < width; i++) { | |
| 476 SkPMColor c = *srcP++; | |
| 477 *dst++ = SkGetPackedR32(c); | |
| 478 *dst++ = SkGetPackedG32(c); | |
| 479 *dst++ = SkGetPackedB32(c); | |
| 480 } | |
| 481 } | |
| 482 | |
| 483 static void transform_scanline_444(const char* SK_RESTRICT src, int width, | |
| 484 char* SK_RESTRICT dst) { | |
| 485 const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src; | |
| 486 for (int i = 0; i < width; i++) { | |
| 487 SkPMColor16 c = *srcP++; | |
| 488 *dst++ = SkPacked4444ToR32(c); | |
| 489 *dst++ = SkPacked4444ToG32(c); | |
| 490 *dst++ = SkPacked4444ToB32(c); | |
| 491 } | |
| 492 } | |
| 493 | |
| 494 static void transform_scanline_8888(const char* SK_RESTRICT src, int width, | |
| 495 char* SK_RESTRICT dst) { | |
| 496 const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src; | |
| 497 const SkUnPreMultiply::Scale* SK_RESTRICT table = | |
| 498 SkUnPreMultiply::GetScaleTable(); | |
| 499 | |
| 500 for (int i = 0; i < width; i++) { | |
| 501 SkPMColor c = *srcP++; | |
| 502 unsigned a = SkGetPackedA32(c); | |
| 503 unsigned r = SkGetPackedR32(c); | |
| 504 unsigned g = SkGetPackedG32(c); | |
| 505 unsigned b = SkGetPackedB32(c); | |
| 506 | |
| 507 if (0 != a && 255 != a) { | |
| 508 SkUnPreMultiply::Scale scale = table[a]; | |
| 509 r = SkUnPreMultiply::ApplyScale(scale, r); | |
| 510 g = SkUnPreMultiply::ApplyScale(scale, g); | |
| 511 b = SkUnPreMultiply::ApplyScale(scale, b); | |
| 512 } | |
| 513 *dst++ = r; | |
| 514 *dst++ = g; | |
| 515 *dst++ = b; | |
| 516 *dst++ = a; | |
| 517 } | |
| 518 } | |
| 519 | |
| 520 static void transform_scanline_4444(const char* SK_RESTRICT src, int width, | |
| 521 char* SK_RESTRICT dst) { | |
| 522 const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src; | |
| 523 const SkUnPreMultiply::Scale* SK_RESTRICT table = | |
| 524 SkUnPreMultiply::GetScaleTable(); | |
| 525 | |
| 526 for (int i = 0; i < width; i++) { | |
| 527 SkPMColor16 c = *srcP++; | |
| 528 unsigned a = SkPacked4444ToA32(c); | |
| 529 unsigned r = SkPacked4444ToR32(c); | |
| 530 unsigned g = SkPacked4444ToG32(c); | |
| 531 unsigned b = SkPacked4444ToB32(c); | |
| 532 | |
| 533 if (0 != a && 255 != a) { | |
| 534 SkUnPreMultiply::Scale scale = table[a]; | |
| 535 r = SkUnPreMultiply::ApplyScale(scale, r); | |
| 536 g = SkUnPreMultiply::ApplyScale(scale, g); | |
| 537 b = SkUnPreMultiply::ApplyScale(scale, b); | |
| 538 } | |
| 539 *dst++ = r; | |
| 540 *dst++ = g; | |
| 541 *dst++ = b; | |
| 542 *dst++ = a; | |
| 543 } | |
| 544 } | |
| 545 | |
| 546 static void transform_scanline_index8(const char* SK_RESTRICT src, int width, | |
| 547 char* SK_RESTRICT dst) { | |
| 548 memcpy(dst, src, width); | |
| 549 } | |
| 550 | |
| 551 static transform_scanline_proc choose_proc(SkBitmap::Config config, | |
| 552 bool hasAlpha) { | |
| 553 // we don't care about search on alpha if we're kIndex8, since only the | |
| 554 // colortable packing cares about that distinction, not the pixels | |
| 555 if (SkBitmap::kIndex8_Config == config) { | |
| 556 hasAlpha = false; // we store false in the table entries for kIndex8 | |
| 557 } | |
| 558 | |
| 559 static const struct { | |
| 560 SkBitmap::Config fConfig; | |
| 561 bool fHasAlpha; | |
| 562 transform_scanline_proc fProc; | |
| 563 } gMap[] = { | |
| 564 { SkBitmap::kRGB_565_Config, false, transform_scanline_565 }, | |
| 565 { SkBitmap::kARGB_8888_Config, false, transform_scanline_888 }, | |
| 566 { SkBitmap::kARGB_8888_Config, true, transform_scanline_8888 }, | |
| 567 { SkBitmap::kARGB_4444_Config, false, transform_scanline_444 }, | |
| 568 { SkBitmap::kARGB_4444_Config, true, transform_scanline_4444 }, | |
| 569 { SkBitmap::kIndex8_Config, false, transform_scanline_index8 }, | |
| 570 }; | |
| 571 | |
| 572 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) { | |
| 573 if (gMap[i].fConfig == config && gMap[i].fHasAlpha == hasAlpha) { | |
| 574 return gMap[i].fProc; | |
| 575 } | |
| 576 } | |
| 577 sk_throw(); | |
| 578 return NULL; | |
| 579 } | |
| 580 | |
| 581 // return the minimum legal bitdepth (by png standards) for this many colortable | |
| 582 // entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16, | |
| 583 // we can use fewer bits per in png | |
| 584 static int computeBitDepth(int colorCount) { | |
| 585 #if 0 | |
| 586 int bits = SkNextLog2(colorCount); | |
| 587 SkASSERT(bits >= 1 && bits <= 8); | |
| 588 // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8) | |
| 589 return SkNextPow2(bits); | |
| 590 #else | |
| 591 // for the moment, we don't know how to pack bitdepth < 8 | |
| 592 return 8; | |
| 593 #endif | |
| 594 } | |
| 595 | |
| 596 /* Pack palette[] with the corresponding colors, and if hasAlpha is true, also | |
| 597 pack trans[] and return the number of trans[] entries written. If hasAlpha | |
| 598 is false, the return value will always be 0. | |
| 599 | |
| 600 Note: this routine takes care of unpremultiplying the RGB values when we | |
| 601 have alpha in the colortable, since png doesn't support premul colors | |
| 602 */ | |
| 603 static int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette, | |
| 604 png_byte* SK_RESTRICT trans, bool hasAlpha) { | |
| 605 SkAutoLockColors alc(ctable); | |
| 606 const SkPMColor* SK_RESTRICT colors = alc.colors(); | |
| 607 const int ctCount = ctable->count(); | |
| 608 int i, num_trans = 0; | |
| 609 | |
| 610 if (hasAlpha) { | |
| 611 /* first see if we have some number of fully opaque at the end of the | |
| 612 ctable. PNG allows num_trans < num_palette, but all of the trans | |
| 613 entries must come first in the palette. If I was smarter, I'd | |
| 614 reorder the indices and ctable so that all non-opaque colors came | |
| 615 first in the palette. But, since that would slow down the encode, | |
| 616 I'm leaving the indices and ctable order as is, and just looking | |
| 617 at the tail of the ctable for opaqueness. | |
| 618 */ | |
| 619 num_trans = ctCount; | |
| 620 for (i = ctCount - 1; i >= 0; --i) { | |
| 621 if (SkGetPackedA32(colors[i]) != 0xFF) { | |
| 622 break; | |
| 623 } | |
| 624 num_trans -= 1; | |
| 625 } | |
| 626 | |
| 627 const SkUnPreMultiply::Scale* SK_RESTRICT table = | |
| 628 SkUnPreMultiply::GetScaleTable(); | |
| 629 | |
| 630 for (i = 0; i < num_trans; i++) { | |
| 631 const SkPMColor c = *colors++; | |
| 632 const unsigned a = SkGetPackedA32(c); | |
| 633 const SkUnPreMultiply::Scale s = table[a]; | |
| 634 trans[i] = a; | |
| 635 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c)); | |
| 636 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c)); | |
| 637 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c)); | |
| 638 } | |
| 639 // now fall out of this if-block to use common code for the trailing | |
| 640 // opaque entries | |
| 641 } | |
| 642 | |
| 643 // these (remaining) entries are opaque | |
| 644 for (i = num_trans; i < ctCount; i++) { | |
| 645 SkPMColor c = *colors++; | |
| 646 palette[i].red = SkGetPackedR32(c); | |
| 647 palette[i].green = SkGetPackedG32(c); | |
| 648 palette[i].blue = SkGetPackedB32(c); | |
| 649 } | |
| 650 return num_trans; | |
| 651 } | |
| 652 | |
| 653 class SkPNGImageEncoder : public SkImageEncoder { | |
| 654 protected: | |
| 655 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); | |
| 656 }; | |
| 657 | |
| 658 bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, | |
| 659 int /*quality*/) { | |
| 660 SkBitmap::Config config = bitmap.getConfig(); | |
| 661 | |
| 662 const bool hasAlpha = !bitmap.isOpaque(); | |
| 663 int colorType = PNG_COLOR_MASK_COLOR; | |
| 664 int bitDepth = 8; // default for color | |
| 665 png_color_8 sig_bit; | |
| 666 | |
| 667 switch (config) { | |
| 668 case SkBitmap::kIndex8_Config: | |
| 669 colorType |= PNG_COLOR_MASK_PALETTE; | |
| 670 // fall through to the ARGB_8888 case | |
| 671 case SkBitmap::kARGB_8888_Config: | |
| 672 sig_bit.red = 8; | |
| 673 sig_bit.green = 8; | |
| 674 sig_bit.blue = 8; | |
| 675 sig_bit.alpha = 8; | |
| 676 break; | |
| 677 case SkBitmap::kARGB_4444_Config: | |
| 678 sig_bit.red = 4; | |
| 679 sig_bit.green = 4; | |
| 680 sig_bit.blue = 4; | |
| 681 sig_bit.alpha = 4; | |
| 682 break; | |
| 683 case SkBitmap::kRGB_565_Config: | |
| 684 sig_bit.red = 5; | |
| 685 sig_bit.green = 6; | |
| 686 sig_bit.blue = 5; | |
| 687 sig_bit.alpha = 0; | |
| 688 break; | |
| 689 default: | |
| 690 return false; | |
| 691 } | |
| 692 | |
| 693 if (hasAlpha) { | |
| 694 // don't specify alpha if we're a palette, even if our ctable has alpha | |
| 695 if (!(colorType & PNG_COLOR_MASK_PALETTE)) { | |
| 696 colorType |= PNG_COLOR_MASK_ALPHA; | |
| 697 } | |
| 698 } else { | |
| 699 sig_bit.alpha = 0; | |
| 700 } | |
| 701 | |
| 702 SkAutoLockPixels alp(bitmap); | |
| 703 // readyToDraw checks for pixels (and colortable if that is required) | |
| 704 if (!bitmap.readyToDraw()) { | |
| 705 return false; | |
| 706 } | |
| 707 | |
| 708 // we must do this after we have locked the pixels | |
| 709 SkColorTable* ctable = bitmap.getColorTable(); | |
| 710 if (NULL != ctable) { | |
| 711 if (ctable->count() == 0) { | |
| 712 return false; | |
| 713 } | |
| 714 // check if we can store in fewer than 8 bits | |
| 715 bitDepth = computeBitDepth(ctable->count()); | |
| 716 } | |
| 717 | |
| 718 png_structp png_ptr; | |
| 719 png_infop info_ptr; | |
| 720 | |
| 721 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn, | |
| 722 NULL); | |
| 723 if (NULL == png_ptr) { | |
| 724 return false; | |
| 725 } | |
| 726 | |
| 727 info_ptr = png_create_info_struct(png_ptr); | |
| 728 if (NULL == info_ptr) { | |
| 729 png_destroy_write_struct(&png_ptr, png_infopp_NULL); | |
| 730 return false; | |
| 731 } | |
| 732 | |
| 733 /* Set error handling. REQUIRED if you aren't supplying your own | |
| 734 * error handling functions in the png_create_write_struct() call. | |
| 735 */ | |
| 736 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 737 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 738 return false; | |
| 739 } | |
| 740 | |
| 741 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL); | |
| 742 | |
| 743 /* Set the image information here. Width and height are up to 2^31, | |
| 744 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on | |
| 745 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, | |
| 746 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, | |
| 747 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or | |
| 748 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST | |
| 749 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED | |
| 750 */ | |
| 751 | |
| 752 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(), | |
| 753 bitDepth, colorType, | |
| 754 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, | |
| 755 PNG_FILTER_TYPE_BASE); | |
| 756 | |
| 757 #if 0 // need to support this some day | |
| 758 /* set the palette if there is one. REQUIRED for indexed-color images */ | |
| 759 palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH | |
| 760 * png_sizeof (png_color)); | |
| 761 /* ... set palette colors ... */ | |
| 762 png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); | |
| 763 /* You must not free palette here, because png_set_PLTE only makes a link to | |
| 764 the palette that you malloced. Wait until you are about to destroy | |
| 765 the png structure. */ | |
| 766 #endif | |
| 767 | |
| 768 png_set_sBIT(png_ptr, info_ptr, &sig_bit); | |
| 769 png_write_info(png_ptr, info_ptr); | |
| 770 | |
| 771 const char* srcImage = (const char*)bitmap.getPixels(); | |
| 772 SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2); | |
| 773 char* storage = (char*)rowStorage.get(); | |
| 774 transform_scanline_proc proc = choose_proc(config, hasAlpha); | |
| 775 | |
| 776 for (int y = 0; y < bitmap.height(); y++) { | |
| 777 png_bytep row_ptr = (png_bytep)storage; | |
| 778 proc(srcImage, bitmap.width(), storage); | |
| 779 png_write_rows(png_ptr, &row_ptr, 1); | |
| 780 srcImage += bitmap.rowBytes(); | |
| 781 } | |
| 782 | |
| 783 png_write_end(png_ptr, info_ptr); | |
| 784 | |
| 785 /* clean up after the write, and free any memory allocated */ | |
| 786 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 787 return true; | |
| 788 } | |
| 789 | |
| 790 SkImageEncoder* SkImageEncoder_PNG_Factory(); | |
| 791 SkImageEncoder* SkImageEncoder_PNG_Factory() { | |
| 792 return SkNEW(SkPNGImageEncoder); | |
| 793 } | |
| 794 | |
| 795 #endif /* SK_SUPPORT_IMAGE_ENCODE */ | |
| OLD | NEW |