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

Side by Side Diff: src/images/SkPNGImageEncoder.cpp

Issue 2325223002: Support RGBA/BGRA Premul/Unpremul from SkPNGImageEncoder (Closed)
Patch Set: Rebase Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | src/images/transform_scanline.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2006 The Android Open Source Project 2 * Copyright 2006 The Android Open Source Project
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkImageEncoder.h" 8 #include "SkImageEncoder.h"
9 #include "SkColor.h" 9 #include "SkColor.h"
10 #include "SkColorPriv.h" 10 #include "SkColorPriv.h"
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
51 longjmp(png_jmpbuf(png_ptr), 1); 51 longjmp(png_jmpbuf(png_ptr), 1);
52 } 52 }
53 53
54 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { 54 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
55 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr); 55 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
56 if (!sk_stream->write(data, len)) { 56 if (!sk_stream->write(data, len)) {
57 png_error(png_ptr, "sk_write_fn Error!"); 57 png_error(png_ptr, "sk_write_fn Error!");
58 } 58 }
59 } 59 }
60 60
61 static transform_scanline_proc choose_proc(SkColorType ct, bool hasAlpha) { 61 static transform_scanline_proc choose_proc(SkColorType ct, SkAlphaType alphaType ) {
62 // we don't care about search on alpha if we're kIndex8, since only the
63 // colortable packing cares about that distinction, not the pixels
64 if (kIndex_8_SkColorType == ct) {
65 hasAlpha = false; // we store false in the table entries for kIndex8
66 }
67
68 static const struct { 62 static const struct {
69 SkColorType fColorType; 63 SkColorType fColorType;
70 bool fHasAlpha; 64 SkAlphaType fAlphaType;
71 transform_scanline_proc fProc; 65 transform_scanline_proc fProc;
72 } gMap[] = { 66 } gMap[] = {
73 { kRGB_565_SkColorType, false, transform_scanline_565 }, 67 { kRGB_565_SkColorType, kOpaque_SkAlphaType, transform_scanline_565 },
74 { kN32_SkColorType, false, transform_scanline_888 }, 68 { kRGBA_8888_SkColorType, kOpaque_SkAlphaType, transform_scanline_RGBX },
75 { kN32_SkColorType, true, transform_scanline_8888 }, 69 { kBGRA_8888_SkColorType, kOpaque_SkAlphaType, transform_scanline_BGRX },
76 { kARGB_4444_SkColorType, false, transform_scanline_444 }, 70 { kRGBA_8888_SkColorType, kPremul_SkAlphaType, transform_scanline_rgbA },
77 { kARGB_4444_SkColorType, true, transform_scanline_4444 }, 71 { kBGRA_8888_SkColorType, kPremul_SkAlphaType, transform_scanline_bgrA },
78 { kIndex_8_SkColorType, false, transform_scanline_memcpy }, 72 { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, transform_scanline_memc py },
79 { kGray_8_SkColorType, false, transform_scanline_memcpy }, 73 { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, transform_scanline_BGRA },
74 { kARGB_4444_SkColorType, kOpaque_SkAlphaType, transform_scanline_444 },
75 { kARGB_4444_SkColorType, kPremul_SkAlphaType, transform_scanline_4444 },
76 { kIndex_8_SkColorType, kOpaque_SkAlphaType, transform_scanline_memc py },
77 { kIndex_8_SkColorType, kPremul_SkAlphaType, transform_scanline_memc py },
78 { kIndex_8_SkColorType, kUnpremul_SkAlphaType, transform_scanline_memc py },
79 { kGray_8_SkColorType, kOpaque_SkAlphaType, transform_scanline_memc py },
80 }; 80 };
81 81
82 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) { 82 for (auto entry : gMap) {
83 if (gMap[i].fColorType == ct && gMap[i].fHasAlpha == hasAlpha) { 83 if (entry.fColorType == ct && entry.fAlphaType == alphaType) {
84 return gMap[i].fProc; 84 return entry.fProc;
85 } 85 }
86 } 86 }
87 sk_throw(); 87 sk_throw();
88 return nullptr; 88 return nullptr;
89 } 89 }
90 90
91 // return the minimum legal bitdepth (by png standards) for this many colortable 91 // return the minimum legal bitdepth (by png standards) for this many colortable
92 // entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16, 92 // entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16,
93 // we can use fewer bits per in png 93 // we can use fewer bits per in png
94 static int computeBitDepth(int colorCount) { 94 static int computeBitDepth(int colorCount) {
(...skipping 10 matching lines...) Expand all
105 105
106 /* Pack palette[] with the corresponding colors, and if hasAlpha is true, also 106 /* Pack palette[] with the corresponding colors, and if hasAlpha is true, also
107 pack trans[] and return the number of trans[] entries written. If hasAlpha 107 pack trans[] and return the number of trans[] entries written. If hasAlpha
108 is false, the return value will always be 0. 108 is false, the return value will always be 0.
109 109
110 Note: this routine takes care of unpremultiplying the RGB values when we 110 Note: this routine takes care of unpremultiplying the RGB values when we
111 have alpha in the colortable, since png doesn't support premul colors 111 have alpha in the colortable, since png doesn't support premul colors
112 */ 112 */
113 static inline int pack_palette(SkColorTable* ctable, 113 static inline int pack_palette(SkColorTable* ctable,
114 png_color* SK_RESTRICT palette, 114 png_color* SK_RESTRICT palette,
115 png_byte* SK_RESTRICT trans, bool hasAlpha) { 115 png_byte* SK_RESTRICT trans, SkAlphaType alphaTyp e) {
116 const SkPMColor* SK_RESTRICT colors = ctable ? ctable->readColors() : nullpt r; 116 const SkPMColor* SK_RESTRICT colors = ctable ? ctable->readColors() : nullpt r;
117 const int ctCount = ctable->count(); 117 const int ctCount = ctable->count();
118 int i, num_trans = 0; 118 int i, num_trans = 0;
119 119
120 if (hasAlpha) { 120 if (kOpaque_SkAlphaType != alphaType) {
121 /* first see if we have some number of fully opaque at the end of the 121 /* first see if we have some number of fully opaque at the end of the
122 ctable. PNG allows num_trans < num_palette, but all of the trans 122 ctable. PNG allows num_trans < num_palette, but all of the trans
123 entries must come first in the palette. If I was smarter, I'd 123 entries must come first in the palette. If I was smarter, I'd
124 reorder the indices and ctable so that all non-opaque colors came 124 reorder the indices and ctable so that all non-opaque colors came
125 first in the palette. But, since that would slow down the encode, 125 first in the palette. But, since that would slow down the encode,
126 I'm leaving the indices and ctable order as is, and just looking 126 I'm leaving the indices and ctable order as is, and just looking
127 at the tail of the ctable for opaqueness. 127 at the tail of the ctable for opaqueness.
128 */ 128 */
129 num_trans = ctCount; 129 num_trans = ctCount;
130 for (i = ctCount - 1; i >= 0; --i) { 130 for (i = ctCount - 1; i >= 0; --i) {
131 if (SkGetPackedA32(colors[i]) != 0xFF) { 131 if (SkGetPackedA32(colors[i]) != 0xFF) {
132 break; 132 break;
133 } 133 }
134 num_trans -= 1; 134 num_trans -= 1;
135 } 135 }
136 136
137 const SkUnPreMultiply::Scale* SK_RESTRICT table = 137 if (kPremul_SkAlphaType == alphaType) {
138 SkUnPreMultiply::GetScaleTable(); 138 const SkUnPreMultiply::Scale* SK_RESTRICT table = SkUnPreMultiply::G etScaleTable();
139 for (i = 0; i < num_trans; i++) {
140 const SkPMColor c = *colors++;
141 const unsigned a = SkGetPackedA32(c);
142 const SkUnPreMultiply::Scale s = table[a];
143 trans[i] = a;
144 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c ));
145 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32( c));
146 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32( c));
147 }
148 } else {
149 for (i = 0; i < num_trans; i++) {
150 const SkPMColor c = *colors++;
151 trans[i] = SkGetPackedA32(c);
152 palette[i].red = SkGetPackedR32(c);
153 palette[i].green = SkGetPackedG32(c);
154 palette[i].blue = SkGetPackedB32(c);
155 }
156 }
139 157
140 for (i = 0; i < num_trans; i++) {
141 const SkPMColor c = *colors++;
142 const unsigned a = SkGetPackedA32(c);
143 const SkUnPreMultiply::Scale s = table[a];
144 trans[i] = a;
145 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
146 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c));
147 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
148 }
149 // now fall out of this if-block to use common code for the trailing 158 // now fall out of this if-block to use common code for the trailing
150 // opaque entries 159 // opaque entries
151 } 160 }
152 161
153 // these (remaining) entries are opaque 162 // these (remaining) entries are opaque
154 for (i = num_trans; i < ctCount; i++) { 163 for (i = num_trans; i < ctCount; i++) {
155 SkPMColor c = *colors++; 164 SkPMColor c = *colors++;
156 palette[i].red = SkGetPackedR32(c); 165 palette[i].red = SkGetPackedR32(c);
157 palette[i].green = SkGetPackedG32(c); 166 palette[i].green = SkGetPackedG32(c);
158 palette[i].blue = SkGetPackedB32(c); 167 palette[i].blue = SkGetPackedB32(c);
159 } 168 }
160 return num_trans; 169 return num_trans;
161 } 170 }
162 171
163 class SkPNGImageEncoder : public SkImageEncoder { 172 class SkPNGImageEncoder : public SkImageEncoder {
164 protected: 173 protected:
165 bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override; 174 bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
166 private: 175 private:
167 bool doEncode(SkWStream* stream, const SkBitmap& bm, 176 bool doEncode(SkWStream* stream, const SkBitmap& bm,
168 const bool& hasAlpha, int colorType, 177 SkAlphaType alphaType, int colorType,
169 int bitDepth, SkColorType ct, 178 int bitDepth, SkColorType ct,
170 png_color_8& sig_bit); 179 png_color_8& sig_bit);
171 180
172 typedef SkImageEncoder INHERITED; 181 typedef SkImageEncoder INHERITED;
173 }; 182 };
174 183
175 bool SkPNGImageEncoder::onEncode(SkWStream* stream, 184 bool SkPNGImageEncoder::onEncode(SkWStream* stream,
176 const SkBitmap& originalBitmap, 185 const SkBitmap& originalBitmap,
177 int /*quality*/) { 186 int /*quality*/) {
178 SkBitmap copy; 187 SkBitmap copy;
179 const SkBitmap* bitmap = &originalBitmap; 188 const SkBitmap* bitmap = &originalBitmap;
180 switch (originalBitmap.colorType()) { 189 switch (originalBitmap.colorType()) {
181 case kIndex_8_SkColorType: 190 case kIndex_8_SkColorType:
182 case kGray_8_SkColorType: 191 case kGray_8_SkColorType:
183 case kN32_SkColorType: 192 case kRGBA_8888_SkColorType:
193 case kBGRA_8888_SkColorType:
184 case kARGB_4444_SkColorType: 194 case kARGB_4444_SkColorType:
185 case kRGB_565_SkColorType: 195 case kRGB_565_SkColorType:
186 break; 196 break;
187 default: 197 default:
188 // TODO(scroggo): support 8888-but-not-N32 natively.
189 // TODO(scroggo): support Alpha_8 as Grayscale(black)+Alpha 198 // TODO(scroggo): support Alpha_8 as Grayscale(black)+Alpha
190 if (originalBitmap.copyTo(&copy, kN32_SkColorType)) { 199 if (originalBitmap.copyTo(&copy, kN32_SkColorType)) {
191 bitmap = &copy; 200 bitmap = &copy;
192 } 201 }
193 } 202 }
194 SkColorType ct = bitmap->colorType(); 203 SkColorType ct = bitmap->colorType();
195 204
196 const bool hasAlpha = !bitmap->isOpaque(); 205 const SkAlphaType alphaType = bitmap->alphaType();
206 switch (alphaType) {
207 case kUnpremul_SkAlphaType:
208 if (kARGB_4444_SkColorType == ct) {
209 return false;
210 }
211
212 break;
213 case kOpaque_SkAlphaType:
214 case kPremul_SkAlphaType:
215 break;
216 default:
217 return false;
218 }
219
220 const bool isOpaque = (kOpaque_SkAlphaType == alphaType);
197 int bitDepth = 8; // default for color 221 int bitDepth = 8; // default for color
198 png_color_8 sig_bit; 222 png_color_8 sig_bit;
199 sk_bzero(&sig_bit, sizeof(png_color_8)); 223 sk_bzero(&sig_bit, sizeof(png_color_8));
200 224
201 int colorType; 225 int colorType;
202 switch (ct) { 226 switch (ct) {
203 case kIndex_8_SkColorType: 227 case kIndex_8_SkColorType:
204 sig_bit.red = 8; 228 sig_bit.red = 8;
205 sig_bit.green = 8; 229 sig_bit.green = 8;
206 sig_bit.blue = 8; 230 sig_bit.blue = 8;
207 sig_bit.alpha = 8; 231 sig_bit.alpha = 8;
208 colorType = PNG_COLOR_TYPE_PALETTE; 232 colorType = PNG_COLOR_TYPE_PALETTE;
209 break; 233 break;
210 case kGray_8_SkColorType: 234 case kGray_8_SkColorType:
211 sig_bit.gray = 8; 235 sig_bit.gray = 8;
212 colorType = PNG_COLOR_TYPE_GRAY; 236 colorType = PNG_COLOR_TYPE_GRAY;
213 SkASSERT(!hasAlpha); 237 SkASSERT(isOpaque);
214 break; 238 break;
215 case kN32_SkColorType: 239 case kRGBA_8888_SkColorType:
240 case kBGRA_8888_SkColorType:
216 sig_bit.red = 8; 241 sig_bit.red = 8;
217 sig_bit.green = 8; 242 sig_bit.green = 8;
218 sig_bit.blue = 8; 243 sig_bit.blue = 8;
219 sig_bit.alpha = 8; 244 sig_bit.alpha = 8;
220 colorType = hasAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB ; 245 colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA ;
221 break; 246 break;
222 case kARGB_4444_SkColorType: 247 case kARGB_4444_SkColorType:
223 sig_bit.red = 4; 248 sig_bit.red = 4;
224 sig_bit.green = 4; 249 sig_bit.green = 4;
225 sig_bit.blue = 4; 250 sig_bit.blue = 4;
226 sig_bit.alpha = 4; 251 sig_bit.alpha = 4;
227 colorType = hasAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB ; 252 colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA ;
228 break; 253 break;
229 case kRGB_565_SkColorType: 254 case kRGB_565_SkColorType:
230 sig_bit.red = 5; 255 sig_bit.red = 5;
231 sig_bit.green = 6; 256 sig_bit.green = 6;
232 sig_bit.blue = 5; 257 sig_bit.blue = 5;
233 colorType = PNG_COLOR_TYPE_RGB; 258 colorType = PNG_COLOR_TYPE_RGB;
234 SkASSERT(!hasAlpha); 259 SkASSERT(isOpaque);
235 break; 260 break;
236 default: 261 default:
237 return false; 262 return false;
238 } 263 }
239 264
240 SkAutoLockPixels alp(*bitmap); 265 SkAutoLockPixels alp(*bitmap);
241 // readyToDraw checks for pixels (and colortable if that is required) 266 // readyToDraw checks for pixels (and colortable if that is required)
242 if (!bitmap->readyToDraw()) { 267 if (!bitmap->readyToDraw()) {
243 return false; 268 return false;
244 } 269 }
245 270
246 // we must do this after we have locked the pixels 271 // we must do this after we have locked the pixels
247 SkColorTable* ctable = bitmap->getColorTable(); 272 SkColorTable* ctable = bitmap->getColorTable();
248 if (ctable) { 273 if (ctable) {
249 if (ctable->count() == 0) { 274 if (ctable->count() == 0) {
250 return false; 275 return false;
251 } 276 }
252 // check if we can store in fewer than 8 bits 277 // check if we can store in fewer than 8 bits
253 bitDepth = computeBitDepth(ctable->count()); 278 bitDepth = computeBitDepth(ctable->count());
254 } 279 }
255 280
256 return doEncode(stream, *bitmap, hasAlpha, colorType, bitDepth, ct, sig_bit) ; 281 return doEncode(stream, *bitmap, alphaType, colorType, bitDepth, ct, sig_bit );
257 } 282 }
258 283
259 bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap, 284 bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,
260 const bool& hasAlpha, int colorType, 285 SkAlphaType alphaType, int colorType,
261 int bitDepth, SkColorType ct, 286 int bitDepth, SkColorType ct,
262 png_color_8& sig_bit) { 287 png_color_8& sig_bit) {
263 288
264 png_structp png_ptr; 289 png_structp png_ptr;
265 png_infop info_ptr; 290 png_infop info_ptr;
266 291
267 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_f n, 292 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_f n,
268 nullptr); 293 nullptr);
269 if (nullptr == png_ptr) { 294 if (nullptr == png_ptr) {
270 return false; 295 return false;
(...skipping 26 matching lines...) Expand all
297 322
298 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(), 323 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),
299 bitDepth, colorType, 324 bitDepth, colorType,
300 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, 325 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
301 PNG_FILTER_TYPE_BASE); 326 PNG_FILTER_TYPE_BASE);
302 327
303 // set our colortable/trans arrays if needed 328 // set our colortable/trans arrays if needed
304 png_color paletteColors[256]; 329 png_color paletteColors[256];
305 png_byte trans[256]; 330 png_byte trans[256];
306 if (kIndex_8_SkColorType == ct) { 331 if (kIndex_8_SkColorType == ct) {
307 SkColorTable* ct = bitmap.getColorTable(); 332 SkColorTable* colorTable = bitmap.getColorTable();
308 int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha); 333 int numTrans = pack_palette(colorTable, paletteColors, trans, alphaType) ;
309 png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count()); 334 png_set_PLTE(png_ptr, info_ptr, paletteColors, colorTable->count());
310 if (numTrans > 0) { 335 if (numTrans > 0) {
311 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, nullptr); 336 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, nullptr);
312 } 337 }
313 } 338 }
314 339
315 png_set_sBIT(png_ptr, info_ptr, &sig_bit); 340 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
316 png_write_info(png_ptr, info_ptr); 341 png_write_info(png_ptr, info_ptr);
317 342
318 const char* srcImage = (const char*)bitmap.getPixels(); 343 const char* srcImage = (const char*)bitmap.getPixels();
319 SkAutoSTMalloc<1024, char> rowStorage(bitmap.width() << 2); 344 SkAutoSTMalloc<1024, char> rowStorage(bitmap.width() << 2);
320 char* storage = rowStorage.get(); 345 char* storage = rowStorage.get();
321 transform_scanline_proc proc = choose_proc(ct, hasAlpha); 346 transform_scanline_proc proc = choose_proc(ct, alphaType);
322 347
323 for (int y = 0; y < bitmap.height(); y++) { 348 for (int y = 0; y < bitmap.height(); y++) {
324 png_bytep row_ptr = (png_bytep)storage; 349 png_bytep row_ptr = (png_bytep)storage;
325 proc(srcImage, bitmap.width(), storage); 350 proc(storage, srcImage, bitmap.width(), SkColorTypeBytesPerPixel(ct));
326 png_write_rows(png_ptr, &row_ptr, 1); 351 png_write_rows(png_ptr, &row_ptr, 1);
327 srcImage += bitmap.rowBytes(); 352 srcImage += bitmap.rowBytes();
328 } 353 }
329 354
330 png_write_end(png_ptr, info_ptr); 355 png_write_end(png_ptr, info_ptr);
331 356
332 /* clean up after the write, and free any memory allocated */ 357 /* clean up after the write, and free any memory allocated */
333 png_destroy_write_struct(&png_ptr, &info_ptr); 358 png_destroy_write_struct(&png_ptr, &info_ptr);
334 return true; 359 return true;
335 } 360 }
336 361
337 /////////////////////////////////////////////////////////////////////////////// 362 ///////////////////////////////////////////////////////////////////////////////
338 DEFINE_ENCODER_CREATOR(PNGImageEncoder); 363 DEFINE_ENCODER_CREATOR(PNGImageEncoder);
339 /////////////////////////////////////////////////////////////////////////////// 364 ///////////////////////////////////////////////////////////////////////////////
340 365
341 SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) { 366 SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
342 return (SkImageEncoder::kPNG_Type == t) ? new SkPNGImageEncoder : nullptr; 367 return (SkImageEncoder::kPNG_Type == t) ? new SkPNGImageEncoder : nullptr;
343 } 368 }
344 369
345 static SkImageEncoder_EncodeReg gEReg(sk_libpng_efactory); 370 static SkImageEncoder_EncodeReg gEReg(sk_libpng_efactory);
OLDNEW
« no previous file with comments | « no previous file | src/images/transform_scanline.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698