blob: b57c32a0fc4d0a721af78649ecd90c1e1534b810 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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
Hal Canarydb683012016-11-23 08:55:18 -07008#include "SkImageEncoderPriv.h"
Hal Canary1fcc4042016-11-30 17:07:59 -05009
10#ifdef SK_HAS_PNG_LIBRARY
11
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkColor.h"
13#include "SkColorPriv.h"
14#include "SkDither.h"
Matt Sarett5df93de2017-03-22 21:52:47 +000015#include "SkImageEncoderFns.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkMath.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017#include "SkStream.h"
Matt Sarett74de1f62017-03-21 13:03:26 -040018#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkTemplates.h"
Hal Canary1fcc4042016-11-30 17:07:59 -050020#include "SkUnPreMultiply.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkUtils.h"
djsollenb2a6fe72015-04-03 12:35:27 -070022
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "png.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024
halcanary4e44efe2016-08-04 10:47:16 -070025// Suppress most PNG warnings when calling image decode functions.
Matt Sarett32bf4492017-01-02 09:56:02 -050026static const bool c_suppressPNGImageDecoderWarnings = true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000027
reed@android.com8a1c16f2008-12-17 15:59:43 +000028static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
scroggo04a0d3f2015-12-10 08:54:36 -080029 if (!c_suppressPNGImageDecoderWarnings) {
30 SkDEBUGF(("------ png error %s\n", msg));
31 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000032 longjmp(png_jmpbuf(png_ptr), 1);
33}
34
reed@android.com8a1c16f2008-12-17 15:59:43 +000035static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
commit-bot@chromium.orgc1c56952013-05-02 13:41:34 +000036 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +000037 if (!sk_stream->write(data, len)) {
38 png_error(png_ptr, "sk_write_fn Error!");
39 }
40}
41
Matt Sarett5df93de2017-03-22 21:52:47 +000042static void set_icc(png_structp png_ptr, png_infop info_ptr, sk_sp<SkData> icc) {
Matt Sarett687b6562017-03-21 10:06:45 -040043#if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
Matt Sarett74de1f62017-03-21 13:03:26 -040044 const char* name = "Skia";
Matt Sarett687b6562017-03-21 10:06:45 -040045 png_const_bytep iccPtr = icc->bytes();
46#else
Matt Sarett74de1f62017-03-21 13:03:26 -040047 SkString str("Skia");
48 char* name = str.writable_str();
Matt Sarett687b6562017-03-21 10:06:45 -040049 png_charp iccPtr = (png_charp) icc->writable_data();
50#endif
Matt Sarett74de1f62017-03-21 13:03:26 -040051 png_set_iCCP(png_ptr, info_ptr, name, 0, iccPtr, icc->size());
Matt Sarett687b6562017-03-21 10:06:45 -040052}
53
Matt Sarett7abfb5e2017-04-05 17:36:04 -040054static transform_scanline_proc choose_proc(const SkImageInfo& info,
55 SkTransferFunctionBehavior unpremulBehavior) {
56 const bool isSRGBTransferFn =
57 (SkTransferFunctionBehavior::kRespect == unpremulBehavior) && info.gammaCloseToSRGB();
Matt Sarett84014f02017-01-10 11:28:54 -050058 switch (info.colorType()) {
59 case kRGBA_8888_SkColorType:
60 switch (info.alphaType()) {
61 case kOpaque_SkAlphaType:
62 return transform_scanline_RGBX;
63 case kUnpremul_SkAlphaType:
64 return transform_scanline_memcpy;
65 case kPremul_SkAlphaType:
Matt Sarett7abfb5e2017-04-05 17:36:04 -040066 return isSRGBTransferFn ? transform_scanline_srgbA :
67 transform_scanline_rgbA;
Matt Sarett84014f02017-01-10 11:28:54 -050068 default:
69 SkASSERT(false);
70 return nullptr;
71 }
72 case kBGRA_8888_SkColorType:
73 switch (info.alphaType()) {
74 case kOpaque_SkAlphaType:
75 return transform_scanline_BGRX;
76 case kUnpremul_SkAlphaType:
77 return transform_scanline_BGRA;
78 case kPremul_SkAlphaType:
Matt Sarett7abfb5e2017-04-05 17:36:04 -040079 return isSRGBTransferFn ? transform_scanline_sbgrA :
80 transform_scanline_bgrA;
Matt Sarett84014f02017-01-10 11:28:54 -050081 default:
82 SkASSERT(false);
83 return nullptr;
84 }
85 case kRGB_565_SkColorType:
86 return transform_scanline_565;
87 case kARGB_4444_SkColorType:
88 switch (info.alphaType()) {
89 case kOpaque_SkAlphaType:
90 return transform_scanline_444;
91 case kPremul_SkAlphaType:
92 // 4444 is assumed to be legacy premul.
93 return transform_scanline_4444;
94 default:
95 SkASSERT(false);
96 return nullptr;
97 }
98 case kIndex_8_SkColorType:
99 case kGray_8_SkColorType:
100 return transform_scanline_memcpy;
Matt Sarett1da27ef2017-01-19 17:14:07 -0500101 case kRGBA_F16_SkColorType:
102 switch (info.alphaType()) {
103 case kOpaque_SkAlphaType:
104 case kUnpremul_SkAlphaType:
105 return transform_scanline_F16;
106 case kPremul_SkAlphaType:
107 return transform_scanline_F16_premul;
108 default:
109 SkASSERT(false);
110 return nullptr;
111 }
Matt Sarett84014f02017-01-10 11:28:54 -0500112 default:
113 SkASSERT(false);
114 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116}
117
msarettfed03342016-09-13 07:08:15 -0700118/* Pack palette[] with the corresponding colors, and if the image has alpha, also
119 pack trans[] and return the number of alphas[] entries written. If the image is
120 opaque, the return value will always be 0.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121*/
msarettfed03342016-09-13 07:08:15 -0700122static inline int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
Matt Sarett7abfb5e2017-04-05 17:36:04 -0400123 png_byte* SK_RESTRICT alphas, const SkImageInfo& info,
124 SkTransferFunctionBehavior unpremulBehavior) {
Matt Sarett84014f02017-01-10 11:28:54 -0500125 const SkPMColor* colors = ctable->readColors();
msarettfed03342016-09-13 07:08:15 -0700126 const int count = ctable->count();
Matt Sarett84014f02017-01-10 11:28:54 -0500127 SkPMColor storage[256];
128 if (kPremul_SkAlphaType == info.alphaType()) {
129 // Unpremultiply the colors.
130 const SkImageInfo rgbaInfo = info.makeColorType(kRGBA_8888_SkColorType);
Matt Sarett7abfb5e2017-04-05 17:36:04 -0400131 transform_scanline_proc proc = choose_proc(rgbaInfo, unpremulBehavior);
Matt Sarett62bb2802017-01-23 12:28:02 -0500132 proc((char*) storage, (const char*) colors, ctable->count(), 4, nullptr);
Matt Sarett84014f02017-01-10 11:28:54 -0500133 colors = storage;
134 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000135
Matt Sarett84014f02017-01-10 11:28:54 -0500136 int numWithAlpha = 0;
137 if (kOpaque_SkAlphaType != info.alphaType()) {
msarettfed03342016-09-13 07:08:15 -0700138 // PNG requires that all non-opaque colors come first in the palette. Write these first.
139 for (int i = 0; i < count; i++) {
140 uint8_t alpha = SkGetPackedA32(colors[i]);
141 if (0xFF != alpha) {
142 alphas[numWithAlpha] = alpha;
Matt Sarett84014f02017-01-10 11:28:54 -0500143 palette[numWithAlpha].red = SkGetPackedR32(colors[i]);
144 palette[numWithAlpha].green = SkGetPackedG32(colors[i]);
145 palette[numWithAlpha].blue = SkGetPackedB32(colors[i]);
msarettfed03342016-09-13 07:08:15 -0700146 numWithAlpha++;
msarettf17b71f2016-09-12 14:30:03 -0700147 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000148 }
msarettf17b71f2016-09-12 14:30:03 -0700149
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000151
msarettfed03342016-09-13 07:08:15 -0700152 if (0 == numWithAlpha) {
153 // All of the entries are opaque.
154 for (int i = 0; i < count; i++) {
155 SkPMColor c = *colors++;
156 palette[i].red = SkGetPackedR32(c);
157 palette[i].green = SkGetPackedG32(c);
158 palette[i].blue = SkGetPackedB32(c);
159 }
160 } else {
161 // We have already written the non-opaque colors. Now just write the opaque colors.
162 int currIndex = numWithAlpha;
163 int i = 0;
164 while (currIndex != count) {
165 uint8_t alpha = SkGetPackedA32(colors[i]);
166 if (0xFF == alpha) {
167 palette[currIndex].red = SkGetPackedR32(colors[i]);
168 palette[currIndex].green = SkGetPackedG32(colors[i]);
169 palette[currIndex].blue = SkGetPackedB32(colors[i]);
170 currIndex++;
171 }
172
173 i++;
174 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 }
msarettfed03342016-09-13 07:08:15 -0700176
177 return numWithAlpha;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178}
179
Matt Sarett7abfb5e2017-04-05 17:36:04 -0400180static bool do_encode(SkWStream*, const SkPixmap&, int, int, png_color_8&,
181 SkTransferFunctionBehavior unpremulBehavior);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000182
Matt Sarett7abfb5e2017-04-05 17:36:04 -0400183bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& pixmap, const SkEncodeOptions& opts) {
184 if (SkTransferFunctionBehavior::kRespect == opts.fUnpremulBehavior) {
185 if (!pixmap.colorSpace() || (!pixmap.colorSpace()->gammaCloseToSRGB() &&
186 !pixmap.colorSpace()->gammaIsLinear())) {
Matt Sarett84014f02017-01-10 11:28:54 -0500187 return false;
188 }
189 }
190
Hal Canary1fcc4042016-11-30 17:07:59 -0500191 if (!pixmap.addr() || pixmap.info().isEmpty()) {
192 return false;
193 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
Matt Sarett1da27ef2017-01-19 17:14:07 -0500195 const SkColorType colorType = pixmap.colorType();
Hal Canary1fcc4042016-11-30 17:07:59 -0500196 const SkAlphaType alphaType = pixmap.alphaType();
msarettf17b71f2016-09-12 14:30:03 -0700197 switch (alphaType) {
198 case kUnpremul_SkAlphaType:
Matt Sarett84014f02017-01-10 11:28:54 -0500199 if (kARGB_4444_SkColorType == colorType) {
msarettf17b71f2016-09-12 14:30:03 -0700200 return false;
201 }
202
203 break;
204 case kOpaque_SkAlphaType:
205 case kPremul_SkAlphaType:
206 break;
207 default:
208 return false;
209 }
210
211 const bool isOpaque = (kOpaque_SkAlphaType == alphaType);
Matt Sarett1da27ef2017-01-19 17:14:07 -0500212 int bitDepth = 8;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 png_color_8 sig_bit;
msarett9b09cd82016-08-29 14:47:49 -0700214 sk_bzero(&sig_bit, sizeof(png_color_8));
Matt Sarett84014f02017-01-10 11:28:54 -0500215 int pngColorType;
216 switch (colorType) {
Matt Sarett1da27ef2017-01-19 17:14:07 -0500217 case kRGBA_F16_SkColorType:
218 if (!pixmap.colorSpace() || !pixmap.colorSpace()->gammaIsLinear()) {
219 return false;
220 }
221
222 sig_bit.red = 16;
223 sig_bit.green = 16;
224 sig_bit.blue = 16;
225 sig_bit.alpha = 16;
226 bitDepth = 16;
227 pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
228 break;
reed0689d7b2014-06-14 05:30:20 -0700229 case kIndex_8_SkColorType:
msarett9b09cd82016-08-29 14:47:49 -0700230 sig_bit.red = 8;
231 sig_bit.green = 8;
232 sig_bit.blue = 8;
233 sig_bit.alpha = 8;
Matt Sarett84014f02017-01-10 11:28:54 -0500234 pngColorType = PNG_COLOR_TYPE_PALETTE;
msarett9b09cd82016-08-29 14:47:49 -0700235 break;
236 case kGray_8_SkColorType:
237 sig_bit.gray = 8;
Matt Sarett84014f02017-01-10 11:28:54 -0500238 pngColorType = PNG_COLOR_TYPE_GRAY;
msarettf17b71f2016-09-12 14:30:03 -0700239 SkASSERT(isOpaque);
msarett9b09cd82016-08-29 14:47:49 -0700240 break;
msarettf17b71f2016-09-12 14:30:03 -0700241 case kRGBA_8888_SkColorType:
242 case kBGRA_8888_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 sig_bit.red = 8;
244 sig_bit.green = 8;
245 sig_bit.blue = 8;
246 sig_bit.alpha = 8;
Matt Sarett84014f02017-01-10 11:28:54 -0500247 pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 break;
reed0689d7b2014-06-14 05:30:20 -0700249 case kARGB_4444_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 sig_bit.red = 4;
251 sig_bit.green = 4;
252 sig_bit.blue = 4;
253 sig_bit.alpha = 4;
Matt Sarett84014f02017-01-10 11:28:54 -0500254 pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 break;
reed0689d7b2014-06-14 05:30:20 -0700256 case kRGB_565_SkColorType:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 sig_bit.red = 5;
258 sig_bit.green = 6;
259 sig_bit.blue = 5;
Matt Sarett84014f02017-01-10 11:28:54 -0500260 pngColorType = PNG_COLOR_TYPE_RGB;
msarettf17b71f2016-09-12 14:30:03 -0700261 SkASSERT(isOpaque);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 break;
263 default:
264 return false;
265 }
Matt Sarett1da27ef2017-01-19 17:14:07 -0500266
Matt Sarett84014f02017-01-10 11:28:54 -0500267 if (kIndex_8_SkColorType == colorType) {
Hal Canary1fcc4042016-11-30 17:07:59 -0500268 SkColorTable* ctable = pixmap.ctable();
269 if (!ctable || ctable->count() == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 return false;
271 }
Matt Sarett32bf4492017-01-02 09:56:02 -0500272
273 // Currently, we always use 8-bit indices for paletted pngs.
274 // When ctable->count() <= 16, we could potentially use 1, 2,
275 // or 4 bit indices.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 }
Matt Sarett1da27ef2017-01-19 17:14:07 -0500277
Matt Sarett7abfb5e2017-04-05 17:36:04 -0400278 return do_encode(stream, pixmap, pngColorType, bitDepth, sig_bit, opts.fUnpremulBehavior);
tomhudson@google.com5c210c72011-07-28 21:06:40 +0000279}
280
Matt Sarett1da27ef2017-01-19 17:14:07 -0500281static int num_components(int pngColorType) {
282 switch (pngColorType) {
283 case PNG_COLOR_TYPE_PALETTE:
284 case PNG_COLOR_TYPE_GRAY:
285 return 1;
286 case PNG_COLOR_TYPE_RGB:
287 return 3;
288 case PNG_COLOR_TYPE_RGBA:
289 return 4;
290 default:
291 SkASSERT(false);
292 return 0;
293 }
294}
295
Matt Sarett7abfb5e2017-04-05 17:36:04 -0400296static bool do_encode(SkWStream* stream, const SkPixmap& pixmap, int pngColorType, int bitDepth,
297 png_color_8& sig_bit, SkTransferFunctionBehavior unpremulBehavior) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 png_structp png_ptr;
299 png_infop info_ptr;
300
Hal Canary1fcc4042016-11-30 17:07:59 -0500301 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, nullptr);
halcanary96fcdcc2015-08-27 07:41:13 -0700302 if (nullptr == png_ptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 return false;
304 }
305
306 info_ptr = png_create_info_struct(png_ptr);
halcanary96fcdcc2015-08-27 07:41:13 -0700307 if (nullptr == info_ptr) {
Matt Sarett32bf4492017-01-02 09:56:02 -0500308 png_destroy_write_struct(&png_ptr, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 return false;
310 }
311
312 /* Set error handling. REQUIRED if you aren't supplying your own
313 * error handling functions in the png_create_write_struct() call.
314 */
315 if (setjmp(png_jmpbuf(png_ptr))) {
316 png_destroy_write_struct(&png_ptr, &info_ptr);
317 return false;
318 }
319
Matt Sarett32bf4492017-01-02 09:56:02 -0500320 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321
322 /* Set the image information here. Width and height are up to 2^31,
323 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
324 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
325 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
326 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
327 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
328 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
329 */
330
Hal Canary1fcc4042016-11-30 17:07:59 -0500331 png_set_IHDR(png_ptr, info_ptr, pixmap.width(), pixmap.height(),
Matt Sarett84014f02017-01-10 11:28:54 -0500332 bitDepth, pngColorType,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
334 PNG_FILTER_TYPE_BASE);
335
reed@android.com61898772009-07-07 19:38:01 +0000336 // set our colortable/trans arrays if needed
337 png_color paletteColors[256];
338 png_byte trans[256];
Matt Sarett84014f02017-01-10 11:28:54 -0500339 if (kIndex_8_SkColorType == pixmap.colorType()) {
Hal Canary1fcc4042016-11-30 17:07:59 -0500340 SkColorTable* colorTable = pixmap.ctable();
msarettfed03342016-09-13 07:08:15 -0700341 SkASSERT(colorTable);
Matt Sarett7abfb5e2017-04-05 17:36:04 -0400342 int numTrans = pack_palette(colorTable, paletteColors, trans, pixmap.info(),
343 unpremulBehavior);
msarettf17b71f2016-09-12 14:30:03 -0700344 png_set_PLTE(png_ptr, info_ptr, paletteColors, colorTable->count());
reed@android.com61898772009-07-07 19:38:01 +0000345 if (numTrans > 0) {
halcanary96fcdcc2015-08-27 07:41:13 -0700346 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, nullptr);
reed@android.com61898772009-07-07 19:38:01 +0000347 }
348 }
msarett9b09cd82016-08-29 14:47:49 -0700349
Matt Sarett0e032be2017-03-15 17:50:08 -0400350 if (pixmap.colorSpace()) {
Matt Sarett0e032be2017-03-15 17:50:08 -0400351 if (pixmap.colorSpace()->isSRGB()) {
352 png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
Matt Sarett5df93de2017-03-22 21:52:47 +0000353 } else {
354 sk_sp<SkData> icc = icc_from_color_space(*pixmap.colorSpace());
355 if (icc) {
356 set_icc(png_ptr, info_ptr, std::move(icc));
357 }
Matt Sarett0e032be2017-03-15 17:50:08 -0400358 }
Matt Sarett0e032be2017-03-15 17:50:08 -0400359 }
360
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
362 png_write_info(png_ptr, info_ptr);
Matt Sarett1da27ef2017-01-19 17:14:07 -0500363 int pngBytesPerPixel = num_components(pngColorType) * (bitDepth / 8);
364 if (kRGBA_F16_SkColorType == pixmap.colorType() && kOpaque_SkAlphaType == pixmap.alphaType()) {
365 // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng
366 // to skip the alpha channel.
367 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
368 pngBytesPerPixel = 8;
369 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370
Matt Sarett1da27ef2017-01-19 17:14:07 -0500371 SkAutoSTMalloc<1024, char> rowStorage(pixmap.width() * pngBytesPerPixel);
scroggo565901d2015-12-10 10:44:13 -0800372 char* storage = rowStorage.get();
Matt Sarett1da27ef2017-01-19 17:14:07 -0500373 const char* srcImage = (const char*)pixmap.addr();
Matt Sarett7abfb5e2017-04-05 17:36:04 -0400374 transform_scanline_proc proc = choose_proc(pixmap.info(), unpremulBehavior);
Hal Canary1fcc4042016-11-30 17:07:59 -0500375 for (int y = 0; y < pixmap.height(); y++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 png_bytep row_ptr = (png_bytep)storage;
Matt Sarett62bb2802017-01-23 12:28:02 -0500377 proc(storage, srcImage, pixmap.width(), SkColorTypeBytesPerPixel(pixmap.colorType()),
378 nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 png_write_rows(png_ptr, &row_ptr, 1);
Hal Canary1fcc4042016-11-30 17:07:59 -0500380 srcImage += pixmap.rowBytes();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 }
382
383 png_write_end(png_ptr, info_ptr);
384
385 /* clean up after the write, and free any memory allocated */
386 png_destroy_write_struct(&png_ptr, &info_ptr);
387 return true;
388}
389
Hal Canary1fcc4042016-11-30 17:07:59 -0500390#endif