| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2006 The Android Open Source Project | |
| 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 "SkImageEncoder.h" | |
| 9 #include "SkColor.h" | |
| 10 #include "SkColorPriv.h" | |
| 11 #include "SkDither.h" | |
| 12 #include "SkMath.h" | |
| 13 #include "SkRTConf.h" | |
| 14 #include "SkStream.h" | |
| 15 #include "SkTemplates.h" | |
| 16 #include "SkUtils.h" | |
| 17 #include "transform_scanline.h" | |
| 18 | |
| 19 #include "png.h" | |
| 20 | |
| 21 /* These were dropped in libpng >= 1.4 */ | |
| 22 #ifndef png_infopp_NULL | |
| 23 #define png_infopp_NULL nullptr | |
| 24 #endif | |
| 25 | |
| 26 #ifndef png_bytepp_NULL | |
| 27 #define png_bytepp_NULL nullptr | |
| 28 #endif | |
| 29 | |
| 30 #ifndef int_p_NULL | |
| 31 #define int_p_NULL nullptr | |
| 32 #endif | |
| 33 | |
| 34 #ifndef png_flush_ptr_NULL | |
| 35 #define png_flush_ptr_NULL nullptr | |
| 36 #endif | |
| 37 | |
| 38 #define DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS true | |
| 39 SK_CONF_DECLARE(bool, c_suppressPNGImageDecoderWarnings, | |
| 40 "images.png.suppressDecoderWarnings", | |
| 41 DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS, | |
| 42 "Suppress most PNG warnings when calling image decode " | |
| 43 "functions."); | |
| 44 | |
| 45 /////////////////////////////////////////////////////////////////////////////// | |
| 46 | |
| 47 #include "SkColorPriv.h" | |
| 48 #include "SkUnPreMultiply.h" | |
| 49 | |
| 50 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { | |
| 51 if (!c_suppressPNGImageDecoderWarnings) { | |
| 52 SkDEBUGF(("------ png error %s\n", msg)); | |
| 53 } | |
| 54 longjmp(png_jmpbuf(png_ptr), 1); | |
| 55 } | |
| 56 | |
| 57 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { | |
| 58 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr); | |
| 59 if (!sk_stream->write(data, len)) { | |
| 60 png_error(png_ptr, "sk_write_fn Error!"); | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 static transform_scanline_proc choose_proc(SkColorType ct, bool hasAlpha) { | |
| 65 // we don't care about search on alpha if we're kIndex8, since only the | |
| 66 // colortable packing cares about that distinction, not the pixels | |
| 67 if (kIndex_8_SkColorType == ct) { | |
| 68 hasAlpha = false; // we store false in the table entries for kIndex8 | |
| 69 } | |
| 70 | |
| 71 static const struct { | |
| 72 SkColorType fColorType; | |
| 73 bool fHasAlpha; | |
| 74 transform_scanline_proc fProc; | |
| 75 } gMap[] = { | |
| 76 { kRGB_565_SkColorType, false, transform_scanline_565 }, | |
| 77 { kN32_SkColorType, false, transform_scanline_888 }, | |
| 78 { kN32_SkColorType, true, transform_scanline_8888 }, | |
| 79 { kARGB_4444_SkColorType, false, transform_scanline_444 }, | |
| 80 { kARGB_4444_SkColorType, true, transform_scanline_4444 }, | |
| 81 { kIndex_8_SkColorType, false, transform_scanline_memcpy }, | |
| 82 }; | |
| 83 | |
| 84 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) { | |
| 85 if (gMap[i].fColorType == ct && gMap[i].fHasAlpha == hasAlpha) { | |
| 86 return gMap[i].fProc; | |
| 87 } | |
| 88 } | |
| 89 sk_throw(); | |
| 90 return nullptr; | |
| 91 } | |
| 92 | |
| 93 // return the minimum legal bitdepth (by png standards) for this many colortable | |
| 94 // entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16, | |
| 95 // we can use fewer bits per in png | |
| 96 static int computeBitDepth(int colorCount) { | |
| 97 #if 0 | |
| 98 int bits = SkNextLog2(colorCount); | |
| 99 SkASSERT(bits >= 1 && bits <= 8); | |
| 100 // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8) | |
| 101 return SkNextPow2(bits); | |
| 102 #else | |
| 103 // for the moment, we don't know how to pack bitdepth < 8 | |
| 104 return 8; | |
| 105 #endif | |
| 106 } | |
| 107 | |
| 108 /* Pack palette[] with the corresponding colors, and if hasAlpha is true, also | |
| 109 pack trans[] and return the number of trans[] entries written. If hasAlpha | |
| 110 is false, the return value will always be 0. | |
| 111 | |
| 112 Note: this routine takes care of unpremultiplying the RGB values when we | |
| 113 have alpha in the colortable, since png doesn't support premul colors | |
| 114 */ | |
| 115 static inline int pack_palette(SkColorTable* ctable, | |
| 116 png_color* SK_RESTRICT palette, | |
| 117 png_byte* SK_RESTRICT trans, bool hasAlpha) { | |
| 118 const SkPMColor* SK_RESTRICT colors = ctable ? ctable->readColors() : nullpt
r; | |
| 119 const int ctCount = ctable->count(); | |
| 120 int i, num_trans = 0; | |
| 121 | |
| 122 if (hasAlpha) { | |
| 123 /* first see if we have some number of fully opaque at the end of the | |
| 124 ctable. PNG allows num_trans < num_palette, but all of the trans | |
| 125 entries must come first in the palette. If I was smarter, I'd | |
| 126 reorder the indices and ctable so that all non-opaque colors came | |
| 127 first in the palette. But, since that would slow down the encode, | |
| 128 I'm leaving the indices and ctable order as is, and just looking | |
| 129 at the tail of the ctable for opaqueness. | |
| 130 */ | |
| 131 num_trans = ctCount; | |
| 132 for (i = ctCount - 1; i >= 0; --i) { | |
| 133 if (SkGetPackedA32(colors[i]) != 0xFF) { | |
| 134 break; | |
| 135 } | |
| 136 num_trans -= 1; | |
| 137 } | |
| 138 | |
| 139 const SkUnPreMultiply::Scale* SK_RESTRICT table = | |
| 140 SkUnPreMultiply::GetScaleTable(); | |
| 141 | |
| 142 for (i = 0; i < num_trans; i++) { | |
| 143 const SkPMColor c = *colors++; | |
| 144 const unsigned a = SkGetPackedA32(c); | |
| 145 const SkUnPreMultiply::Scale s = table[a]; | |
| 146 trans[i] = a; | |
| 147 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c)); | |
| 148 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c)); | |
| 149 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c)); | |
| 150 } | |
| 151 // now fall out of this if-block to use common code for the trailing | |
| 152 // opaque entries | |
| 153 } | |
| 154 | |
| 155 // these (remaining) entries are opaque | |
| 156 for (i = num_trans; i < ctCount; i++) { | |
| 157 SkPMColor c = *colors++; | |
| 158 palette[i].red = SkGetPackedR32(c); | |
| 159 palette[i].green = SkGetPackedG32(c); | |
| 160 palette[i].blue = SkGetPackedB32(c); | |
| 161 } | |
| 162 return num_trans; | |
| 163 } | |
| 164 | |
| 165 class SkPNGImageEncoder : public SkImageEncoder { | |
| 166 protected: | |
| 167 bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override; | |
| 168 private: | |
| 169 bool doEncode(SkWStream* stream, const SkBitmap& bm, | |
| 170 const bool& hasAlpha, int colorType, | |
| 171 int bitDepth, SkColorType ct, | |
| 172 png_color_8& sig_bit); | |
| 173 | |
| 174 typedef SkImageEncoder INHERITED; | |
| 175 }; | |
| 176 | |
| 177 bool SkPNGImageEncoder::onEncode(SkWStream* stream, | |
| 178 const SkBitmap& originalBitmap, | |
| 179 int /*quality*/) { | |
| 180 SkBitmap copy; | |
| 181 const SkBitmap* bitmap = &originalBitmap; | |
| 182 switch (originalBitmap.colorType()) { | |
| 183 case kIndex_8_SkColorType: | |
| 184 case kN32_SkColorType: | |
| 185 case kARGB_4444_SkColorType: | |
| 186 case kRGB_565_SkColorType: | |
| 187 break; | |
| 188 default: | |
| 189 // TODO(scroggo): support 8888-but-not-N32 natively. | |
| 190 // TODO(scroggo): support kGray_8 directly. | |
| 191 // TODO(scroggo): support Alpha_8 as Grayscale(black)+Alpha | |
| 192 if (originalBitmap.copyTo(©, kN32_SkColorType)) { | |
| 193 bitmap = © | |
| 194 } | |
| 195 } | |
| 196 SkColorType ct = bitmap->colorType(); | |
| 197 | |
| 198 const bool hasAlpha = !bitmap->isOpaque(); | |
| 199 int colorType = PNG_COLOR_MASK_COLOR; | |
| 200 int bitDepth = 8; // default for color | |
| 201 png_color_8 sig_bit; | |
| 202 | |
| 203 switch (ct) { | |
| 204 case kIndex_8_SkColorType: | |
| 205 colorType |= PNG_COLOR_MASK_PALETTE; | |
| 206 // fall through to the ARGB_8888 case | |
| 207 case kN32_SkColorType: | |
| 208 sig_bit.red = 8; | |
| 209 sig_bit.green = 8; | |
| 210 sig_bit.blue = 8; | |
| 211 sig_bit.alpha = 8; | |
| 212 break; | |
| 213 case kARGB_4444_SkColorType: | |
| 214 sig_bit.red = 4; | |
| 215 sig_bit.green = 4; | |
| 216 sig_bit.blue = 4; | |
| 217 sig_bit.alpha = 4; | |
| 218 break; | |
| 219 case kRGB_565_SkColorType: | |
| 220 sig_bit.red = 5; | |
| 221 sig_bit.green = 6; | |
| 222 sig_bit.blue = 5; | |
| 223 sig_bit.alpha = 0; | |
| 224 break; | |
| 225 default: | |
| 226 return false; | |
| 227 } | |
| 228 | |
| 229 if (hasAlpha) { | |
| 230 // don't specify alpha if we're a palette, even if our ctable has alpha | |
| 231 if (!(colorType & PNG_COLOR_MASK_PALETTE)) { | |
| 232 colorType |= PNG_COLOR_MASK_ALPHA; | |
| 233 } | |
| 234 } else { | |
| 235 sig_bit.alpha = 0; | |
| 236 } | |
| 237 | |
| 238 SkAutoLockPixels alp(*bitmap); | |
| 239 // readyToDraw checks for pixels (and colortable if that is required) | |
| 240 if (!bitmap->readyToDraw()) { | |
| 241 return false; | |
| 242 } | |
| 243 | |
| 244 // we must do this after we have locked the pixels | |
| 245 SkColorTable* ctable = bitmap->getColorTable(); | |
| 246 if (ctable) { | |
| 247 if (ctable->count() == 0) { | |
| 248 return false; | |
| 249 } | |
| 250 // check if we can store in fewer than 8 bits | |
| 251 bitDepth = computeBitDepth(ctable->count()); | |
| 252 } | |
| 253 | |
| 254 return doEncode(stream, *bitmap, hasAlpha, colorType, bitDepth, ct, sig_bit)
; | |
| 255 } | |
| 256 | |
| 257 bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap, | |
| 258 const bool& hasAlpha, int colorType, | |
| 259 int bitDepth, SkColorType ct, | |
| 260 png_color_8& sig_bit) { | |
| 261 | |
| 262 png_structp png_ptr; | |
| 263 png_infop info_ptr; | |
| 264 | |
| 265 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_f
n, | |
| 266 nullptr); | |
| 267 if (nullptr == png_ptr) { | |
| 268 return false; | |
| 269 } | |
| 270 | |
| 271 info_ptr = png_create_info_struct(png_ptr); | |
| 272 if (nullptr == info_ptr) { | |
| 273 png_destroy_write_struct(&png_ptr, png_infopp_NULL); | |
| 274 return false; | |
| 275 } | |
| 276 | |
| 277 /* Set error handling. REQUIRED if you aren't supplying your own | |
| 278 * error handling functions in the png_create_write_struct() call. | |
| 279 */ | |
| 280 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 281 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 282 return false; | |
| 283 } | |
| 284 | |
| 285 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL); | |
| 286 | |
| 287 /* Set the image information here. Width and height are up to 2^31, | |
| 288 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on | |
| 289 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, | |
| 290 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, | |
| 291 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or | |
| 292 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST | |
| 293 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED | |
| 294 */ | |
| 295 | |
| 296 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(), | |
| 297 bitDepth, colorType, | |
| 298 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, | |
| 299 PNG_FILTER_TYPE_BASE); | |
| 300 | |
| 301 // set our colortable/trans arrays if needed | |
| 302 png_color paletteColors[256]; | |
| 303 png_byte trans[256]; | |
| 304 if (kIndex_8_SkColorType == ct) { | |
| 305 SkColorTable* ct = bitmap.getColorTable(); | |
| 306 int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha); | |
| 307 png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count()); | |
| 308 if (numTrans > 0) { | |
| 309 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, nullptr); | |
| 310 } | |
| 311 } | |
| 312 #ifdef PNG_sBIT_SUPPORTED | |
| 313 png_set_sBIT(png_ptr, info_ptr, &sig_bit); | |
| 314 #endif | |
| 315 png_write_info(png_ptr, info_ptr); | |
| 316 | |
| 317 const char* srcImage = (const char*)bitmap.getPixels(); | |
| 318 SkAutoSTMalloc<1024, char> rowStorage(bitmap.width() << 2); | |
| 319 char* storage = rowStorage.get(); | |
| 320 transform_scanline_proc proc = choose_proc(ct, hasAlpha); | |
| 321 | |
| 322 for (int y = 0; y < bitmap.height(); y++) { | |
| 323 png_bytep row_ptr = (png_bytep)storage; | |
| 324 proc(srcImage, bitmap.width(), storage); | |
| 325 png_write_rows(png_ptr, &row_ptr, 1); | |
| 326 srcImage += bitmap.rowBytes(); | |
| 327 } | |
| 328 | |
| 329 png_write_end(png_ptr, info_ptr); | |
| 330 | |
| 331 /* clean up after the write, and free any memory allocated */ | |
| 332 png_destroy_write_struct(&png_ptr, &info_ptr); | |
| 333 return true; | |
| 334 } | |
| 335 | |
| 336 /////////////////////////////////////////////////////////////////////////////// | |
| 337 DEFINE_ENCODER_CREATOR(PNGImageEncoder); | |
| 338 /////////////////////////////////////////////////////////////////////////////// | |
| 339 | |
| 340 SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) { | |
| 341 return (SkImageEncoder::kPNG_Type == t) ? new SkPNGImageEncoder : nullptr; | |
| 342 } | |
| 343 | |
| 344 static SkImageEncoder_EncodeReg gEReg(sk_libpng_efactory); | |
| OLD | NEW |