blob: 2cf3ba496e2a59d1b778a23b5628d1591e65cbf0 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/images/SkImageEncoderPriv.h"
Hal Canary1fcc4042016-11-30 17:07:59 -05009
Leon Scroggins IIIa77f30c2020-03-09 14:23:30 -040010#ifdef SK_ENCODE_PNG
Mike Kleinab737e42019-05-15 21:58:15 +000011
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/core/SkStream.h"
13#include "include/core/SkString.h"
14#include "include/encode/SkPngEncoder.h"
15#include "include/private/SkImageInfoPriv.h"
16#include "src/codec/SkColorTable.h"
17#include "src/codec/SkPngPriv.h"
Mike Klein33c36e02019-10-14 10:28:25 -050018#include "src/core/SkMSAN.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/images/SkImageEncoderFns.h"
Mike Klein509ccb02018-11-02 12:54:01 -040020#include <vector>
djsollenb2a6fe72015-04-03 12:35:27 -070021
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "png.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023
Matt Sarettbe4c9b02017-05-08 12:11:44 -040024static_assert(PNG_FILTER_NONE == (int)SkPngEncoder::FilterFlag::kNone, "Skia libpng filter err.");
25static_assert(PNG_FILTER_SUB == (int)SkPngEncoder::FilterFlag::kSub, "Skia libpng filter err.");
26static_assert(PNG_FILTER_UP == (int)SkPngEncoder::FilterFlag::kUp, "Skia libpng filter err.");
27static_assert(PNG_FILTER_AVG == (int)SkPngEncoder::FilterFlag::kAvg, "Skia libpng filter err.");
28static_assert(PNG_FILTER_PAETH == (int)SkPngEncoder::FilterFlag::kPaeth, "Skia libpng filter err.");
29static_assert(PNG_ALL_FILTERS == (int)SkPngEncoder::FilterFlag::kAll, "Skia libpng filter err.");
30
Matt Sarettc367d032017-05-05 11:13:26 -040031static constexpr bool kSuppressPngEncodeWarnings = true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000032
reed@android.com8a1c16f2008-12-17 15:59:43 +000033static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
Matt Sarettc367d032017-05-05 11:13:26 -040034 if (!kSuppressPngEncodeWarnings) {
35 SkDebugf("libpng encode error: %s\n", msg);
scroggo04a0d3f2015-12-10 08:54:36 -080036 }
Matt Sarettc367d032017-05-05 11:13:26 -040037
reed@android.com8a1c16f2008-12-17 15:59:43 +000038 longjmp(png_jmpbuf(png_ptr), 1);
39}
40
reed@android.com8a1c16f2008-12-17 15:59:43 +000041static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
Matt Sarettc367d032017-05-05 11:13:26 -040042 SkWStream* stream = (SkWStream*)png_get_io_ptr(png_ptr);
43 if (!stream->write(data, len)) {
44 png_error(png_ptr, "sk_write_fn cannot write to stream");
reed@android.com8a1c16f2008-12-17 15:59:43 +000045 }
46}
47
Matt Sarettc367d032017-05-05 11:13:26 -040048class SkPngEncoderMgr final : SkNoncopyable {
49public:
50
51 /*
52 * Create the decode manager
53 * Does not take ownership of stream
54 */
55 static std::unique_ptr<SkPngEncoderMgr> Make(SkWStream* stream);
56
Matt Sarettbe4c9b02017-05-08 12:11:44 -040057 bool setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options);
Matt Sarett1950e0a2017-06-12 16:17:30 -040058 bool setColorSpace(const SkImageInfo& info);
Matt Sarettc367d032017-05-05 11:13:26 -040059 bool writeInfo(const SkImageInfo& srcInfo);
Brian Osmanb62f50c2018-07-12 14:44:27 -040060 void chooseProc(const SkImageInfo& srcInfo);
Matt Sarettc367d032017-05-05 11:13:26 -040061
62 png_structp pngPtr() { return fPngPtr; }
63 png_infop infoPtr() { return fInfoPtr; }
64 int pngBytesPerPixel() const { return fPngBytesPerPixel; }
65 transform_scanline_proc proc() const { return fProc; }
66
67 ~SkPngEncoderMgr() {
68 png_destroy_write_struct(&fPngPtr, &fInfoPtr);
69 }
70
71private:
72
73 SkPngEncoderMgr(png_structp pngPtr, png_infop infoPtr)
74 : fPngPtr(pngPtr)
75 , fInfoPtr(infoPtr)
76 {}
77
78 png_structp fPngPtr;
79 png_infop fInfoPtr;
80 int fPngBytesPerPixel;
81 transform_scanline_proc fProc;
82};
83
84std::unique_ptr<SkPngEncoderMgr> SkPngEncoderMgr::Make(SkWStream* stream) {
85 png_structp pngPtr =
86 png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, nullptr);
87 if (!pngPtr) {
88 return nullptr;
89 }
90
91 png_infop infoPtr = png_create_info_struct(pngPtr);
92 if (!infoPtr) {
93 png_destroy_write_struct(&pngPtr, nullptr);
94 return nullptr;
95 }
96
97 png_set_write_fn(pngPtr, (void*)stream, sk_write_fn, nullptr);
98 return std::unique_ptr<SkPngEncoderMgr>(new SkPngEncoderMgr(pngPtr, infoPtr));
99}
100
Matt Sarettbe4c9b02017-05-08 12:11:44 -0400101bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options) {
Matt Sarettc367d032017-05-05 11:13:26 -0400102 if (setjmp(png_jmpbuf(fPngPtr))) {
103 return false;
104 }
105
106 int pngColorType;
107 png_color_8 sigBit;
108 int bitDepth = 8;
109 switch (srcInfo.colorType()) {
Mike Kleinb70990e2019-02-28 10:03:27 -0600110 case kRGBA_F16Norm_SkColorType:
Matt Sarettc367d032017-05-05 11:13:26 -0400111 case kRGBA_F16_SkColorType:
Mike Klein37854712018-06-26 11:43:06 -0400112 case kRGBA_F32_SkColorType:
Matt Sarettc367d032017-05-05 11:13:26 -0400113 sigBit.red = 16;
114 sigBit.green = 16;
115 sigBit.blue = 16;
116 sigBit.alpha = 16;
117 bitDepth = 16;
118 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
119 fPngBytesPerPixel = 8;
120 break;
Matt Sarettc367d032017-05-05 11:13:26 -0400121 case kGray_8_SkColorType:
122 sigBit.gray = 8;
123 pngColorType = PNG_COLOR_TYPE_GRAY;
124 fPngBytesPerPixel = 1;
125 SkASSERT(srcInfo.isOpaque());
126 break;
127 case kRGBA_8888_SkColorType:
128 case kBGRA_8888_SkColorType:
129 sigBit.red = 8;
130 sigBit.green = 8;
131 sigBit.blue = 8;
132 sigBit.alpha = 8;
133 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
134 fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4;
135 break;
Mike Kleinac568a92018-01-25 09:09:32 -0500136 case kRGB_888x_SkColorType:
137 sigBit.red = 8;
138 sigBit.green = 8;
139 sigBit.blue = 8;
140 pngColorType = PNG_COLOR_TYPE_RGB;
141 fPngBytesPerPixel = 3;
142 SkASSERT(srcInfo.isOpaque());
143 break;
Matt Sarettc367d032017-05-05 11:13:26 -0400144 case kARGB_4444_SkColorType:
145 if (kUnpremul_SkAlphaType == srcInfo.alphaType()) {
146 return false;
147 }
148
149 sigBit.red = 4;
150 sigBit.green = 4;
151 sigBit.blue = 4;
152 sigBit.alpha = 4;
153 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
154 fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4;
155 break;
156 case kRGB_565_SkColorType:
157 sigBit.red = 5;
158 sigBit.green = 6;
159 sigBit.blue = 5;
160 pngColorType = PNG_COLOR_TYPE_RGB;
161 fPngBytesPerPixel = 3;
162 SkASSERT(srcInfo.isOpaque());
163 break;
Mike Reedd6cb11e2017-11-30 15:33:04 -0500164 case kAlpha_8_SkColorType: // store as gray+alpha, but ignore gray
165 sigBit.gray = kGraySigBit_GrayAlphaIsJustAlpha;
166 sigBit.alpha = 8;
167 pngColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
168 fPngBytesPerPixel = 2;
169 break;
Mike Kleinac568a92018-01-25 09:09:32 -0500170 case kRGBA_1010102_SkColorType:
171 bitDepth = 16;
172 sigBit.red = 10;
173 sigBit.green = 10;
174 sigBit.blue = 10;
175 sigBit.alpha = 2;
176 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
177 fPngBytesPerPixel = 8;
178 break;
179 case kRGB_101010x_SkColorType:
180 bitDepth = 16;
181 sigBit.red = 10;
182 sigBit.green = 10;
183 sigBit.blue = 10;
184 pngColorType = PNG_COLOR_TYPE_RGB;
185 fPngBytesPerPixel = 6;
186 break;
Matt Sarettc367d032017-05-05 11:13:26 -0400187 default:
188 return false;
189 }
190
191 png_set_IHDR(fPngPtr, fInfoPtr, srcInfo.width(), srcInfo.height(),
192 bitDepth, pngColorType,
193 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
194 PNG_FILTER_TYPE_BASE);
195 png_set_sBIT(fPngPtr, fInfoPtr, &sigBit);
196
Matt Sarettbe4c9b02017-05-08 12:11:44 -0400197 int filters = (int)options.fFilterFlags & (int)SkPngEncoder::FilterFlag::kAll;
198 SkASSERT(filters == (int)options.fFilterFlags);
199 png_set_filter(fPngPtr, PNG_FILTER_TYPE_BASE, filters);
200
Brian Osman788b9162020-02-07 10:36:46 -0500201 int zlibLevel = std::min(std::max(0, options.fZLibLevel), 9);
Matt Sarettbe4c9b02017-05-08 12:11:44 -0400202 SkASSERT(zlibLevel == options.fZLibLevel);
203 png_set_compression_level(fPngPtr, zlibLevel);
Yuqian Lid0dbee62017-06-09 11:35:58 -0400204
205 // Set comments in tEXt chunk
206 const sk_sp<SkDataTable>& comments = options.fComments;
207 if (comments != nullptr) {
208 std::vector<png_text> png_texts(comments->count());
209 std::vector<SkString> clippedKeys;
210 for (int i = 0; i < comments->count() / 2; ++i) {
211 const char* keyword;
212 const char* originalKeyword = comments->atStr(2 * i);
213 const char* text = comments->atStr(2 * i + 1);
214 if (strlen(originalKeyword) <= PNG_KEYWORD_MAX_LENGTH) {
215 keyword = originalKeyword;
216 } else {
217 SkDEBUGFAILF("PNG tEXt keyword should be no longer than %d.",
218 PNG_KEYWORD_MAX_LENGTH);
219 clippedKeys.emplace_back(originalKeyword, PNG_KEYWORD_MAX_LENGTH);
220 keyword = clippedKeys.back().c_str();
221 }
222 // It seems safe to convert png_const_charp to png_charp for key/text,
223 // and we don't have to provide text_length and other fields as we're providing
224 // 0-terminated c_str with PNG_TEXT_COMPRESSION_NONE (no compression, no itxt).
225 png_texts[i].compression = PNG_TEXT_COMPRESSION_NONE;
226 png_texts[i].key = (png_charp)keyword;
227 png_texts[i].text = (png_charp)text;
228 }
229 png_set_text(fPngPtr, fInfoPtr, png_texts.data(), png_texts.size());
230 }
231
Matt Sarettc367d032017-05-05 11:13:26 -0400232 return true;
Matt Sarett687b6562017-03-21 10:06:45 -0400233}
234
Brian Osmanb62f50c2018-07-12 14:44:27 -0400235static transform_scanline_proc choose_proc(const SkImageInfo& info) {
Matt Sarett84014f02017-01-10 11:28:54 -0500236 switch (info.colorType()) {
Mike Kleinb70990e2019-02-28 10:03:27 -0600237 case kUnknown_SkColorType:
238 break;
239
Brian Osman9f1e06a2021-08-10 14:39:18 -0400240 // TODO: I don't think this can just use kRGBA's procs.
241 // kPremul is especially tricky here, since it's presumably TF⁻¹(rgb * a),
242 // so to get at unpremul rgb we'd need to undo the transfer function first.
243 case kSRGBA_8888_SkColorType: return nullptr;
244
Matt Sarett84014f02017-01-10 11:28:54 -0500245 case kRGBA_8888_SkColorType:
246 switch (info.alphaType()) {
247 case kOpaque_SkAlphaType:
248 return transform_scanline_RGBX;
249 case kUnpremul_SkAlphaType:
250 return transform_scanline_memcpy;
251 case kPremul_SkAlphaType:
Brian Osmanb62f50c2018-07-12 14:44:27 -0400252 return transform_scanline_rgbA;
Matt Sarett84014f02017-01-10 11:28:54 -0500253 default:
254 SkASSERT(false);
255 return nullptr;
256 }
257 case kBGRA_8888_SkColorType:
258 switch (info.alphaType()) {
259 case kOpaque_SkAlphaType:
260 return transform_scanline_BGRX;
261 case kUnpremul_SkAlphaType:
262 return transform_scanline_BGRA;
263 case kPremul_SkAlphaType:
Brian Osmanb62f50c2018-07-12 14:44:27 -0400264 return transform_scanline_bgrA;
Matt Sarett84014f02017-01-10 11:28:54 -0500265 default:
266 SkASSERT(false);
267 return nullptr;
268 }
269 case kRGB_565_SkColorType:
270 return transform_scanline_565;
Mike Kleinac568a92018-01-25 09:09:32 -0500271 case kRGB_888x_SkColorType:
Mike Klein08883cd2018-11-02 13:03:57 -0400272 return transform_scanline_RGBX;
Matt Sarett84014f02017-01-10 11:28:54 -0500273 case kARGB_4444_SkColorType:
274 switch (info.alphaType()) {
275 case kOpaque_SkAlphaType:
276 return transform_scanline_444;
277 case kPremul_SkAlphaType:
Matt Sarett84014f02017-01-10 11:28:54 -0500278 return transform_scanline_4444;
279 default:
280 SkASSERT(false);
281 return nullptr;
282 }
Matt Sarett84014f02017-01-10 11:28:54 -0500283 case kGray_8_SkColorType:
284 return transform_scanline_memcpy;
Mike Kleinb70990e2019-02-28 10:03:27 -0600285
286 case kRGBA_F16Norm_SkColorType:
Matt Sarett1da27ef2017-01-19 17:14:07 -0500287 case kRGBA_F16_SkColorType:
288 switch (info.alphaType()) {
289 case kOpaque_SkAlphaType:
290 case kUnpremul_SkAlphaType:
291 return transform_scanline_F16;
292 case kPremul_SkAlphaType:
293 return transform_scanline_F16_premul;
294 default:
295 SkASSERT(false);
296 return nullptr;
297 }
Mike Klein37854712018-06-26 11:43:06 -0400298 case kRGBA_F32_SkColorType:
299 switch (info.alphaType()) {
300 case kOpaque_SkAlphaType:
301 case kUnpremul_SkAlphaType:
302 return transform_scanline_F32;
303 case kPremul_SkAlphaType:
304 return transform_scanline_F32_premul;
305 default:
306 SkASSERT(false);
307 return nullptr;
308 }
Mike Kleinac568a92018-01-25 09:09:32 -0500309 case kRGBA_1010102_SkColorType:
310 switch (info.alphaType()) {
311 case kOpaque_SkAlphaType:
312 case kUnpremul_SkAlphaType:
313 return transform_scanline_1010102;
314 case kPremul_SkAlphaType:
315 return transform_scanline_1010102_premul;
316 default:
317 SkASSERT(false);
318 return nullptr;
319 }
Mike Kleinf7eb0542020-02-11 12:19:08 -0600320 case kBGRA_1010102_SkColorType:
321 switch (info.alphaType()) {
322 case kOpaque_SkAlphaType:
323 case kUnpremul_SkAlphaType:
324 return transform_scanline_bgra_1010102;
325 case kPremul_SkAlphaType:
326 return transform_scanline_bgra_1010102_premul;
327 default:
328 SkASSERT(false);
329 return nullptr;
330 }
331 case kRGB_101010x_SkColorType: return transform_scanline_101010x;
332 case kBGR_101010x_SkColorType: return transform_scanline_bgr_101010x;
333
Mike Reedd6cb11e2017-11-30 15:33:04 -0500334 case kAlpha_8_SkColorType:
335 return transform_scanline_A8_to_GrayAlpha;
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400336 case kR8G8_unorm_SkColorType:
337 case kR16G16_unorm_SkColorType:
338 case kR16G16_float_SkColorType:
339 case kA16_unorm_SkColorType:
340 case kA16_float_SkColorType:
341 case kR16G16B16A16_unorm_SkColorType:
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400342 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 }
Mike Kleinb70990e2019-02-28 10:03:27 -0600344 SkASSERT(false);
345 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346}
347
Matt Sarett1950e0a2017-06-12 16:17:30 -0400348static void set_icc(png_structp png_ptr, png_infop info_ptr, const SkImageInfo& info) {
349 sk_sp<SkData> icc = icc_from_color_space(info);
Matt Sarettc367d032017-05-05 11:13:26 -0400350 if (!icc) {
351 return;
352 }
353
354#if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
355 const char* name = "Skia";
356 png_const_bytep iccPtr = icc->bytes();
357#else
358 SkString str("Skia");
359 char* name = str.writable_str();
360 png_charp iccPtr = (png_charp) icc->writable_data();
361#endif
362 png_set_iCCP(png_ptr, info_ptr, name, 0, iccPtr, icc->size());
363}
364
Matt Sarett1950e0a2017-06-12 16:17:30 -0400365bool SkPngEncoderMgr::setColorSpace(const SkImageInfo& info) {
Matt Sarettc367d032017-05-05 11:13:26 -0400366 if (setjmp(png_jmpbuf(fPngPtr))) {
367 return false;
368 }
369
Matt Sarett1950e0a2017-06-12 16:17:30 -0400370 if (info.colorSpace() && info.colorSpace()->isSRGB()) {
371 png_set_sRGB(fPngPtr, fInfoPtr, PNG_sRGB_INTENT_PERCEPTUAL);
372 } else {
373 set_icc(fPngPtr, fInfoPtr, info);
Matt Sarett0e032be2017-03-15 17:50:08 -0400374 }
375
Matt Sarettc367d032017-05-05 11:13:26 -0400376 return true;
377}
378
379bool SkPngEncoderMgr::writeInfo(const SkImageInfo& srcInfo) {
380 if (setjmp(png_jmpbuf(fPngPtr))) {
381 return false;
382 }
383
384 png_write_info(fPngPtr, fInfoPtr);
385 if (kRGBA_F16_SkColorType == srcInfo.colorType() &&
386 kOpaque_SkAlphaType == srcInfo.alphaType())
387 {
Matt Sarett1da27ef2017-01-19 17:14:07 -0500388 // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng
389 // to skip the alpha channel.
Matt Sarettc367d032017-05-05 11:13:26 -0400390 png_set_filler(fPngPtr, 0, PNG_FILLER_AFTER);
Matt Sarett1da27ef2017-01-19 17:14:07 -0500391 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 return true;
394}
395
Brian Osmanb62f50c2018-07-12 14:44:27 -0400396void SkPngEncoderMgr::chooseProc(const SkImageInfo& srcInfo) {
397 fProc = choose_proc(srcInfo);
Matt Sarettc367d032017-05-05 11:13:26 -0400398}
399
Matt Sarett6a4dc662017-05-11 09:32:59 -0400400std::unique_ptr<SkEncoder> SkPngEncoder::Make(SkWStream* dst, const SkPixmap& src,
401 const Options& options) {
Brian Osmane1adc3a2018-06-04 09:21:17 -0400402 if (!SkPixmapIsValid(src)) {
Matt Sarettc367d032017-05-05 11:13:26 -0400403 return nullptr;
404 }
405
406 std::unique_ptr<SkPngEncoderMgr> encoderMgr = SkPngEncoderMgr::Make(dst);
407 if (!encoderMgr) {
408 return nullptr;
409 }
410
Matt Sarettbe4c9b02017-05-08 12:11:44 -0400411 if (!encoderMgr->setHeader(src.info(), options)) {
Matt Sarettc367d032017-05-05 11:13:26 -0400412 return nullptr;
413 }
414
Matt Sarett1950e0a2017-06-12 16:17:30 -0400415 if (!encoderMgr->setColorSpace(src.info())) {
Matt Sarettc367d032017-05-05 11:13:26 -0400416 return nullptr;
417 }
418
419 if (!encoderMgr->writeInfo(src.info())) {
420 return nullptr;
421 }
422
Brian Osmanb62f50c2018-07-12 14:44:27 -0400423 encoderMgr->chooseProc(src.info());
Matt Sarettc367d032017-05-05 11:13:26 -0400424
425 return std::unique_ptr<SkPngEncoder>(new SkPngEncoder(std::move(encoderMgr), src));
426}
427
428SkPngEncoder::SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr, const SkPixmap& src)
429 : INHERITED(src, encoderMgr->pngBytesPerPixel() * src.width())
430 , fEncoderMgr(std::move(encoderMgr))
431{}
432
433SkPngEncoder::~SkPngEncoder() {}
434
435bool SkPngEncoder::onEncodeRows(int numRows) {
436 if (setjmp(png_jmpbuf(fEncoderMgr->pngPtr()))) {
437 return false;
438 }
439
440 const void* srcRow = fSrc.addr(0, fCurrRow);
441 for (int y = 0; y < numRows; y++) {
Mike Klein33c36e02019-10-14 10:28:25 -0500442 sk_msan_assert_initialized(srcRow,
443 (const uint8_t*)srcRow + (fSrc.width() << fSrc.shiftPerPixel()));
Mike Klein0c904fa2018-11-02 12:24:15 -0400444 fEncoderMgr->proc()((char*)fStorage.get(),
445 (const char*)srcRow,
446 fSrc.width(),
447 SkColorTypeBytesPerPixel(fSrc.colorType()));
Matt Sarettc367d032017-05-05 11:13:26 -0400448
449 png_bytep rowPtr = (png_bytep) fStorage.get();
450 png_write_rows(fEncoderMgr->pngPtr(), &rowPtr, 1);
451 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
452 }
453
454 fCurrRow += numRows;
455 if (fCurrRow == fSrc.height()) {
456 png_write_end(fEncoderMgr->pngPtr(), fEncoderMgr->infoPtr());
457 }
458
459 return true;
460}
461
462bool SkPngEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
463 auto encoder = SkPngEncoder::Make(dst, src, options);
464 return encoder.get() && encoder->encodeRows(src.height());
465}
Mike Kleinab737e42019-05-15 21:58:15 +0000466
467#endif