blob: 3fb10dde2a73cc8b1722db72164c1fe4cf0cb281 [file] [log] [blame]
Guy Schalnat0d580581995-07-20 02:43:20 -05001
Andreas Dilger47a0c421997-05-16 02:46:07 -05002/* pngwutil.c - utilities to write a PNG file
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -06003 *
John Bowler5d567862011-12-24 09:12:00 -06004 * Last changed in libpng 1.6.0 [(PENDING RELEASE)]
Glenn Randers-Pehrson1531bd62012-01-01 14:45:04 -06005 * Copyright (c) 1998-2012 Glenn Randers-Pehrson
Glenn Randers-Pehrsond4366722000-06-04 14:29:29 -05006 * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
7 * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
Glenn Randers-Pehrson3e61d792009-06-24 09:31:28 -05008 *
Glenn Randers-Pehrsonbfbf8652009-06-26 21:46:52 -05009 * This code is released under the libpng license.
Glenn Randers-Pehrsonc332bbc2009-06-25 13:43:50 -050010 * For conditions of distribution and use, see the disclaimer
Glenn Randers-Pehrson037023b2009-06-24 10:27:36 -050011 * and license in png.h
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -060012 */
Andreas Dilger47a0c421997-05-16 02:46:07 -050013
Glenn Randers-Pehrson9c90d7f2009-07-19 13:11:25 -050014#include "pngpriv.h"
Guy Schalnat0d580581995-07-20 02:43:20 -050015
Glenn Randers-Pehrsonc3cd22b2010-03-08 21:10:25 -060016#ifdef PNG_WRITE_SUPPORTED
17
Glenn Randers-Pehrson34713ce2010-04-28 07:49:28 -050018#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -050019/* Place a 32-bit number into a buffer in PNG byte order. We work
20 * with unsigned numbers for convenience, although one supported
21 * ancillary chunk uses signed (two's complement) numbers.
22 */
Glenn Randers-Pehrson9c3ab682006-02-20 22:09:05 -060023void PNGAPI
Guy Schalnat6d764711995-12-19 03:22:19 -060024png_save_uint_32(png_bytep buf, png_uint_32 i)
Guy Schalnat0d580581995-07-20 02:43:20 -050025{
26 buf[0] = (png_byte)((i >> 24) & 0xff);
27 buf[1] = (png_byte)((i >> 16) & 0xff);
28 buf[2] = (png_byte)((i >> 8) & 0xff);
29 buf[3] = (png_byte)(i & 0xff);
30}
31
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -050032#ifdef PNG_SAVE_INT_32_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -050033/* The png_save_int_32 function assumes integers are stored in two's
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -060034 * complement format. If this isn't the case, then this routine needs to
Glenn Randers-Pehrson31aee0d2010-07-29 17:39:14 -050035 * be modified to write data in two's complement format. Note that,
36 * the following works correctly even if png_int_32 has more than 32 bits
37 * (compare the more complex code required on read for sign extention.)
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -060038 */
Glenn Randers-Pehrson9c3ab682006-02-20 22:09:05 -060039void PNGAPI
Andreas Dilger47a0c421997-05-16 02:46:07 -050040png_save_int_32(png_bytep buf, png_int_32 i)
41{
42 buf[0] = (png_byte)((i >> 24) & 0xff);
43 buf[1] = (png_byte)((i >> 16) & 0xff);
44 buf[2] = (png_byte)((i >> 8) & 0xff);
45 buf[3] = (png_byte)(i & 0xff);
46}
Glenn Randers-Pehrson9c90d7f2009-07-19 13:11:25 -050047#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -050048
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -060049/* Place a 16-bit number into a buffer in PNG byte order.
50 * The parameter is declared unsigned int, not png_uint_16,
51 * just to avoid potential problems on pre-ANSI C compilers.
52 */
Glenn Randers-Pehrson9c3ab682006-02-20 22:09:05 -060053void PNGAPI
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -060054png_save_uint_16(png_bytep buf, unsigned int i)
Guy Schalnat0d580581995-07-20 02:43:20 -050055{
56 buf[0] = (png_byte)((i >> 8) & 0xff);
57 buf[1] = (png_byte)(i & 0xff);
58}
Glenn Randers-Pehrson34713ce2010-04-28 07:49:28 -050059#endif
Guy Schalnat0d580581995-07-20 02:43:20 -050060
Glenn Randers-Pehrson6bc53be2006-06-16 07:52:03 -050061/* Simple function to write the signature. If we have already written
62 * the magic bytes of the signature, or more likely, the PNG stream is
63 * being embedded into another stream and doesn't need its own signature,
64 * we should call png_set_sig_bytes() to tell libpng how many of the
65 * bytes have already been written.
66 */
Glenn Randers-Pehrson9c90d7f2009-07-19 13:11:25 -050067void PNGAPI
John Bowler5d567862011-12-24 09:12:00 -060068png_write_sig(png_structrp png_ptr)
Glenn Randers-Pehrson6bc53be2006-06-16 07:52:03 -050069{
70 png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -050071
Glenn Randers-Pehrson9c90d7f2009-07-19 13:11:25 -050072#ifdef PNG_IO_STATE_SUPPORTED
73 /* Inform the I/O callback that the signature is being written */
74 png_ptr->io_state = PNG_IO_WRITING | PNG_IO_SIGNATURE;
75#endif
76
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -050077 /* Write the rest of the 8 byte signature */
Glenn Randers-Pehrson6bc53be2006-06-16 07:52:03 -050078 png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes],
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -050079 (png_size_t)(8 - png_ptr->sig_bytes));
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -050080
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -050081 if (png_ptr->sig_bytes < 3)
Glenn Randers-Pehrson6bc53be2006-06-16 07:52:03 -050082 png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
83}
84
Andreas Dilger47a0c421997-05-16 02:46:07 -050085/* Write the start of a PNG chunk. The type is the chunk type.
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -060086 * The total_length is the sum of the lengths of all the data you will be
87 * passing in png_write_chunk_data().
88 */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -050089static void
John Bowler5d567862011-12-24 09:12:00 -060090png_write_chunk_header(png_structrp png_ptr, png_uint_32 chunk_name,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -060091 png_uint_32 length)
Guy Schalnat0d580581995-07-20 02:43:20 -050092{
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -050093 png_byte buf[8];
Andreas Dilger47a0c421997-05-16 02:46:07 -050094
Glenn Randers-Pehrson5b3b54e2011-10-12 06:36:56 -050095#if defined(PNG_DEBUG) && (PNG_DEBUG > 0)
Glenn Randers-Pehrsonba55c072011-10-11 21:21:37 -050096 PNG_CSTRING_FROM_CHUNK(buf, chunk_name);
97 png_debug2(0, "Writing %s chunk, length = %lu", buf, (unsigned long)length);
98#endif
Glenn Randers-Pehrsonda009802009-08-15 13:25:47 -050099
100 if (png_ptr == NULL)
101 return;
102
Glenn Randers-Pehrson9c90d7f2009-07-19 13:11:25 -0500103#ifdef PNG_IO_STATE_SUPPORTED
104 /* Inform the I/O callback that the chunk header is being written.
105 * PNG_IO_CHUNK_HDR requires a single I/O call.
106 */
107 png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_HDR;
108#endif
109
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500110 /* Write the length and the chunk name */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500111 png_save_uint_32(buf, length);
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500112 png_save_uint_32(buf + 4, chunk_name);
113 png_write_data(png_ptr, buf, 8);
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -0500114
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500115 /* Put the chunk name into png_ptr->chunk_name */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500116 png_ptr->chunk_name = chunk_name;
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -0500117
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500118 /* Reset the crc and run it over the chunk name */
Guy Schalnat0d580581995-07-20 02:43:20 -0500119 png_reset_crc(png_ptr);
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -0500120
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500121 png_calculate_crc(png_ptr, buf + 4, 4);
Glenn Randers-Pehrson9c90d7f2009-07-19 13:11:25 -0500122
123#ifdef PNG_IO_STATE_SUPPORTED
124 /* Inform the I/O callback that chunk data will (possibly) be written.
125 * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls.
126 */
127 png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_DATA;
128#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500129}
130
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500131void PNGAPI
John Bowler5d567862011-12-24 09:12:00 -0600132png_write_chunk_start(png_structrp png_ptr, png_const_bytep chunk_string,
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500133 png_uint_32 length)
134{
135 png_write_chunk_header(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), length);
136}
137
138/* Write the data of a PNG chunk started with png_write_chunk_header().
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600139 * Note that multiple calls to this function are allowed, and that the
140 * sum of the lengths from these calls *must* add up to the total_length
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500141 * given to png_write_chunk_header().
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600142 */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -0500143void PNGAPI
John Bowler5d567862011-12-24 09:12:00 -0600144png_write_chunk_data(png_structrp png_ptr, png_const_bytep data,
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -0500145 png_size_t length)
Guy Schalnat0d580581995-07-20 02:43:20 -0500146{
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500147 /* Write the data, and run the CRC over it */
148 if (png_ptr == NULL)
149 return;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500150
Andreas Dilger47a0c421997-05-16 02:46:07 -0500151 if (data != NULL && length > 0)
Guy Schalnat0d580581995-07-20 02:43:20 -0500152 {
Guy Schalnat6d764711995-12-19 03:22:19 -0600153 png_write_data(png_ptr, data, length);
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -0500154
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500155 /* Update the CRC after writing the data,
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -0500156 * in case that the user I/O routine alters it.
157 */
158 png_calculate_crc(png_ptr, data, length);
Guy Schalnat0d580581995-07-20 02:43:20 -0500159 }
160}
161
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500162/* Finish a chunk started with png_write_chunk_header(). */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -0500163void PNGAPI
John Bowler5d567862011-12-24 09:12:00 -0600164png_write_chunk_end(png_structrp png_ptr)
Guy Schalnat0d580581995-07-20 02:43:20 -0500165{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500166 png_byte buf[4];
167
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -0500168 if (png_ptr == NULL) return;
169
Glenn Randers-Pehrson9c90d7f2009-07-19 13:11:25 -0500170#ifdef PNG_IO_STATE_SUPPORTED
171 /* Inform the I/O callback that the chunk CRC is being written.
172 * PNG_IO_CHUNK_CRC requires a single I/O function call.
173 */
174 png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_CRC;
175#endif
176
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500177 /* Write the crc in a single operation */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500178 png_save_uint_32(buf, png_ptr->crc);
Andreas Dilger47a0c421997-05-16 02:46:07 -0500179
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -0500180 png_write_data(png_ptr, buf, (png_size_t)4);
Guy Schalnat0d580581995-07-20 02:43:20 -0500181}
182
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500183/* Write a PNG chunk all at once. The type is an array of ASCII characters
184 * representing the chunk name. The array must be at least 4 bytes in
185 * length, and does not need to be null terminated. To be safe, pass the
186 * pre-defined chunk names here, and if you need a new one, define it
187 * where the others are defined. The length is the length of the data.
188 * All the data must be present. If that is not possible, use the
189 * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()
190 * functions instead.
191 */
192static void
John Bowler5d567862011-12-24 09:12:00 -0600193png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name,
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500194 png_const_bytep data, png_size_t length)
195{
196 if (png_ptr == NULL)
197 return;
198
199 /* On 64 bit architectures 'length' may not fit in a png_uint_32. */
John Bowlerb5d00512012-03-09 09:15:18 -0600200 if (length > PNG_UINT_31_MAX)
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500201 png_error(png_ptr, "length exceeds PNG maxima");
202
203 png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length);
204 png_write_chunk_data(png_ptr, data, length);
205 png_write_chunk_end(png_ptr);
206}
207
208/* This is the API that calls the internal function above. */
209void PNGAPI
John Bowler5d567862011-12-24 09:12:00 -0600210png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string,
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500211 png_const_bytep data, png_size_t length)
212{
213 png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data,
214 length);
215}
216
John Bowler42a2b552012-03-05 20:57:40 -0600217/* This is used below to find the size of an image to pass to png_deflate_claim,
218 * so it only needs to be accurate if the size is less than 16384 bytes (the
219 * point at which a lower LZ window size can be used.)
220 */
221static png_alloc_size_t
222png_image_size(png_structrp png_ptr)
John Bowlerc5bef942011-05-05 17:35:39 -0500223{
John Bowler42a2b552012-03-05 20:57:40 -0600224 /* Only return sizes up to the maximum of a png_uint_32, do this by limiting
225 * the width and height used to 15 bits.
226 */
227 png_uint_32 h = png_ptr->height;
228
229 if (png_ptr->rowbytes < 32768 && h < 32768)
John Bowlerc5bef942011-05-05 17:35:39 -0500230 {
John Bowler42a2b552012-03-05 20:57:40 -0600231 if (png_ptr->interlaced)
John Bowlerc5bef942011-05-05 17:35:39 -0500232 {
John Bowler42a2b552012-03-05 20:57:40 -0600233 /* Interlacing makes the image larger because of the replication of
234 * both the filter byte and the padding to a byte boundary.
235 */
236 png_uint_32 w = png_ptr->width;
237 unsigned int pd = png_ptr->pixel_depth;
238 png_alloc_size_t cbBase;
239 int pass;
John Bowlerc5bef942011-05-05 17:35:39 -0500240
John Bowler42a2b552012-03-05 20:57:40 -0600241 for (cbBase=0, pass=0; pass<=6; ++pass)
John Bowlerc5bef942011-05-05 17:35:39 -0500242 {
John Bowler42a2b552012-03-05 20:57:40 -0600243 png_uint_32 pw = PNG_PASS_COLS(w, pass);
244
245 if (pw > 0)
246 cbBase += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass);
John Bowlerc5bef942011-05-05 17:35:39 -0500247 }
248
John Bowler42a2b552012-03-05 20:57:40 -0600249 return cbBase;
John Bowlerc5bef942011-05-05 17:35:39 -0500250 }
251
John Bowler42a2b552012-03-05 20:57:40 -0600252 else
253 return (png_ptr->rowbytes+1) * h;
254 }
255
256 else
257 return 0xffffffffU;
258}
259
John Bowlerb5d00512012-03-09 09:15:18 -0600260#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
261 /* This is the code to hack the first two bytes of the deflate stream (the
262 * deflate header) to correct the windowBits value to match the actual data
263 * size. Note that the second argument is the *uncompressed* size but the
264 * first argument is the *compressed* data (and it must be deflate
265 * compressed.)
266 */
John Bowler42a2b552012-03-05 20:57:40 -0600267static void
John Bowlerb5d00512012-03-09 09:15:18 -0600268optimize_cmf(png_bytep data, png_alloc_size_t data_size)
269{
270 /* Optimize the CMF field in the zlib stream. The resultant zlib stream is
271 * still compliant to the stream specification.
272 */
273 if (data_size <= 16384) /* else windowBits must be 15 */
274 {
275 unsigned int z_cmf = data[0]; /* zlib compression method and flags */
276
277 if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
278 {
279 unsigned int z_cinfo;
280 unsigned int half_z_window_size;
281
282 z_cinfo = z_cmf >> 4;
283 half_z_window_size = 1U << (z_cinfo + 7);
284
285 if (data_size <= half_z_window_size) /* else no change */
286 {
287 unsigned int tmp;
288
289 do
290 {
291 half_z_window_size >>= 1;
292 --z_cinfo;
293 }
294 while (z_cinfo > 0 && data_size <= half_z_window_size);
295
296 z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
297
298 data[0] = (png_byte)z_cmf;
299 tmp = data[1] & 0xe0;
300 tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
301 data[1] = (png_byte)tmp;
302 }
303 }
304 }
305}
306#else
307# define optimize_cmf(dp,dl) ((void)0)
308#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */
309
310/* Initialize the compressor for the appropriate type of compression. */
311static int
312png_deflate_claim(png_structrp png_ptr, png_uint_32 owner,
John Bowler42a2b552012-03-05 20:57:40 -0600313 png_alloc_size_t data_size)
314{
John Bowlerb5d00512012-03-09 09:15:18 -0600315 if (png_ptr->zowner != 0)
316 {
317 char msg[64];
318
319 PNG_STRING_FROM_CHUNK(msg, owner);
320 msg[4] = ':';
321 msg[5] = ' ';
322 PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner);
323 /* So the message that results is "<chunk> using zstream"; this is an
324 * internal error, but is very useful for debugging. i18n requirements
325 * are minimal.
326 */
327 (void)png_safecat(msg, sizeof msg, 10, " using zstream");
John Bowler921648a2012-03-28 23:36:12 -0500328# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC
John Bowlerb5d00512012-03-09 09:15:18 -0600329 png_warning(png_ptr, msg);
330
331 /* Attempt sane error recovery */
332 if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */
333 {
334 png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT");
335 return Z_STREAM_ERROR;
336 }
337
338 png_ptr->zowner = 0;
339# else
340 png_error(png_ptr, msg);
341# endif
342 }
343
John Bowler42a2b552012-03-05 20:57:40 -0600344 {
345 int level = png_ptr->zlib_level;
346 int method = png_ptr->zlib_method;
347 int windowBits = png_ptr->zlib_window_bits;
348 int memLevel = png_ptr->zlib_mem_level;
349 int strategy; /* set below */
350 int ret; /* zlib return code */
351
John Bowlerb5d00512012-03-09 09:15:18 -0600352 if (owner == png_IDAT)
John Bowler42a2b552012-03-05 20:57:40 -0600353 {
354 if (png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)
355 strategy = png_ptr->zlib_strategy;
356
357 else if (png_ptr->do_filter != PNG_FILTER_NONE)
358 strategy = Z_FILTERED;
359
360 else
361 strategy = Z_DEFAULT_STRATEGY;
362 }
363
364 else
365 {
366# ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
367 level = png_ptr->zlib_text_level;
368 method = png_ptr->zlib_text_method;
369 windowBits = png_ptr->zlib_text_window_bits;
370 memLevel = png_ptr->zlib_text_mem_level;
371 strategy = png_ptr->zlib_text_strategy;
372# else
373 /* If customization is not supported the values all come from the
374 * IDAT values except for the strategy, which is fixed to the
375 * default. (This is the pre-1.6.0 behavior too, although it was
376 * implemented in a very different way.)
377 */
378 strategy = Z_DEFAULT_STRATEGY;
379# endif
380 }
381
382 /* Adjust 'windowBits' down if larger than 'data_size'; to stop this
John Bowler6225b0e2012-03-05 21:26:57 -0600383 * happening just pass 32768 as the data_size parameter. Notice that zlib
384 * requires an extra 262 bytes in the window in addition to the data to be
385 * able to see the whole of the data, so if data_size+262 takes us to the
386 * next windowBits size we need to fix up the value later. (Because even
387 * though deflate needs the extra window, inflate does not!)
John Bowler42a2b552012-03-05 20:57:40 -0600388 */
John Bowlerb5d00512012-03-09 09:15:18 -0600389 if (data_size <= 16384)
John Bowler42a2b552012-03-05 20:57:40 -0600390 {
John Bowlerb5d00512012-03-09 09:15:18 -0600391 /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to
392 * work round a Microsoft Visual C misbehavior which, contrary to C-90,
393 * widens the result of the following shift to 64-bits if (and,
394 * apparently, only if) it is used in a test.
395 */
396 unsigned int half_window_size = 1U << (windowBits-1);
John Bowler42a2b552012-03-05 20:57:40 -0600397
John Bowlerb5d00512012-03-09 09:15:18 -0600398 while (data_size + 262 <= half_window_size)
John Bowler6225b0e2012-03-05 21:26:57 -0600399 {
John Bowlerb5d00512012-03-09 09:15:18 -0600400 half_window_size >>= 1;
401 --windowBits;
John Bowler6225b0e2012-03-05 21:26:57 -0600402 }
John Bowler6225b0e2012-03-05 21:26:57 -0600403 }
404
John Bowler42a2b552012-03-05 20:57:40 -0600405 /* Check against the previous initialized values, if any. */
406 if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) &&
407 (png_ptr->zlib_set_level != level ||
408 png_ptr->zlib_set_method != method ||
409 png_ptr->zlib_set_window_bits != windowBits ||
410 png_ptr->zlib_set_mem_level != memLevel ||
411 png_ptr->zlib_set_strategy != strategy))
412 {
413 if (deflateEnd(&png_ptr->zstream) != Z_OK)
414 png_warning(png_ptr, "deflateEnd failed (ignored)");
415
416 png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED;
417 }
418
John Bowlerb5d00512012-03-09 09:15:18 -0600419 /* For safety clear out the input and output pointers (currently zlib
420 * doesn't use them on Init, but it might in the future).
421 */
422 png_ptr->zstream.next_in = NULL;
423 png_ptr->zstream.avail_in = 0;
424 png_ptr->zstream.next_out = NULL;
425 png_ptr->zstream.avail_out = 0;
426
John Bowler42a2b552012-03-05 20:57:40 -0600427 /* Now initialize if required, setting the new parameters, otherwise just
428 * to a simple reset to the previous parameters.
429 */
430 if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)
431 ret = deflateReset(&png_ptr->zstream);
432
433 else
434 {
435 ret = deflateInit2(&png_ptr->zstream, level, method, windowBits,
436 memLevel, strategy);
437
438 if (ret == Z_OK)
439 png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED;
440 }
441
442 /* The return code is from either deflateReset or deflateInit2; they have
443 * pretty much the same set of error codes.
444 */
445 if (ret == Z_OK)
John Bowlerb5d00512012-03-09 09:15:18 -0600446 png_ptr->zowner = owner;
John Bowler42a2b552012-03-05 20:57:40 -0600447
448 else
John Bowlerb5d00512012-03-09 09:15:18 -0600449 png_zstream_error(png_ptr, ret);
John Bowler42a2b552012-03-05 20:57:40 -0600450
John Bowlerb5d00512012-03-09 09:15:18 -0600451 return ret;
John Bowlerc5bef942011-05-05 17:35:39 -0500452 }
John Bowlerc5bef942011-05-05 17:35:39 -0500453}
454
Glenn Randers-Pehrson205483d2011-04-01 12:33:42 -0500455#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500456/* This pair of functions encapsulates the operation of (a) compressing a
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600457 * text string, and (b) issuing it later as a series of chunk data writes.
458 * The compression_state structure is shared context for these functions
John Bowlerb5d00512012-03-09 09:15:18 -0600459 * set up by the caller to allow access to the relevant local variables.
460 *
461 * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size
462 * temporary buffers. From 1.6.0 it is retained in png_struct so that it will
463 * be correctly freed in the event of a write error (previous implementations
464 * just leaked memory.)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600465 */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600466typedef struct
467{
John Bowlerb5d00512012-03-09 09:15:18 -0600468 png_const_bytep input; /* The uncompressed input data */
469 png_alloc_size_t input_len; /* Its length */
470 png_uint_32 output_len; /* Final compressed length */
471 png_byte output[1024]; /* First block of output */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600472} compression_state;
473
John Bowlerb5d00512012-03-09 09:15:18 -0600474static void
475png_text_compress_init(compression_state *comp, png_const_bytep input,
476 png_alloc_size_t input_len)
477{
478 comp->input = input;
479 comp->input_len = input_len;
480 comp->output_len = 0;
481}
482
483void /* PRIVATE */
484png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp)
485{
486 png_compression_bufferp list = *listp;
487
488 if (list != NULL)
489 {
490 *listp = NULL;
491
492 do
493 {
494 png_compression_bufferp next = list->next;
495
496 png_free(png_ptr, list);
497 list = next;
498 }
499 while (list != NULL);
500 }
501}
502
503/* Compress the data in the compression state input */
504static int
505png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name,
506 compression_state *comp, png_uint_32 prefix_len)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600507{
508 int ret;
509
John Bowlerb5d00512012-03-09 09:15:18 -0600510 /* To find the length of the output it is necessary to first compress the
511 * input, the result is buffered rather than using the two-pass algorithm
512 * that is used on the inflate side; deflate is assumed to be slower and a
513 * PNG writer is assumed to have more memory available than a PNG reader.
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600514 *
John Bowlerb5d00512012-03-09 09:15:18 -0600515 * IMPLEMENTATION NOTE: the zlib API deflateBound() can be used to find an
516 * upper limit on the output size, but it is always bigger than the input
517 * size so it is likely to be more efficient to use this linked-list
518 * approach.
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600519 */
John Bowlerb5d00512012-03-09 09:15:18 -0600520 ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len);
Glenn Randers-Pehrson6b3d50b2011-03-31 20:14:29 -0500521
John Bowlerb5d00512012-03-09 09:15:18 -0600522 if (ret != Z_OK)
523 return ret;
Glenn Randers-Pehrson6b3d50b2011-03-31 20:14:29 -0500524
John Bowlerb5d00512012-03-09 09:15:18 -0600525 /* Set up the compression buffers, we need a loop here to avoid overflowing a
526 * uInt. Use ZLIB_IO_MAX to limit the input. The output is always limited
527 * by the output buffer size, so there is no need to check that. Since this
528 * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits
529 * in size.
530 */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600531 {
John Bowlerb5d00512012-03-09 09:15:18 -0600532 png_compression_bufferp *end = &png_ptr->zbuffer_list;
533 png_alloc_size_t input_len = comp->input_len; /* may be zero! */
534 png_uint_32 output_len;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500535
John Bowlerb5d00512012-03-09 09:15:18 -0600536 /* zlib updates these for us: */
537 png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input);
538 png_ptr->zstream.avail_in = 0; /* Set below */
539 png_ptr->zstream.next_out = comp->output;
540 png_ptr->zstream.avail_out = sizeof comp->output;
541
542 output_len = png_ptr->zstream.avail_out;
543
544 do
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600545 {
John Bowlerb5d00512012-03-09 09:15:18 -0600546 uInt avail_in = ZLIB_IO_MAX;
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -0500547
John Bowlerb5d00512012-03-09 09:15:18 -0600548 if (avail_in > input_len)
549 avail_in = (uInt)input_len;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500550
John Bowlerb5d00512012-03-09 09:15:18 -0600551 input_len -= avail_in;
552
553 png_ptr->zstream.avail_in = avail_in;
554
555 if (png_ptr->zstream.avail_out == 0)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600556 {
John Bowlerb5d00512012-03-09 09:15:18 -0600557 png_compression_buffer *next;
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600558
John Bowlerb5d00512012-03-09 09:15:18 -0600559 /* Chunk data is limited to 2^31 bytes in length, so the prefix
560 * length must be counted here.
561 */
562 if (output_len + prefix_len > PNG_UINT_31_MAX)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600563 {
John Bowlerb5d00512012-03-09 09:15:18 -0600564 ret = Z_MEM_ERROR;
565 break;
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600566 }
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600567
John Bowlerb5d00512012-03-09 09:15:18 -0600568 /* Need a new (malloc'ed) buffer, but there may be one present
569 * already.
570 */
571 next = *end;
572 if (next == NULL)
Glenn Randers-Pehrson104622b2000-05-29 08:58:03 -0500573 {
John Bowlerb5d00512012-03-09 09:15:18 -0600574 next = png_voidcast(png_compression_bufferp, png_malloc_base
575 (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
Glenn Randers-Pehrson104622b2000-05-29 08:58:03 -0500576
John Bowlerb5d00512012-03-09 09:15:18 -0600577 if (next == NULL)
Glenn Randers-Pehrson104622b2000-05-29 08:58:03 -0500578 {
John Bowlerb5d00512012-03-09 09:15:18 -0600579 ret = Z_MEM_ERROR;
580 break;
Glenn Randers-Pehrson104622b2000-05-29 08:58:03 -0500581 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500582
John Bowlerb5d00512012-03-09 09:15:18 -0600583 /* Link in this buffer (so that it will be freed later) */
584 next->next = NULL;
585 *end = next;
Glenn Randers-Pehrson104622b2000-05-29 08:58:03 -0500586 }
587
John Bowlerb5d00512012-03-09 09:15:18 -0600588 png_ptr->zstream.next_out = next->output;
589 png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
590 output_len += png_ptr->zstream.avail_out;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500591
John Bowlerb5d00512012-03-09 09:15:18 -0600592 /* Move 'end' to the next buffer pointer. */
593 end = &next->next;
Glenn Randers-Pehrson104622b2000-05-29 08:58:03 -0500594 }
John Bowlerb5d00512012-03-09 09:15:18 -0600595
596 /* Compress the data */
597 ret = deflate(&png_ptr->zstream,
598 input_len > 0 ? Z_NO_FLUSH : Z_FINISH);
599
600 /* Claw back input data that was not consumed (because avail_in is
601 * reset above every time round the loop).
602 */
603 input_len += png_ptr->zstream.avail_in;
604 png_ptr->zstream.avail_in = 0; /* safety */
Glenn Randers-Pehrson104622b2000-05-29 08:58:03 -0500605 }
John Bowlerb5d00512012-03-09 09:15:18 -0600606 while (ret == Z_OK);
607
608 /* There may be some space left in the last output buffer, this needs to
609 * be subtracted from output_len.
610 */
611 output_len -= png_ptr->zstream.avail_out;
612 png_ptr->zstream.avail_out = 0; /* safety */
613 comp->output_len = output_len;
614
615 /* Now double check the output length, put in a custom message if it is
616 * too long. Otherwise ensure the z_stream::msg pointer is set to
617 * something.
618 */
619 if (output_len + prefix_len >= PNG_UINT_31_MAX)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600620 {
John Bowlerb5d00512012-03-09 09:15:18 -0600621 png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long");
622 ret = Z_MEM_ERROR;
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600623 }
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600624
John Bowlerb5d00512012-03-09 09:15:18 -0600625 else
626 png_zstream_error(png_ptr, ret);
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -0500627
John Bowlerb5d00512012-03-09 09:15:18 -0600628 /* Reset zlib for another zTXt/iTXt or image data */
629 png_ptr->zowner = 0;
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600630
John Bowlerb5d00512012-03-09 09:15:18 -0600631 /* The only success case is Z_STREAM_END, input_len must be 0, if not this
632 * is an internal error.
633 */
634 if (ret == Z_STREAM_END && input_len == 0)
635 {
636 /* Fix up the deflate header, if required */
637 optimize_cmf(comp->output, comp->input_len);
638
639 /* But Z_OK is returned, not Z_STREAM_END; this allows the claim
640 * function above to return Z_STREAM_END on an error (though it never
641 * does in the current versions of zlib.)
642 */
643 return Z_OK;
644 }
645
646 else
647 return ret;
648 }
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600649}
650
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -0500651/* Ship the compressed text out via chunk writes */
John Bowlerb5d00512012-03-09 09:15:18 -0600652static void
John Bowler5d567862011-12-24 09:12:00 -0600653png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600654{
John Bowlerb5d00512012-03-09 09:15:18 -0600655 png_uint_32 output_len = comp->output_len;
656 png_const_bytep output = comp->output;
657 png_uint_32 avail = sizeof comp->output;
658 png_compression_buffer *next = png_ptr->zbuffer_list;
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600659
John Bowlerb5d00512012-03-09 09:15:18 -0600660 for (;;)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600661 {
John Bowlerb5d00512012-03-09 09:15:18 -0600662 if (avail > output_len)
663 avail = output_len;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500664
John Bowlerb5d00512012-03-09 09:15:18 -0600665 png_write_chunk_data(png_ptr, output, avail);
666
667 output_len -= avail;
668
669 if (output_len == 0 || next == NULL)
670 break;
671
672 avail = png_ptr->zbuffer_size;
673 output = next->output;
674 next = next->next;
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600675 }
676
John Bowlerb5d00512012-03-09 09:15:18 -0600677 /* This is an internal error; 'next' must have been NULL! */
678 if (output_len > 0)
679 png_error(png_ptr, "error writing ancilliary chunked compressed data");
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600680}
Glenn Randers-Pehrson205483d2011-04-01 12:33:42 -0500681#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -0600682
John Bowlerb5d00512012-03-09 09:15:18 -0600683#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
684 defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
685/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
686 * and if invalid, correct the keyword rather than discarding the entire
687 * chunk. The PNG 1.0 specification requires keywords 1-79 characters in
688 * length, forbids leading or trailing whitespace, multiple internal spaces,
689 * and the non-break space (0x80) from ISO 8859-1. Returns keyword length.
690 *
691 * The 'new_key' buffer must be 80 characters in size (for the keyword plus a
692 * trailing '\0'). If this routine returns 0 then there was no keyword, or a
693 * valid one could not be generated, and the caller must png_error.
694 */
695static png_uint_32
696png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key)
697{
698 png_const_charp orig_key = key;
699 png_uint_32 key_len = 0;
700 int bad_character = 0;
701 int space = 1;
702
703 png_debug(1, "in png_check_keyword");
704
705 if (key == NULL)
706 {
707 *new_key = 0;
708 return 0;
709 }
710
711 while (*key && key_len < 79)
712 {
713 png_byte ch = (png_byte)(0xff & *key++);
714
715 if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/))
716 *new_key++ = ch, ++key_len, space = 0;
717
718 else if (!space)
719 {
720 /* A space or an invalid character when one wasn't seen immediately
721 * before; output just a space.
722 */
723 *new_key++ = 32, ++key_len, space = 1;
724
Glenn Randers-Pehrson8f4d6722012-05-23 12:30:59 -0500725 /* If the character was not a space then it is invalid. */
John Bowlerb5d00512012-03-09 09:15:18 -0600726 if (ch != 32)
727 bad_character = ch;
728 }
729
730 else if (!bad_character)
731 bad_character = ch; /* just skip it, record the first error */
732 }
733
734 if (key_len > 0 && space) /* trailing space */
735 {
736 --key_len, --new_key;
737 if (!bad_character)
738 bad_character = 32;
739 }
740
741 /* Terminate the keyword */
742 *new_key = 0;
743
744 if (key_len == 0)
745 return 0;
746
747 /* Try to only output one warning per keyword: */
748 if (*key) /* keyword too long */
749 png_warning(png_ptr, "keyword truncated");
750
751 else if (bad_character)
752 {
753 PNG_WARNING_PARAMETERS(p)
754
755 png_warning_parameter(p, 1, orig_key);
756 png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_02x, bad_character);
757
758 png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'");
759 }
760
761 return key_len;
762}
763#endif
764
Guy Schalnat0d580581995-07-20 02:43:20 -0500765/* Write the IHDR chunk, and update the png_struct with the necessary
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600766 * information. Note that the rest of this code depends upon this
767 * information being correct.
768 */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -0500769void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -0600770png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600771 int bit_depth, int color_type, int compression_type, int filter_type,
772 int interlace_type)
Guy Schalnat0d580581995-07-20 02:43:20 -0500773{
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -0500774 png_byte buf[13]; /* Buffer to store the IHDR info */
Guy Schalnat0d580581995-07-20 02:43:20 -0500775
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -0500776 png_debug(1, "in png_write_IHDR");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -0500777
Guy Schalnate5a37791996-06-05 15:50:50 -0500778 /* Check that we have valid input data from the application info */
779 switch (color_type)
780 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500781 case PNG_COLOR_TYPE_GRAY:
Guy Schalnate5a37791996-06-05 15:50:50 -0500782 switch (bit_depth)
783 {
784 case 1:
785 case 2:
786 case 4:
787 case 8:
Glenn Randers-Pehrson7d3e6732010-08-26 17:14:07 -0500788#ifdef PNG_WRITE_16BIT_SUPPORTED
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600789 case 16:
Glenn Randers-Pehrson7d3e6732010-08-26 17:14:07 -0500790#endif
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600791 png_ptr->channels = 1; break;
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -0500792
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600793 default:
794 png_error(png_ptr,
795 "Invalid bit depth for grayscale image");
Guy Schalnate5a37791996-06-05 15:50:50 -0500796 }
797 break;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500798
Andreas Dilger47a0c421997-05-16 02:46:07 -0500799 case PNG_COLOR_TYPE_RGB:
Glenn Randers-Pehrson7d3e6732010-08-26 17:14:07 -0500800#ifdef PNG_WRITE_16BIT_SUPPORTED
Guy Schalnate5a37791996-06-05 15:50:50 -0500801 if (bit_depth != 8 && bit_depth != 16)
Glenn Randers-Pehrson7d3e6732010-08-26 17:14:07 -0500802#else
803 if (bit_depth != 8)
804#endif
Guy Schalnate5a37791996-06-05 15:50:50 -0500805 png_error(png_ptr, "Invalid bit depth for RGB image");
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500806
Guy Schalnate5a37791996-06-05 15:50:50 -0500807 png_ptr->channels = 3;
808 break;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500809
Andreas Dilger47a0c421997-05-16 02:46:07 -0500810 case PNG_COLOR_TYPE_PALETTE:
Guy Schalnate5a37791996-06-05 15:50:50 -0500811 switch (bit_depth)
812 {
813 case 1:
814 case 2:
815 case 4:
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600816 case 8:
817 png_ptr->channels = 1;
818 break;
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -0500819
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600820 default:
821 png_error(png_ptr, "Invalid bit depth for paletted image");
Guy Schalnate5a37791996-06-05 15:50:50 -0500822 }
823 break;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500824
Andreas Dilger47a0c421997-05-16 02:46:07 -0500825 case PNG_COLOR_TYPE_GRAY_ALPHA:
Guy Schalnate5a37791996-06-05 15:50:50 -0500826 if (bit_depth != 8 && bit_depth != 16)
827 png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500828
Guy Schalnate5a37791996-06-05 15:50:50 -0500829 png_ptr->channels = 2;
830 break;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500831
Andreas Dilger47a0c421997-05-16 02:46:07 -0500832 case PNG_COLOR_TYPE_RGB_ALPHA:
Glenn Randers-Pehrson7d3e6732010-08-26 17:14:07 -0500833#ifdef PNG_WRITE_16BIT_SUPPORTED
Guy Schalnate5a37791996-06-05 15:50:50 -0500834 if (bit_depth != 8 && bit_depth != 16)
Glenn Randers-Pehrson7d3e6732010-08-26 17:14:07 -0500835#else
836 if (bit_depth != 8)
837#endif
Guy Schalnate5a37791996-06-05 15:50:50 -0500838 png_error(png_ptr, "Invalid bit depth for RGBA image");
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500839
Guy Schalnate5a37791996-06-05 15:50:50 -0500840 png_ptr->channels = 4;
841 break;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500842
Guy Schalnate5a37791996-06-05 15:50:50 -0500843 default:
844 png_error(png_ptr, "Invalid image color type specified");
845 }
846
Andreas Dilger47a0c421997-05-16 02:46:07 -0500847 if (compression_type != PNG_COMPRESSION_TYPE_BASE)
Guy Schalnate5a37791996-06-05 15:50:50 -0500848 {
849 png_warning(png_ptr, "Invalid compression type specified");
Andreas Dilger47a0c421997-05-16 02:46:07 -0500850 compression_type = PNG_COMPRESSION_TYPE_BASE;
Guy Schalnate5a37791996-06-05 15:50:50 -0500851 }
852
Glenn Randers-Pehrson408b4212000-12-18 09:33:57 -0600853 /* Write filter_method 64 (intrapixel differencing) only if
854 * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
855 * 2. Libpng did not write a PNG signature (this filter_method is only
856 * used in PNG datastreams that are embedded in MNG datastreams) and
857 * 3. The application called png_permit_mng_features with a mask that
858 * included PNG_FLAG_MNG_FILTER_64 and
859 * 4. The filter_method is 64 and
860 * 5. The color_type is RGB or RGBA
861 */
Glenn Randers-Pehrson2ad31ae2000-12-15 08:54:42 -0600862 if (
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -0500863#ifdef PNG_MNG_FEATURES_SUPPORTED
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600864 !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
865 ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) &&
866 (color_type == PNG_COLOR_TYPE_RGB ||
867 color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
868 (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) &&
Glenn Randers-Pehrson2ad31ae2000-12-15 08:54:42 -0600869#endif
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600870 filter_type != PNG_FILTER_TYPE_BASE)
Guy Schalnate5a37791996-06-05 15:50:50 -0500871 {
872 png_warning(png_ptr, "Invalid filter type specified");
Andreas Dilger47a0c421997-05-16 02:46:07 -0500873 filter_type = PNG_FILTER_TYPE_BASE;
Guy Schalnate5a37791996-06-05 15:50:50 -0500874 }
875
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -0600876#ifdef PNG_WRITE_INTERLACING_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -0500877 if (interlace_type != PNG_INTERLACE_NONE &&
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500878 interlace_type != PNG_INTERLACE_ADAM7)
Guy Schalnate5a37791996-06-05 15:50:50 -0500879 {
880 png_warning(png_ptr, "Invalid interlace type specified");
Andreas Dilger47a0c421997-05-16 02:46:07 -0500881 interlace_type = PNG_INTERLACE_ADAM7;
Guy Schalnate5a37791996-06-05 15:50:50 -0500882 }
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -0600883#else
884 interlace_type=PNG_INTERLACE_NONE;
885#endif
Guy Schalnate5a37791996-06-05 15:50:50 -0500886
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500887 /* Save the relevent information */
Guy Schalnate5a37791996-06-05 15:50:50 -0500888 png_ptr->bit_depth = (png_byte)bit_depth;
889 png_ptr->color_type = (png_byte)color_type;
890 png_ptr->interlaced = (png_byte)interlace_type;
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -0500891#ifdef PNG_MNG_FEATURES_SUPPORTED
Glenn Randers-Pehrson2ad31ae2000-12-15 08:54:42 -0600892 png_ptr->filter_type = (png_byte)filter_type;
Glenn Randers-Pehrson8b6a8892001-05-18 04:54:50 -0500893#endif
Glenn Randers-Pehrson5b5dcf82004-07-17 22:45:44 -0500894 png_ptr->compression_type = (png_byte)compression_type;
Guy Schalnate5a37791996-06-05 15:50:50 -0500895 png_ptr->width = width;
896 png_ptr->height = height;
897
898 png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels);
Glenn Randers-Pehrson272489d2004-08-04 06:34:52 -0500899 png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -0500900 /* Set the usr info, so any transformations can modify it */
Guy Schalnate5a37791996-06-05 15:50:50 -0500901 png_ptr->usr_width = png_ptr->width;
902 png_ptr->usr_bit_depth = png_ptr->bit_depth;
903 png_ptr->usr_channels = png_ptr->channels;
904
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500905 /* Pack the header information into the buffer */
Guy Schalnat0d580581995-07-20 02:43:20 -0500906 png_save_uint_32(buf, width);
907 png_save_uint_32(buf + 4, height);
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600908 buf[8] = (png_byte)bit_depth;
909 buf[9] = (png_byte)color_type;
910 buf[10] = (png_byte)compression_type;
911 buf[11] = (png_byte)filter_type;
912 buf[12] = (png_byte)interlace_type;
Guy Schalnat0d580581995-07-20 02:43:20 -0500913
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500914 /* Write the chunk */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500915 png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13);
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500916
Guy Schalnate5a37791996-06-05 15:50:50 -0500917 if (!(png_ptr->do_filter))
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500918 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500919 if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600920 png_ptr->bit_depth < 8)
Guy Schalnate5a37791996-06-05 15:50:50 -0500921 png_ptr->do_filter = PNG_FILTER_NONE;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500922
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500923 else
Guy Schalnate5a37791996-06-05 15:50:50 -0500924 png_ptr->do_filter = PNG_ALL_FILTERS;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500925 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -0500926
Glenn Randers-Pehrsonf8378312011-03-31 22:06:04 -0500927 png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */
Guy Schalnat0d580581995-07-20 02:43:20 -0500928}
929
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -0500930/* Write the palette. We are careful not to trust png_color to be in the
Glenn Randers-Pehrson345bc271998-06-14 14:43:31 -0500931 * correct order for PNG, so people can redefine it to any convenient
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600932 * structure.
933 */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -0500934void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -0600935png_write_PLTE(png_structrp png_ptr, png_const_colorp palette,
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -0500936 png_uint_32 num_pal)
Guy Schalnat0d580581995-07-20 02:43:20 -0500937{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500938 png_uint_32 i;
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -0500939 png_const_colorp pal_ptr;
Guy Schalnat0d580581995-07-20 02:43:20 -0500940 png_byte buf[3];
941
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -0500942 png_debug(1, "in png_write_PLTE");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -0500943
Glenn Randers-Pehrson4393a9a1999-09-17 12:27:26 -0500944 if ((
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -0500945#ifdef PNG_MNG_FEATURES_SUPPORTED
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600946 !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) &&
Glenn Randers-Pehrson4393a9a1999-09-17 12:27:26 -0500947#endif
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600948 num_pal == 0) || num_pal > 256)
Glenn Randers-Pehrson3097f612001-05-07 14:52:45 -0500949 {
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600950 if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
951 {
952 png_error(png_ptr, "Invalid number of colors in palette");
953 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500954
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600955 else
956 {
957 png_warning(png_ptr, "Invalid number of colors in palette");
958 return;
959 }
Glenn Randers-Pehrson3097f612001-05-07 14:52:45 -0500960 }
961
962 if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))
963 {
964 png_warning(png_ptr,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -0600965 "Ignoring request to write a PLTE chunk in grayscale PNG");
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500966
Glenn Randers-Pehrson3097f612001-05-07 14:52:45 -0500967 return;
Guy Schalnate5a37791996-06-05 15:50:50 -0500968 }
969
Andreas Dilger47a0c421997-05-16 02:46:07 -0500970 png_ptr->num_palette = (png_uint_16)num_pal;
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -0500971 png_debug1(3, "num_palette = %d", png_ptr->num_palette);
Guy Schalnate5a37791996-06-05 15:50:50 -0500972
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -0500973 png_write_chunk_header(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3));
Glenn Randers-Pehrsondbd40142009-08-31 08:42:02 -0500974#ifdef PNG_POINTER_INDEXING_SUPPORTED
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500975
Andreas Dilger47a0c421997-05-16 02:46:07 -0500976 for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)
Guy Schalnat0d580581995-07-20 02:43:20 -0500977 {
978 buf[0] = pal_ptr->red;
979 buf[1] = pal_ptr->green;
980 buf[2] = pal_ptr->blue;
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -0500981 png_write_chunk_data(png_ptr, buf, (png_size_t)3);
Guy Schalnat0d580581995-07-20 02:43:20 -0500982 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500983
Glenn Randers-Pehrsond4366722000-06-04 14:29:29 -0500984#else
Glenn Randers-Pehrsone3f3c4e2010-02-07 18:08:50 -0600985 /* This is a little slower but some buggy compilers need to do this
986 * instead
987 */
Glenn Randers-Pehrsond4366722000-06-04 14:29:29 -0500988 pal_ptr=palette;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500989
Glenn Randers-Pehrsond4366722000-06-04 14:29:29 -0500990 for (i = 0; i < num_pal; i++)
991 {
992 buf[0] = pal_ptr[i].red;
993 buf[1] = pal_ptr[i].green;
994 buf[2] = pal_ptr[i].blue;
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -0500995 png_write_chunk_data(png_ptr, buf, (png_size_t)3);
Glenn Randers-Pehrsond4366722000-06-04 14:29:29 -0500996 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -0500997
Glenn Randers-Pehrsond4366722000-06-04 14:29:29 -0500998#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500999 png_write_chunk_end(png_ptr);
Guy Schalnate5a37791996-06-05 15:50:50 -05001000 png_ptr->mode |= PNG_HAVE_PLTE;
Guy Schalnat0d580581995-07-20 02:43:20 -05001001}
1002
John Bowlerb5d00512012-03-09 09:15:18 -06001003/* This is similar to png_text_compress, above, except that it does not require
1004 * all of the data at once and, instead of buffering the compressed result,
1005 * writes it as IDAT chunks. Unlike png_text_compress it *can* png_error out
1006 * because it calls the write interface. As a result it does its own error
1007 * reporting and does not return an error code. In the event of error it will
1008 * just call png_error. The input data length may exceed 32-bits. The 'flush'
1009 * parameter is exactly the same as that to deflate, with the following
1010 * meanings:
1011 *
1012 * Z_NO_FLUSH: normal incremental output of compressed data
1013 * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush
1014 * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up
1015 *
1016 * The routine manages the acquire and release of the png_ptr->zstream by
1017 * checking and (at the end) clearing png_ptr->zowner, it does some sanity
1018 * checks on the 'mode' flags while doing this.
1019 */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001020void /* PRIVATE */
John Bowlerb5d00512012-03-09 09:15:18 -06001021png_compress_IDAT(png_structrp png_ptr, png_const_bytep input,
1022 png_alloc_size_t input_len, int flush)
Guy Schalnat0d580581995-07-20 02:43:20 -05001023{
John Bowlerb5d00512012-03-09 09:15:18 -06001024 if (png_ptr->zowner != png_IDAT)
Glenn Randers-Pehrson5b779162004-09-04 13:25:08 -05001025 {
John Bowlerb5d00512012-03-09 09:15:18 -06001026 /* First time. Ensure we have a temporary buffer for compression and
1027 * trim the buffer list if it has more than one entry to free memory.
Glenn Randers-Pehrsonec8296a2011-04-01 15:09:05 -05001028 */
John Bowlerb5d00512012-03-09 09:15:18 -06001029 if (png_ptr->zbuffer_list == NULL)
Glenn Randers-Pehrson5b779162004-09-04 13:25:08 -05001030 {
John Bowlerb5d00512012-03-09 09:15:18 -06001031 png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp,
1032 png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr)));
1033 png_ptr->zbuffer_list->next = NULL;
Glenn Randers-Pehrson5b779162004-09-04 13:25:08 -05001034 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001035
Glenn Randers-Pehrson5b779162004-09-04 13:25:08 -05001036 else
John Bowlerb5d00512012-03-09 09:15:18 -06001037 png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next);
1038
1039 /* It is a terminal error if we can't claim the zstream. */
1040 if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK)
1041 png_error(png_ptr, png_ptr->zstream.msg);
1042
1043 /* The output state is maintained in png_ptr->zstream, so it must be
1044 * initialized here after the claim.
1045 */
1046 png_ptr->zstream.next_out = png_ptr->zbuffer_list->output;
1047 png_ptr->zstream.avail_out = png_ptr->zbuffer_size;
Glenn Randers-Pehrson5b779162004-09-04 13:25:08 -05001048 }
1049
John Bowlerb5d00512012-03-09 09:15:18 -06001050 /* Now loop reading and writing until all the input is consumed or an error
1051 * terminates the operation. The _out values are maintained across calls to
1052 * this function, but the input must be reset each time.
John Bowlerc5bef942011-05-05 17:35:39 -05001053 */
John Bowlerb5d00512012-03-09 09:15:18 -06001054 png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input);
1055 png_ptr->zstream.avail_in = 0; /* set below */
1056 for (;;)
1057 {
1058 int ret;
1059
1060 /* INPUT: from the row data */
1061 uInt avail = ZLIB_IO_MAX;
1062
1063 if (avail > input_len)
1064 avail = (uInt)input_len; /* safe because of the check */
1065
1066 png_ptr->zstream.avail_in = avail;
1067 input_len -= avail;
1068
1069 ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush);
1070
1071 /* Include as-yet unconsumed input */
1072 input_len += png_ptr->zstream.avail_in;
1073 png_ptr->zstream.avail_in = 0;
1074
1075 /* OUTPUT: write complete IDAT chunks when avail_out drops to zero, note
1076 * that these two zstream fields are preserved across the calls, therefore
1077 * there is no need to set these up on entry to the loop.
1078 */
1079 if (png_ptr->zstream.avail_out == 0)
1080 {
1081 png_bytep data = png_ptr->zbuffer_list->output;
1082 uInt size = png_ptr->zbuffer_size;
1083
1084 /* Write an IDAT containing the data then reset the buffer. The
1085 * first IDAT may need deflate header optimization.
1086 */
1087# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
1088 if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
1089 png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
1090 optimize_cmf(data, png_image_size(png_ptr));
1091# endif
1092
1093 png_write_complete_chunk(png_ptr, png_IDAT, data, size);
1094 png_ptr->mode |= PNG_HAVE_IDAT;
1095
1096 png_ptr->zstream.next_out = data;
1097 png_ptr->zstream.avail_out = size;
1098
1099 /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with
1100 * the same flush parameter until it has finished output, for NO_FLUSH
1101 * it doesn't matter.
1102 */
1103 if (ret == Z_OK && flush != Z_NO_FLUSH)
1104 continue;
1105 }
1106
1107 /* The order of these checks doesn't matter much; it just effect which
1108 * possible error might be detected if multiple things go wrong at once.
1109 */
1110 if (ret == Z_OK) /* most likely return code! */
1111 {
1112 /* If all the input has been consumed then just return. If Z_FINISH
1113 * was used as the flush parameter something has gone wrong if we get
1114 * here.
1115 */
1116 if (input_len == 0)
1117 {
1118 if (flush == Z_FINISH)
1119 png_error(png_ptr, "Z_OK on Z_FINISH with output space");
1120
1121 return;
1122 }
1123 }
1124
1125 else if (ret == Z_STREAM_END && flush == Z_FINISH)
1126 {
1127 /* This is the end of the IDAT data; any pending output must be
1128 * flushed. For small PNG files we may still be at the beginning.
1129 */
1130 png_bytep data = png_ptr->zbuffer_list->output;
1131 uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out;
1132
1133# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
1134 if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
1135 png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
1136 optimize_cmf(data, png_image_size(png_ptr));
1137# endif
1138
1139 png_write_complete_chunk(png_ptr, png_IDAT, data, size);
1140 png_ptr->zstream.avail_out = 0;
1141 png_ptr->zstream.next_out = NULL;
1142 png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT;
1143
1144 png_ptr->zowner = 0; /* Release the stream */
1145 return;
1146 }
1147
1148 else
1149 {
1150 /* This is an error condition. */
1151 png_zstream_error(png_ptr, ret);
1152 png_error(png_ptr, png_ptr->zstream.msg);
1153 }
1154 }
Guy Schalnat0d580581995-07-20 02:43:20 -05001155}
1156
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001157/* Write an IEND chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001158void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001159png_write_IEND(png_structrp png_ptr)
Guy Schalnat0d580581995-07-20 02:43:20 -05001160{
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001161 png_debug(1, "in png_write_IEND");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001162
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001163 png_write_complete_chunk(png_ptr, png_IEND, NULL, (png_size_t)0);
Andreas Dilger02ad0ef1997-01-17 01:34:35 -06001164 png_ptr->mode |= PNG_HAVE_IEND;
Guy Schalnat0d580581995-07-20 02:43:20 -05001165}
1166
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001167#ifdef PNG_WRITE_gAMA_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001168/* Write a gAMA chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001169void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001170png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001171{
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001172 png_byte buf[4];
1173
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001174 png_debug(1, "in png_write_gAMA");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001175
Glenn Randers-Pehrson626afd62009-08-20 22:52:51 -05001176 /* file_gamma is saved in 1/100,000ths */
Glenn Randers-Pehrsonb1828932001-06-23 08:03:17 -05001177 png_save_uint_32(buf, (png_uint_32)file_gamma);
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001178 png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001179}
1180#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05001181
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001182#ifdef PNG_WRITE_sRGB_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001183/* Write a sRGB chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001184void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001185png_write_sRGB(png_structrp png_ptr, int srgb_intent)
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -06001186{
1187 png_byte buf[1];
1188
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001189 png_debug(1, "in png_write_sRGB");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001190
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001191 if (srgb_intent >= PNG_sRGB_INTENT_LAST)
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001192 png_warning(png_ptr,
1193 "Invalid sRGB rendering intent specified");
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001194
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -06001195 buf[0]=(png_byte)srgb_intent;
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001196 png_write_complete_chunk(png_ptr, png_sRGB, buf, (png_size_t)1);
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -06001197}
1198#endif
1199
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001200#ifdef PNG_WRITE_iCCP_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001201/* Write an iCCP chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001202void /* PRIVATE */
John Bowlerb11b31a2012-03-21 07:55:46 -05001203png_write_iCCP(png_structrp png_ptr, png_const_charp name,
1204 png_const_bytep profile)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001205{
John Bowlerb5d00512012-03-09 09:15:18 -06001206 png_uint_32 name_len;
John Bowlerb11b31a2012-03-21 07:55:46 -05001207 png_uint_32 profile_len;
John Bowlerb5d00512012-03-09 09:15:18 -06001208 png_byte new_name[81]; /* 1 byte for the compression byte */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001209 compression_state comp;
1210
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001211 png_debug(1, "in png_write_iCCP");
Glenn Randers-Pehrson9c3ab682006-02-20 22:09:05 -06001212
John Bowlerb11b31a2012-03-21 07:55:46 -05001213 /* These are all internal problems: the profile should have been checked
1214 * before when it was stored.
1215 */
John Bowlercf499192012-03-01 21:54:07 -06001216 if (profile == NULL)
John Bowlerb11b31a2012-03-21 07:55:46 -05001217 png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */
1218
1219 profile_len = png_get_uint_32(profile);
John Bowlercf499192012-03-01 21:54:07 -06001220
1221 if (profile_len < 132)
1222 png_error(png_ptr, "ICC profile too short");
1223
John Bowler6f237b62012-03-02 13:13:15 -06001224 if (profile_len & 0x03)
John Bowlercf499192012-03-01 21:54:07 -06001225 png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)");
1226
John Bowlercf499192012-03-01 21:54:07 -06001227 {
John Bowlerb5d00512012-03-09 09:15:18 -06001228 png_uint_32 embedded_profile_len = png_get_uint_32(profile);
1229
1230 if (profile_len != embedded_profile_len)
1231 png_error(png_ptr, "Profile length does not match profile");
John Bowlercf499192012-03-01 21:54:07 -06001232 }
1233
John Bowlerb5d00512012-03-09 09:15:18 -06001234 name_len = png_check_keyword(png_ptr, name, new_name);
John Bowlercf499192012-03-01 21:54:07 -06001235
John Bowlerb5d00512012-03-09 09:15:18 -06001236 if (name_len == 0)
1237 png_error(png_ptr, "iCCP: invalid keyword");
Glenn Randers-Pehrson9c3ab682006-02-20 22:09:05 -06001238
John Bowlerb5d00512012-03-09 09:15:18 -06001239 new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE;
Glenn Randers-Pehrson3a054e12009-08-01 08:53:23 -05001240
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05001241 /* Make sure we include the NULL after the name and the compression type */
John Bowlerb5d00512012-03-09 09:15:18 -06001242 ++name_len;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001243
John Bowlerb5d00512012-03-09 09:15:18 -06001244 png_text_compress_init(&comp, profile, profile_len);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001245
John Bowlerb5d00512012-03-09 09:15:18 -06001246 /* Allow for keyword terminator and compression byte */
1247 if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK)
1248 png_error(png_ptr, png_ptr->zstream.msg);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001249
John Bowlerb5d00512012-03-09 09:15:18 -06001250 png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len);
1251
1252 png_write_chunk_data(png_ptr, new_name, name_len);
1253
1254 png_write_compressed_data_out(png_ptr, &comp);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001255
1256 png_write_chunk_end(png_ptr);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001257}
1258#endif
1259
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001260#ifdef PNG_WRITE_sPLT_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001261/* Write a sPLT chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001262void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001263png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001264{
John Bowlerb5d00512012-03-09 09:15:18 -06001265 png_uint_32 name_len;
1266 png_byte new_name[80];
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001267 png_byte entrybuf[10];
Glenn Randers-Pehrson9c90d7f2009-07-19 13:11:25 -05001268 png_size_t entry_size = (spalette->depth == 8 ? 6 : 10);
1269 png_size_t palette_size = entry_size * spalette->nentries;
Glenn Randers-Pehrson520a7642000-03-21 05:13:06 -06001270 png_sPLT_entryp ep;
Glenn Randers-Pehrsondbd40142009-08-31 08:42:02 -05001271#ifndef PNG_POINTER_INDEXING_SUPPORTED
Glenn Randers-Pehrsond4366722000-06-04 14:29:29 -05001272 int i;
1273#endif
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001274
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001275 png_debug(1, "in png_write_sPLT");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001276
John Bowlerb5d00512012-03-09 09:15:18 -06001277 name_len = png_check_keyword(png_ptr, spalette->name, new_name);
1278
1279 if (name_len == 0)
1280 png_error(png_ptr, "sPLT: invalid keyword");
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001281
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001282 /* Make sure we include the NULL after the name */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001283 png_write_chunk_header(png_ptr, png_sPLT,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001284 (png_uint_32)(name_len + 2 + palette_size));
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001285
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001286 png_write_chunk_data(png_ptr, (png_bytep)new_name,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001287 (png_size_t)(name_len + 1));
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001288
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001289 png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001290
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001291 /* Loop through each palette entry, writing appropriately */
Glenn Randers-Pehrsondbd40142009-08-31 08:42:02 -05001292#ifdef PNG_POINTER_INDEXING_SUPPORTED
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001293 for (ep = spalette->entries; ep<spalette->entries + spalette->nentries; ep++)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001294 {
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001295 if (spalette->depth == 8)
1296 {
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001297 entrybuf[0] = (png_byte)ep->red;
1298 entrybuf[1] = (png_byte)ep->green;
1299 entrybuf[2] = (png_byte)ep->blue;
1300 entrybuf[3] = (png_byte)ep->alpha;
1301 png_save_uint_16(entrybuf + 4, ep->frequency);
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001302 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001303
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001304 else
1305 {
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001306 png_save_uint_16(entrybuf + 0, ep->red);
1307 png_save_uint_16(entrybuf + 2, ep->green);
1308 png_save_uint_16(entrybuf + 4, ep->blue);
1309 png_save_uint_16(entrybuf + 6, ep->alpha);
1310 png_save_uint_16(entrybuf + 8, ep->frequency);
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001311 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001312
John Bowlerb5d00512012-03-09 09:15:18 -06001313 png_write_chunk_data(png_ptr, entrybuf, entry_size);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001314 }
Glenn Randers-Pehrsond4366722000-06-04 14:29:29 -05001315#else
1316 ep=spalette->entries;
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001317 for (i = 0; i>spalette->nentries; i++)
Glenn Randers-Pehrsond4366722000-06-04 14:29:29 -05001318 {
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001319 if (spalette->depth == 8)
1320 {
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001321 entrybuf[0] = (png_byte)ep[i].red;
1322 entrybuf[1] = (png_byte)ep[i].green;
1323 entrybuf[2] = (png_byte)ep[i].blue;
1324 entrybuf[3] = (png_byte)ep[i].alpha;
1325 png_save_uint_16(entrybuf + 4, ep[i].frequency);
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001326 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001327
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001328 else
1329 {
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001330 png_save_uint_16(entrybuf + 0, ep[i].red);
1331 png_save_uint_16(entrybuf + 2, ep[i].green);
1332 png_save_uint_16(entrybuf + 4, ep[i].blue);
1333 png_save_uint_16(entrybuf + 6, ep[i].alpha);
1334 png_save_uint_16(entrybuf + 8, ep[i].frequency);
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001335 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001336
John Bowlerb5d00512012-03-09 09:15:18 -06001337 png_write_chunk_data(png_ptr, entrybuf, entry_size);
Glenn Randers-Pehrsond4366722000-06-04 14:29:29 -05001338 }
1339#endif
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001340
1341 png_write_chunk_end(png_ptr);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001342}
1343#endif
1344
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001345#ifdef PNG_WRITE_sBIT_SUPPORTED
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05001346/* Write the sBIT chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001347void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001348png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type)
Guy Schalnat0d580581995-07-20 02:43:20 -05001349{
1350 png_byte buf[4];
Andreas Dilger47a0c421997-05-16 02:46:07 -05001351 png_size_t size;
Guy Schalnat0d580581995-07-20 02:43:20 -05001352
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001353 png_debug(1, "in png_write_sBIT");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001354
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001355 /* Make sure we don't depend upon the order of PNG_COLOR_8 */
Guy Schalnat0d580581995-07-20 02:43:20 -05001356 if (color_type & PNG_COLOR_MASK_COLOR)
1357 {
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -06001358 png_byte maxbits;
Guy Schalnate5a37791996-06-05 15:50:50 -05001359
Glenn Randers-Pehrson860ab2b1999-10-14 07:43:10 -05001360 maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 :
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001361 png_ptr->usr_bit_depth);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001362
Glenn Randers-Pehrson5c6aeb21998-12-29 11:47:59 -06001363 if (sbit->red == 0 || sbit->red > maxbits ||
1364 sbit->green == 0 || sbit->green > maxbits ||
Guy Schalnate5a37791996-06-05 15:50:50 -05001365 sbit->blue == 0 || sbit->blue > maxbits)
1366 {
1367 png_warning(png_ptr, "Invalid sBIT depth specified");
1368 return;
1369 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001370
Guy Schalnat0d580581995-07-20 02:43:20 -05001371 buf[0] = sbit->red;
1372 buf[1] = sbit->green;
1373 buf[2] = sbit->blue;
1374 size = 3;
1375 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001376
Guy Schalnat0d580581995-07-20 02:43:20 -05001377 else
1378 {
Guy Schalnate5a37791996-06-05 15:50:50 -05001379 if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)
1380 {
1381 png_warning(png_ptr, "Invalid sBIT depth specified");
1382 return;
1383 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001384
Guy Schalnat0d580581995-07-20 02:43:20 -05001385 buf[0] = sbit->gray;
1386 size = 1;
1387 }
1388
1389 if (color_type & PNG_COLOR_MASK_ALPHA)
1390 {
Guy Schalnate5a37791996-06-05 15:50:50 -05001391 if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)
1392 {
1393 png_warning(png_ptr, "Invalid sBIT depth specified");
1394 return;
1395 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001396
Guy Schalnat0d580581995-07-20 02:43:20 -05001397 buf[size++] = sbit->alpha;
1398 }
1399
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001400 png_write_complete_chunk(png_ptr, png_sBIT, buf, size);
Guy Schalnat0d580581995-07-20 02:43:20 -05001401}
Guy Schalnat51f0eb41995-09-26 05:22:39 -05001402#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05001403
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001404#ifdef PNG_WRITE_cHRM_SUPPORTED
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05001405/* Write the cHRM chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001406void /* PRIVATE */
John Bowlerb11b31a2012-03-21 07:55:46 -05001407png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001408{
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001409 png_byte buf[32];
1410
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001411 png_debug(1, "in png_write_cHRM");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001412
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05001413 /* Each value is saved in 1/100,000ths */
John Bowlerb11b31a2012-03-21 07:55:46 -05001414 png_save_int_32(buf, xy->whitex);
1415 png_save_int_32(buf + 4, xy->whitey);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001416
John Bowlerb11b31a2012-03-21 07:55:46 -05001417 png_save_int_32(buf + 8, xy->redx);
1418 png_save_int_32(buf + 12, xy->redy);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001419
John Bowlerb11b31a2012-03-21 07:55:46 -05001420 png_save_int_32(buf + 16, xy->greenx);
1421 png_save_int_32(buf + 20, xy->greeny);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001422
John Bowlerb11b31a2012-03-21 07:55:46 -05001423 png_save_int_32(buf + 24, xy->bluex);
1424 png_save_int_32(buf + 28, xy->bluey);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001425
John Bowlerb11b31a2012-03-21 07:55:46 -05001426 png_write_complete_chunk(png_ptr, png_cHRM, buf, 32);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001427}
1428#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05001429
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001430#ifdef PNG_WRITE_tRNS_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001431/* Write the tRNS chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001432void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001433png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha,
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001434 png_const_color_16p tran, int num_trans, int color_type)
Guy Schalnat0d580581995-07-20 02:43:20 -05001435{
1436 png_byte buf[6];
1437
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001438 png_debug(1, "in png_write_tRNS");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001439
Guy Schalnat0d580581995-07-20 02:43:20 -05001440 if (color_type == PNG_COLOR_TYPE_PALETTE)
1441 {
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -06001442 if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette)
Guy Schalnate5a37791996-06-05 15:50:50 -05001443 {
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001444 png_warning(png_ptr, "Invalid number of transparent colors specified");
Guy Schalnate5a37791996-06-05 15:50:50 -05001445 return;
1446 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001447
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05001448 /* Write the chunk out as it is */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001449 png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha, (png_size_t)num_trans);
Guy Schalnat0d580581995-07-20 02:43:20 -05001450 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001451
Guy Schalnat0d580581995-07-20 02:43:20 -05001452 else if (color_type == PNG_COLOR_TYPE_GRAY)
1453 {
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05001454 /* One 16 bit value */
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001455 if (tran->gray >= (1 << png_ptr->bit_depth))
Glenn Randers-Pehrson1ea0ff32001-08-07 22:25:59 -05001456 {
1457 png_warning(png_ptr,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001458 "Ignoring attempt to write tRNS chunk out-of-range for bit_depth");
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001459
Glenn Randers-Pehrson1ea0ff32001-08-07 22:25:59 -05001460 return;
1461 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001462
Guy Schalnat0d580581995-07-20 02:43:20 -05001463 png_save_uint_16(buf, tran->gray);
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001464 png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2);
Guy Schalnat0d580581995-07-20 02:43:20 -05001465 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001466
Guy Schalnat0d580581995-07-20 02:43:20 -05001467 else if (color_type == PNG_COLOR_TYPE_RGB)
1468 {
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05001469 /* Three 16 bit values */
Guy Schalnat0d580581995-07-20 02:43:20 -05001470 png_save_uint_16(buf, tran->red);
1471 png_save_uint_16(buf + 2, tran->green);
1472 png_save_uint_16(buf + 4, tran->blue);
Glenn Randers-Pehrson7d3e6732010-08-26 17:14:07 -05001473#ifdef PNG_WRITE_16BIT_SUPPORTED
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001474 if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
Glenn Randers-Pehrson7d3e6732010-08-26 17:14:07 -05001475#else
1476 if (buf[0] | buf[2] | buf[4])
1477#endif
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001478 {
1479 png_warning(png_ptr,
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001480 "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001481 return;
1482 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001483
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001484 png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6);
Guy Schalnat0d580581995-07-20 02:43:20 -05001485 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001486
Guy Schalnate5a37791996-06-05 15:50:50 -05001487 else
1488 {
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001489 png_warning(png_ptr, "Can't write tRNS with an alpha channel");
Guy Schalnate5a37791996-06-05 15:50:50 -05001490 }
Guy Schalnat0d580581995-07-20 02:43:20 -05001491}
Guy Schalnat51f0eb41995-09-26 05:22:39 -05001492#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05001493
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001494#ifdef PNG_WRITE_bKGD_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001495/* Write the background chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001496void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001497png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type)
Guy Schalnat0d580581995-07-20 02:43:20 -05001498{
1499 png_byte buf[6];
1500
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001501 png_debug(1, "in png_write_bKGD");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001502
Guy Schalnat0d580581995-07-20 02:43:20 -05001503 if (color_type == PNG_COLOR_TYPE_PALETTE)
1504 {
Glenn Randers-Pehrson4393a9a1999-09-17 12:27:26 -05001505 if (
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001506#ifdef PNG_MNG_FEATURES_SUPPORTED
Glenn Randers-Pehrson5e5c1e12000-11-10 12:26:19 -06001507 (png_ptr->num_palette ||
1508 (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) &&
Glenn Randers-Pehrson4393a9a1999-09-17 12:27:26 -05001509#endif
Glenn Randers-Pehrsond6d80752008-12-02 09:49:43 -06001510 back->index >= png_ptr->num_palette)
Guy Schalnate5a37791996-06-05 15:50:50 -05001511 {
1512 png_warning(png_ptr, "Invalid background palette index");
1513 return;
1514 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001515
Guy Schalnat0d580581995-07-20 02:43:20 -05001516 buf[0] = back->index;
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001517 png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1);
Guy Schalnat0d580581995-07-20 02:43:20 -05001518 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001519
Guy Schalnat0d580581995-07-20 02:43:20 -05001520 else if (color_type & PNG_COLOR_MASK_COLOR)
1521 {
1522 png_save_uint_16(buf, back->red);
1523 png_save_uint_16(buf + 2, back->green);
1524 png_save_uint_16(buf + 4, back->blue);
Glenn Randers-Pehrson7d3e6732010-08-26 17:14:07 -05001525#ifdef PNG_WRITE_16BIT_SUPPORTED
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001526 if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
Glenn Randers-Pehrson7d3e6732010-08-26 17:14:07 -05001527#else
1528 if (buf[0] | buf[2] | buf[4])
1529#endif
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001530 {
1531 png_warning(png_ptr,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001532 "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8");
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001533
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05001534 return;
1535 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001536
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001537 png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)6);
Guy Schalnat0d580581995-07-20 02:43:20 -05001538 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001539
Guy Schalnat0d580581995-07-20 02:43:20 -05001540 else
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06001541 {
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001542 if (back->gray >= (1 << png_ptr->bit_depth))
Glenn Randers-Pehrson1ea0ff32001-08-07 22:25:59 -05001543 {
1544 png_warning(png_ptr,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001545 "Ignoring attempt to write bKGD chunk out-of-range for bit_depth");
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001546
Glenn Randers-Pehrson1ea0ff32001-08-07 22:25:59 -05001547 return;
1548 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001549
Guy Schalnat0d580581995-07-20 02:43:20 -05001550 png_save_uint_16(buf, back->gray);
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001551 png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)2);
Guy Schalnat0d580581995-07-20 02:43:20 -05001552 }
1553}
Guy Schalnat51f0eb41995-09-26 05:22:39 -05001554#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05001555
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001556#ifdef PNG_WRITE_hIST_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001557/* Write the histogram */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001558void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001559png_write_hIST(png_structrp png_ptr, png_const_uint_16p hist, int num_hist)
Guy Schalnat0d580581995-07-20 02:43:20 -05001560{
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -06001561 int i;
Guy Schalnat0d580581995-07-20 02:43:20 -05001562 png_byte buf[3];
1563
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001564 png_debug(1, "in png_write_hIST");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001565
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -06001566 if (num_hist > (int)png_ptr->num_palette)
Guy Schalnate5a37791996-06-05 15:50:50 -05001567 {
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001568 png_debug2(3, "num_hist = %d, num_palette = %d", num_hist,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001569 png_ptr->num_palette);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001570
Guy Schalnate5a37791996-06-05 15:50:50 -05001571 png_warning(png_ptr, "Invalid number of histogram entries specified");
1572 return;
1573 }
1574
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001575 png_write_chunk_header(png_ptr, png_hIST, (png_uint_32)(num_hist * 2));
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001576
Andreas Dilger47a0c421997-05-16 02:46:07 -05001577 for (i = 0; i < num_hist; i++)
Guy Schalnat0d580581995-07-20 02:43:20 -05001578 {
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06001579 png_save_uint_16(buf, hist[i]);
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001580 png_write_chunk_data(png_ptr, buf, (png_size_t)2);
Guy Schalnat0d580581995-07-20 02:43:20 -05001581 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001582
Guy Schalnat0d580581995-07-20 02:43:20 -05001583 png_write_chunk_end(png_ptr);
1584}
Guy Schalnat51f0eb41995-09-26 05:22:39 -05001585#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05001586
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001587#ifdef PNG_WRITE_tEXt_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001588/* Write a tEXt chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001589void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001590png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001591 png_size_t text_len)
Guy Schalnat0d580581995-07-20 02:43:20 -05001592{
John Bowlerb5d00512012-03-09 09:15:18 -06001593 png_uint_32 key_len;
1594 png_byte new_key[80];
Guy Schalnat0d580581995-07-20 02:43:20 -05001595
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001596 png_debug(1, "in png_write_tEXt");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001597
John Bowlerb5d00512012-03-09 09:15:18 -06001598 key_len = png_check_keyword(png_ptr, key, new_key);
1599
1600 if (key_len == 0)
1601 png_error(png_ptr, "tEXt: invalid keyword");
Guy Schalnate5a37791996-06-05 15:50:50 -05001602
Andreas Dilger47a0c421997-05-16 02:46:07 -05001603 if (text == NULL || *text == '\0')
Andreas Dilger02ad0ef1997-01-17 01:34:35 -06001604 text_len = 0;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001605
Glenn Randers-Pehrson61c32d92000-02-04 23:40:16 -06001606 else
1607 text_len = png_strlen(text);
Andreas Dilger02ad0ef1997-01-17 01:34:35 -06001608
John Bowlerb5d00512012-03-09 09:15:18 -06001609 if (text_len > PNG_UINT_31_MAX - (key_len+1))
1610 png_error(png_ptr, "tEXt: text too long");
1611
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05001612 /* Make sure we include the 0 after the key */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001613 png_write_chunk_header(png_ptr, png_tEXt,
John Bowlerb5d00512012-03-09 09:15:18 -06001614 (png_uint_32)/*checked above*/(key_len + text_len + 1));
Glenn Randers-Pehrson4393a9a1999-09-17 12:27:26 -05001615 /*
1616 * We leave it to the application to meet PNG-1.0 requirements on the
1617 * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of
1618 * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them.
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001619 * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
Glenn Randers-Pehrson4393a9a1999-09-17 12:27:26 -05001620 */
John Bowlerb5d00512012-03-09 09:15:18 -06001621 png_write_chunk_data(png_ptr, new_key, key_len + 1);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001622
Andreas Dilger02ad0ef1997-01-17 01:34:35 -06001623 if (text_len)
John Bowlerb5d00512012-03-09 09:15:18 -06001624 png_write_chunk_data(png_ptr, (png_const_bytep)text, text_len);
Andreas Dilger02ad0ef1997-01-17 01:34:35 -06001625
Guy Schalnat0d580581995-07-20 02:43:20 -05001626 png_write_chunk_end(png_ptr);
1627}
Guy Schalnat51f0eb41995-09-26 05:22:39 -05001628#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05001629
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001630#ifdef PNG_WRITE_zTXt_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001631/* Write a compressed text chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001632void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001633png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001634 png_size_t text_len, int compression)
Guy Schalnat0d580581995-07-20 02:43:20 -05001635{
John Bowlerb5d00512012-03-09 09:15:18 -06001636 png_uint_32 key_len;
1637 png_byte new_key[81];
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001638 compression_state comp;
Guy Schalnat0d580581995-07-20 02:43:20 -05001639
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001640 png_debug(1, "in png_write_zTXt");
John Bowlerb5d00512012-03-09 09:15:18 -06001641 PNG_UNUSED(text_len); /* Always use strlen */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -06001642
John Bowlerb5d00512012-03-09 09:15:18 -06001643 if (compression == PNG_TEXT_COMPRESSION_NONE)
Guy Schalnate5a37791996-06-05 15:50:50 -05001644 {
John Bowlerb5d00512012-03-09 09:15:18 -06001645 png_write_tEXt(png_ptr, key, text, 0);
Andreas Dilger47a0c421997-05-16 02:46:07 -05001646 return;
Guy Schalnate5a37791996-06-05 15:50:50 -05001647 }
1648
John Bowlerb5d00512012-03-09 09:15:18 -06001649 if (compression != PNG_TEXT_COMPRESSION_zTXt)
1650 png_error(png_ptr, "zTXt: invalid compression type");
Andreas Dilger47a0c421997-05-16 02:46:07 -05001651
John Bowlerb5d00512012-03-09 09:15:18 -06001652 key_len = png_check_keyword(png_ptr, key, new_key);
1653
1654 if (key_len == 0)
1655 png_error(png_ptr, "zTXt: invalid keyword");
1656
1657 /* Add the compression method and 1 for the keyword separator. */
1658 new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
1659 ++key_len;
Glenn Randers-Pehrson61c32d92000-02-04 23:40:16 -06001660
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001661 /* Compute the compressed data; do it now for the length */
John Bowlerb5d00512012-03-09 09:15:18 -06001662 png_text_compress_init(&comp, (png_const_bytep)text,
1663 text == NULL ? 0 : strlen(text));
1664
1665 if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK)
1666 png_error(png_ptr, png_ptr->zstream.msg);
Guy Schalnat0d580581995-07-20 02:43:20 -05001667
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001668 /* Write start of chunk */
John Bowlerb5d00512012-03-09 09:15:18 -06001669 png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001670
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001671 /* Write key */
John Bowlerb5d00512012-03-09 09:15:18 -06001672 png_write_chunk_data(png_ptr, new_key, key_len);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001673
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001674 /* Write the compressed data */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001675 png_write_compressed_data_out(png_ptr, &comp);
Guy Schalnat0d580581995-07-20 02:43:20 -05001676
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001677 /* Close the chunk */
Guy Schalnat0d580581995-07-20 02:43:20 -05001678 png_write_chunk_end(png_ptr);
Guy Schalnat0d580581995-07-20 02:43:20 -05001679}
Guy Schalnat51f0eb41995-09-26 05:22:39 -05001680#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05001681
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001682#ifdef PNG_WRITE_iTXt_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001683/* Write an iTXt chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001684void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001685png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key,
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001686 png_const_charp lang, png_const_charp lang_key, png_const_charp text)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001687{
John Bowlerb5d00512012-03-09 09:15:18 -06001688 png_uint_32 key_len, prefix_len;
1689 png_size_t lang_len, lang_key_len;
1690 png_byte new_key[82];
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001691 compression_state comp;
1692
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001693 png_debug(1, "in png_write_iTXt");
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001694
John Bowlerb5d00512012-03-09 09:15:18 -06001695 key_len = png_check_keyword(png_ptr, key, new_key);
1696
1697 if (key_len == 0)
1698 png_error(png_ptr, "iTXt: invalid keyword");
Glenn Randers-Pehrson9c3ab682006-02-20 22:09:05 -06001699
John Bowlerb5d00512012-03-09 09:15:18 -06001700 /* Set the compression flag */
1701 switch (compression)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001702 {
John Bowlerb5d00512012-03-09 09:15:18 -06001703 case PNG_ITXT_COMPRESSION_NONE:
1704 case PNG_TEXT_COMPRESSION_NONE:
1705 compression = new_key[++key_len] = 0; /* no compression */
1706 break;
1707
1708 case PNG_TEXT_COMPRESSION_zTXt:
1709 case PNG_ITXT_COMPRESSION_zTXt:
1710 compression = new_key[++key_len] = 1; /* compressed */
1711 break;
1712
1713 default:
1714 png_error(png_ptr, "iTXt: invalid compression");
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001715 }
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001716
John Bowlerb5d00512012-03-09 09:15:18 -06001717 new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
1718 ++key_len; /* for the keywod separator */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001719
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001720 /* We leave it to the application to meet PNG-1.0 requirements on the
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001721 * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of
John Bowlerb5d00512012-03-09 09:15:18 -06001722 * any non-Latin-1 characters except for NEWLINE. ISO PNG, however,
1723 * specifies that the text is UTF-8 and this really doesn't require any
1724 * checking.
1725 *
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001726 * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
John Bowlerb5d00512012-03-09 09:15:18 -06001727 *
1728 * TODO: validate the language tag correctly (see the spec.)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001729 */
John Bowlerb5d00512012-03-09 09:15:18 -06001730 if (lang == NULL) lang = ""; /* empty language is valid */
1731 lang_len = strlen(lang)+1;
1732 if (lang_key == NULL) lang_key = ""; /* may be empty */
1733 lang_key_len = strlen(lang_key)+1;
1734 if (text == NULL) text = ""; /* may be empty */
Glenn Randers-Pehrson61c32d92000-02-04 23:40:16 -06001735
John Bowlerb5d00512012-03-09 09:15:18 -06001736 prefix_len = key_len;
1737 if (lang_len > PNG_UINT_31_MAX-prefix_len)
1738 prefix_len = PNG_UINT_31_MAX;
1739 else
1740 prefix_len = (png_uint_32)(prefix_len + lang_len);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001741
John Bowlerb5d00512012-03-09 09:15:18 -06001742 if (lang_key_len > PNG_UINT_31_MAX-prefix_len)
1743 prefix_len = PNG_UINT_31_MAX;
1744 else
1745 prefix_len = (png_uint_32)(prefix_len + lang_key_len);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001746
John Bowlerb5d00512012-03-09 09:15:18 -06001747 png_text_compress_init(&comp, (png_const_bytep)text, strlen(text));
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001748
John Bowlerb5d00512012-03-09 09:15:18 -06001749 if (compression)
1750 {
1751 if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK)
1752 png_error(png_ptr, png_ptr->zstream.msg);
1753 }
Glenn Randers-Pehrson61c32d92000-02-04 23:40:16 -06001754
John Bowlerb5d00512012-03-09 09:15:18 -06001755 else
1756 {
1757 if (comp.input_len > PNG_UINT_31_MAX-prefix_len)
1758 png_error(png_ptr, "iTXt: uncompressed text too long");
1759 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001760
John Bowlerb5d00512012-03-09 09:15:18 -06001761 png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001762
John Bowlerb5d00512012-03-09 09:15:18 -06001763 png_write_chunk_data(png_ptr, new_key, key_len);
1764
1765 png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len);
1766
1767 png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len);
1768
1769 if (compression)
1770 png_write_compressed_data_out(png_ptr, &comp);
1771
1772 else
1773 png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.input_len);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001774
1775 png_write_chunk_end(png_ptr);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001776}
1777#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -05001778
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001779#ifdef PNG_WRITE_oFFs_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001780/* Write the oFFs chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001781void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001782png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001783 int unit_type)
Andreas Dilger47a0c421997-05-16 02:46:07 -05001784{
1785 png_byte buf[9];
1786
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001787 png_debug(1, "in png_write_oFFs");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001788
Andreas Dilger47a0c421997-05-16 02:46:07 -05001789 if (unit_type >= PNG_OFFSET_LAST)
1790 png_warning(png_ptr, "Unrecognized unit type for oFFs chunk");
1791
Glenn Randers-Pehrsonb1828932001-06-23 08:03:17 -05001792 png_save_int_32(buf, x_offset);
1793 png_save_int_32(buf + 4, y_offset);
Andreas Dilger47a0c421997-05-16 02:46:07 -05001794 buf[8] = (png_byte)unit_type;
1795
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001796 png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9);
Andreas Dilger47a0c421997-05-16 02:46:07 -05001797}
1798#endif
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001799#ifdef PNG_WRITE_pCAL_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001800/* Write the pCAL chunk (described in the PNG extensions document) */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001801void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001802png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0,
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05001803 png_int_32 X1, int type, int nparams, png_const_charp units,
1804 png_charpp params)
Andreas Dilger47a0c421997-05-16 02:46:07 -05001805{
John Bowlerb5d00512012-03-09 09:15:18 -06001806 png_uint_32 purpose_len;
1807 png_size_t units_len, total_len;
John Bowlerf3f7e142011-09-09 07:32:37 -05001808 png_size_tp params_len;
Andreas Dilger47a0c421997-05-16 02:46:07 -05001809 png_byte buf[10];
John Bowlerb5d00512012-03-09 09:15:18 -06001810 png_byte new_purpose[80];
Andreas Dilger47a0c421997-05-16 02:46:07 -05001811 int i;
1812
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001813 png_debug1(1, "in png_write_pCAL (%d parameters)", nparams);
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001814
Andreas Dilger47a0c421997-05-16 02:46:07 -05001815 if (type >= PNG_EQUATION_LAST)
John Bowlerb5d00512012-03-09 09:15:18 -06001816 png_error(png_ptr, "Unrecognized equation type for pCAL chunk");
Andreas Dilger47a0c421997-05-16 02:46:07 -05001817
John Bowlerb5d00512012-03-09 09:15:18 -06001818 purpose_len = png_check_keyword(png_ptr, purpose, new_purpose);
1819
1820 if (purpose_len == 0)
1821 png_error(png_ptr, "pCAL: invalid keyword");
1822
1823 ++purpose_len; /* terminator */
1824
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001825 png_debug1(3, "pCAL purpose length = %d", (int)purpose_len);
Andreas Dilger47a0c421997-05-16 02:46:07 -05001826 units_len = png_strlen(units) + (nparams == 0 ? 0 : 1);
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001827 png_debug1(3, "pCAL units length = %d", (int)units_len);
Andreas Dilger47a0c421997-05-16 02:46:07 -05001828 total_len = purpose_len + units_len + 10;
1829
John Bowlerf3f7e142011-09-09 07:32:37 -05001830 params_len = (png_size_tp)png_malloc(png_ptr,
1831 (png_alloc_size_t)(nparams * png_sizeof(png_size_t)));
Andreas Dilger47a0c421997-05-16 02:46:07 -05001832
1833 /* Find the length of each parameter, making sure we don't count the
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001834 * null terminator for the last parameter.
1835 */
Andreas Dilger47a0c421997-05-16 02:46:07 -05001836 for (i = 0; i < nparams; i++)
1837 {
1838 params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001839 png_debug2(3, "pCAL parameter %d length = %lu", i,
Glenn Randers-Pehrsond2332872010-10-12 19:19:28 -05001840 (unsigned long)params_len[i]);
John Bowlerf3f7e142011-09-09 07:32:37 -05001841 total_len += params_len[i];
Andreas Dilger47a0c421997-05-16 02:46:07 -05001842 }
1843
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001844 png_debug1(3, "pCAL total length = %d", (int)total_len);
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001845 png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len);
John Bowlerb5d00512012-03-09 09:15:18 -06001846 png_write_chunk_data(png_ptr, new_purpose, purpose_len);
Andreas Dilger47a0c421997-05-16 02:46:07 -05001847 png_save_int_32(buf, X0);
1848 png_save_int_32(buf + 4, X1);
1849 buf[8] = (png_byte)type;
1850 buf[9] = (png_byte)nparams;
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001851 png_write_chunk_data(png_ptr, buf, (png_size_t)10);
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001852 png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len);
Andreas Dilger47a0c421997-05-16 02:46:07 -05001853
Andreas Dilger47a0c421997-05-16 02:46:07 -05001854 for (i = 0; i < nparams; i++)
1855 {
John Bowlerf3f7e142011-09-09 07:32:37 -05001856 png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]);
Andreas Dilger47a0c421997-05-16 02:46:07 -05001857 }
1858
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -06001859 png_free(png_ptr, params_len);
Andreas Dilger47a0c421997-05-16 02:46:07 -05001860 png_write_chunk_end(png_ptr);
1861}
1862#endif
1863
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001864#ifdef PNG_WRITE_sCAL_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001865/* Write the sCAL chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001866void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001867png_write_sCAL_s(png_structrp png_ptr, int unit, png_const_charp width,
Glenn Randers-Pehrsone600c512010-08-18 07:25:46 -05001868 png_const_charp height)
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001869{
Glenn Randers-Pehrson6bc53be2006-06-16 07:52:03 -05001870 png_byte buf[64];
1871 png_size_t wlen, hlen, total_len;
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001872
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001873 png_debug(1, "in png_write_sCAL_s");
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001874
Glenn Randers-Pehrson6bc53be2006-06-16 07:52:03 -05001875 wlen = png_strlen(width);
1876 hlen = png_strlen(height);
1877 total_len = wlen + hlen + 2;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001878
Glenn Randers-Pehrson6bc53be2006-06-16 07:52:03 -05001879 if (total_len > 64)
1880 {
1881 png_warning(png_ptr, "Can't write sCAL (buffer too small)");
1882 return;
1883 }
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001884
Glenn Randers-Pehrson6bc53be2006-06-16 07:52:03 -05001885 buf[0] = (png_byte)unit;
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05001886 png_memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */
1887 png_memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001888
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001889 png_debug1(3, "sCAL total length = %u", (unsigned int)total_len);
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001890 png_write_complete_chunk(png_ptr, png_sCAL, buf, total_len);
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001891}
1892#endif
1893
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001894#ifdef PNG_WRITE_pHYs_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001895/* Write the pHYs chunk */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001896void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001897png_write_pHYs(png_structrp png_ptr, png_uint_32 x_pixels_per_unit,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06001898 png_uint_32 y_pixels_per_unit,
1899 int unit_type)
Guy Schalnat0d580581995-07-20 02:43:20 -05001900{
1901 png_byte buf[9];
1902
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001903 png_debug(1, "in png_write_pHYs");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001904
Guy Schalnate5a37791996-06-05 15:50:50 -05001905 if (unit_type >= PNG_RESOLUTION_LAST)
1906 png_warning(png_ptr, "Unrecognized unit type for pHYs chunk");
1907
Guy Schalnat0d580581995-07-20 02:43:20 -05001908 png_save_uint_32(buf, x_pixels_per_unit);
1909 png_save_uint_32(buf + 4, y_pixels_per_unit);
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06001910 buf[8] = (png_byte)unit_type;
Guy Schalnat0d580581995-07-20 02:43:20 -05001911
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001912 png_write_complete_chunk(png_ptr, png_pHYs, buf, (png_size_t)9);
Guy Schalnat0d580581995-07-20 02:43:20 -05001913}
Guy Schalnat51f0eb41995-09-26 05:22:39 -05001914#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05001915
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05001916#ifdef PNG_WRITE_tIME_SUPPORTED
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -06001917/* Write the tIME chunk. Use either png_convert_from_struct_tm()
1918 * or png_convert_from_time_t(), or fill in the structure yourself.
1919 */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001920void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001921png_write_tIME(png_structrp png_ptr, png_const_timep mod_time)
Guy Schalnat0d580581995-07-20 02:43:20 -05001922{
1923 png_byte buf[7];
1924
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001925 png_debug(1, "in png_write_tIME");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001926
Guy Schalnate5a37791996-06-05 15:50:50 -05001927 if (mod_time->month > 12 || mod_time->month < 1 ||
1928 mod_time->day > 31 || mod_time->day < 1 ||
1929 mod_time->hour > 23 || mod_time->second > 60)
1930 {
1931 png_warning(png_ptr, "Invalid time specified for tIME chunk");
1932 return;
1933 }
1934
Guy Schalnat0d580581995-07-20 02:43:20 -05001935 png_save_uint_16(buf, mod_time->year);
1936 buf[2] = mod_time->month;
1937 buf[3] = mod_time->day;
1938 buf[4] = mod_time->hour;
1939 buf[5] = mod_time->minute;
1940 buf[6] = mod_time->second;
1941
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001942 png_write_complete_chunk(png_ptr, png_tIME, buf, (png_size_t)7);
Guy Schalnat0d580581995-07-20 02:43:20 -05001943}
Guy Schalnat51f0eb41995-09-26 05:22:39 -05001944#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05001945
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001946/* Initializes the row writing capability of libpng */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05001947void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06001948png_write_start_row(png_structrp png_ptr)
Guy Schalnat0d580581995-07-20 02:43:20 -05001949{
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05001950#ifdef PNG_WRITE_INTERLACING_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001951 /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001952
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001953 /* Start of interlace block */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001954 static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001955
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001956 /* Offset to next interlace block */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001957 static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001958
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001959 /* Start of interlace block in the y direction */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001960 static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001961
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001962 /* Offset to next interlace block in the y direction */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001963 static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
Glenn Randers-Pehrson074af5e1999-11-28 23:32:18 -06001964#endif
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06001965
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001966 png_alloc_size_t buf_size;
1967 int usr_pixel_depth;
Andreas Dilger47a0c421997-05-16 02:46:07 -05001968
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05001969 png_debug(1, "in png_write_start_row");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05001970
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001971 usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth;
1972 buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1;
1973
1974 /* 1.5.6: added to allow checking in the row write code. */
1975 png_ptr->transformed_pixel_depth = png_ptr->pixel_depth;
1976 png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth;
Andreas Dilger47a0c421997-05-16 02:46:07 -05001977
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001978 /* Set up row buffer */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001979 png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, buf_size);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001980
Andreas Dilger47a0c421997-05-16 02:46:07 -05001981 png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;
Guy Schalnate5a37791996-06-05 15:50:50 -05001982
Glenn Randers-Pehrsondbd40142009-08-31 08:42:02 -05001983#ifdef PNG_WRITE_FILTER_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05001984 /* Set up filtering buffer, if using this filter */
Guy Schalnate5a37791996-06-05 15:50:50 -05001985 if (png_ptr->do_filter & PNG_FILTER_SUB)
Guy Schalnat0d580581995-07-20 02:43:20 -05001986 {
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05001987 png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, png_ptr->rowbytes + 1);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05001988
Andreas Dilger47a0c421997-05-16 02:46:07 -05001989 png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
Guy Schalnate5a37791996-06-05 15:50:50 -05001990 }
1991
Andreas Dilger47a0c421997-05-16 02:46:07 -05001992 /* We only need to keep the previous row if we are using one of these. */
Guy Schalnate5a37791996-06-05 15:50:50 -05001993 if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH))
1994 {
Glenn Randers-Pehrsondfa99af2009-10-29 23:33:46 -05001995 /* Set up previous row buffer */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05001996 png_ptr->prev_row = (png_bytep)png_calloc(png_ptr, buf_size);
Guy Schalnate5a37791996-06-05 15:50:50 -05001997
1998 if (png_ptr->do_filter & PNG_FILTER_UP)
1999 {
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05002000 png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002001 png_ptr->rowbytes + 1);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002002
Andreas Dilger47a0c421997-05-16 02:46:07 -05002003 png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
Guy Schalnate5a37791996-06-05 15:50:50 -05002004 }
2005
2006 if (png_ptr->do_filter & PNG_FILTER_AVG)
2007 {
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -06002008 png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002009 png_ptr->rowbytes + 1);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002010
Andreas Dilger47a0c421997-05-16 02:46:07 -05002011 png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
Guy Schalnate5a37791996-06-05 15:50:50 -05002012 }
2013
2014 if (png_ptr->do_filter & PNG_FILTER_PAETH)
2015 {
Glenn Randers-Pehrsonbeb572e2006-08-19 13:59:24 -05002016 png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002017 png_ptr->rowbytes + 1);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002018
Andreas Dilger47a0c421997-05-16 02:46:07 -05002019 png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
Guy Schalnate5a37791996-06-05 15:50:50 -05002020 }
Guy Schalnat0d580581995-07-20 02:43:20 -05002021 }
Glenn Randers-Pehrsondbd40142009-08-31 08:42:02 -05002022#endif /* PNG_WRITE_FILTER_SUPPORTED */
Guy Schalnat0d580581995-07-20 02:43:20 -05002023
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -06002024#ifdef PNG_WRITE_INTERLACING_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002025 /* If interlaced, we need to set up width and height of pass */
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002026 if (png_ptr->interlaced)
Guy Schalnat0d580581995-07-20 02:43:20 -05002027 {
2028 if (!(png_ptr->transformations & PNG_INTERLACE))
2029 {
2030 png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002031 png_pass_ystart[0]) / png_pass_yinc[0];
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002032
Andreas Dilger47a0c421997-05-16 02:46:07 -05002033 png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 -
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002034 png_pass_start[0]) / png_pass_inc[0];
Guy Schalnat0d580581995-07-20 02:43:20 -05002035 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002036
Guy Schalnat0d580581995-07-20 02:43:20 -05002037 else
2038 {
2039 png_ptr->num_rows = png_ptr->height;
2040 png_ptr->usr_width = png_ptr->width;
2041 }
2042 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002043
Guy Schalnat0d580581995-07-20 02:43:20 -05002044 else
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -06002045#endif
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002046 {
Guy Schalnat0d580581995-07-20 02:43:20 -05002047 png_ptr->num_rows = png_ptr->height;
2048 png_ptr->usr_width = png_ptr->width;
2049 }
Guy Schalnat0d580581995-07-20 02:43:20 -05002050}
2051
Andreas Dilger47a0c421997-05-16 02:46:07 -05002052/* Internal use only. Called when finished processing a row of data. */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05002053void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06002054png_write_finish_row(png_structrp png_ptr)
Guy Schalnat0d580581995-07-20 02:43:20 -05002055{
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05002056#ifdef PNG_WRITE_INTERLACING_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002057 /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06002058
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002059 /* Start of interlace block */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05002060 static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06002061
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002062 /* Offset to next interlace block */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05002063 static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06002064
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002065 /* Start of interlace block in the y direction */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05002066 static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06002067
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002068 /* Offset to next interlace block in the y direction */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05002069 static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
Glenn Randers-Pehrson074af5e1999-11-28 23:32:18 -06002070#endif
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06002071
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05002072 png_debug(1, "in png_write_finish_row");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05002073
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05002074 /* Next row */
Guy Schalnat0d580581995-07-20 02:43:20 -05002075 png_ptr->row_number++;
Guy Schalnatc21f90c1996-06-17 16:24:45 -05002076
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002077 /* See if we are done */
Guy Schalnat6d764711995-12-19 03:22:19 -06002078 if (png_ptr->row_number < png_ptr->num_rows)
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002079 return;
Guy Schalnat0d580581995-07-20 02:43:20 -05002080
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -06002081#ifdef PNG_WRITE_INTERLACING_SUPPORTED
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002082 /* If interlaced, go to next pass */
Guy Schalnat0d580581995-07-20 02:43:20 -05002083 if (png_ptr->interlaced)
2084 {
2085 png_ptr->row_number = 0;
2086 if (png_ptr->transformations & PNG_INTERLACE)
2087 {
2088 png_ptr->pass++;
2089 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002090
Guy Schalnat0d580581995-07-20 02:43:20 -05002091 else
2092 {
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002093 /* Loop until we find a non-zero width or height pass */
Guy Schalnat0d580581995-07-20 02:43:20 -05002094 do
2095 {
2096 png_ptr->pass++;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002097
Guy Schalnat0d580581995-07-20 02:43:20 -05002098 if (png_ptr->pass >= 7)
2099 break;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002100
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002101 png_ptr->usr_width = (png_ptr->width +
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002102 png_pass_inc[png_ptr->pass] - 1 -
2103 png_pass_start[png_ptr->pass]) /
2104 png_pass_inc[png_ptr->pass];
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002105
Guy Schalnat0d580581995-07-20 02:43:20 -05002106 png_ptr->num_rows = (png_ptr->height +
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002107 png_pass_yinc[png_ptr->pass] - 1 -
2108 png_pass_ystart[png_ptr->pass]) /
2109 png_pass_yinc[png_ptr->pass];
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002110
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002111 if (png_ptr->transformations & PNG_INTERLACE)
2112 break;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002113
Guy Schalnat0d580581995-07-20 02:43:20 -05002114 } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);
2115
2116 }
2117
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002118 /* Reset the row above the image for the next pass */
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002119 if (png_ptr->pass < 7)
Guy Schalnatc21f90c1996-06-17 16:24:45 -05002120 {
Andreas Dilger47a0c421997-05-16 02:46:07 -05002121 if (png_ptr->prev_row != NULL)
Glenn Randers-Pehrson5c6aeb21998-12-29 11:47:59 -06002122 png_memset(png_ptr->prev_row, 0,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002123 (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels*
2124 png_ptr->usr_bit_depth, png_ptr->width)) + 1);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002125
Guy Schalnat0d580581995-07-20 02:43:20 -05002126 return;
Guy Schalnatc21f90c1996-06-17 16:24:45 -05002127 }
Guy Schalnat0d580581995-07-20 02:43:20 -05002128 }
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -06002129#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05002130
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002131 /* If we get here, we've just written the last row, so we need
Guy Schalnat0d580581995-07-20 02:43:20 -05002132 to flush the compressor */
John Bowlerb5d00512012-03-09 09:15:18 -06002133 png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH);
Guy Schalnat0d580581995-07-20 02:43:20 -05002134}
2135
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002136#ifdef PNG_WRITE_INTERLACING_SUPPORTED
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -06002137/* Pick out the correct pixels for the interlace pass.
2138 * The basic idea here is to go through the row with a source
2139 * pointer and a destination pointer (sp and dp), and copy the
2140 * correct pixels for the pass. As the row gets compacted,
2141 * sp will always be >= dp, so we should never overwrite anything.
2142 * See the default: case for the easiest code to understand.
2143 */
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05002144void /* PRIVATE */
Guy Schalnat6d764711995-12-19 03:22:19 -06002145png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
Guy Schalnat0d580581995-07-20 02:43:20 -05002146{
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002147 /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06002148
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002149 /* Start of interlace block */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05002150 static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06002151
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002152 /* Offset to next interlace block */
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05002153 static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06002154
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05002155 png_debug(1, "in png_do_write_interlace");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05002156
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002157 /* We don't have to do anything on the last pass (6) */
Andreas Dilger47a0c421997-05-16 02:46:07 -05002158 if (pass < 6)
Guy Schalnat0d580581995-07-20 02:43:20 -05002159 {
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002160 /* Each pixel depth is handled separately */
Guy Schalnat0d580581995-07-20 02:43:20 -05002161 switch (row_info->pixel_depth)
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002162 {
Guy Schalnat0d580581995-07-20 02:43:20 -05002163 case 1:
2164 {
Guy Schalnat6d764711995-12-19 03:22:19 -06002165 png_bytep sp;
2166 png_bytep dp;
Guy Schalnat0d580581995-07-20 02:43:20 -05002167 int shift;
2168 int d;
2169 int value;
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002170 png_uint_32 i;
2171 png_uint_32 row_width = row_info->width;
Guy Schalnat0d580581995-07-20 02:43:20 -05002172
2173 dp = row;
2174 d = 0;
2175 shift = 7;
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002176
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002177 for (i = png_pass_start[pass]; i < row_width;
Guy Schalnat0d580581995-07-20 02:43:20 -05002178 i += png_pass_inc[pass])
2179 {
2180 sp = row + (png_size_t)(i >> 3);
Glenn Randers-Pehrson61c32d92000-02-04 23:40:16 -06002181 value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;
Guy Schalnat0d580581995-07-20 02:43:20 -05002182 d |= (value << shift);
2183
2184 if (shift == 0)
2185 {
2186 shift = 7;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002187 *dp++ = (png_byte)d;
Guy Schalnat0d580581995-07-20 02:43:20 -05002188 d = 0;
2189 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002190
Guy Schalnat0d580581995-07-20 02:43:20 -05002191 else
2192 shift--;
2193
2194 }
2195 if (shift != 7)
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002196 *dp = (png_byte)d;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002197
Guy Schalnat0d580581995-07-20 02:43:20 -05002198 break;
2199 }
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002200
Guy Schalnat0d580581995-07-20 02:43:20 -05002201 case 2:
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002202 {
Guy Schalnat6d764711995-12-19 03:22:19 -06002203 png_bytep sp;
2204 png_bytep dp;
Guy Schalnat0d580581995-07-20 02:43:20 -05002205 int shift;
2206 int d;
2207 int value;
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002208 png_uint_32 i;
2209 png_uint_32 row_width = row_info->width;
Guy Schalnat0d580581995-07-20 02:43:20 -05002210
2211 dp = row;
2212 shift = 6;
2213 d = 0;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002214
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002215 for (i = png_pass_start[pass]; i < row_width;
Guy Schalnat0d580581995-07-20 02:43:20 -05002216 i += png_pass_inc[pass])
2217 {
2218 sp = row + (png_size_t)(i >> 2);
Glenn Randers-Pehrson61c32d92000-02-04 23:40:16 -06002219 value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;
Guy Schalnat0d580581995-07-20 02:43:20 -05002220 d |= (value << shift);
2221
2222 if (shift == 0)
2223 {
2224 shift = 6;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002225 *dp++ = (png_byte)d;
Guy Schalnat0d580581995-07-20 02:43:20 -05002226 d = 0;
2227 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002228
Guy Schalnat0d580581995-07-20 02:43:20 -05002229 else
2230 shift -= 2;
2231 }
2232 if (shift != 6)
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002233 *dp = (png_byte)d;
2234
Guy Schalnat0d580581995-07-20 02:43:20 -05002235 break;
2236 }
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002237
Guy Schalnat0d580581995-07-20 02:43:20 -05002238 case 4:
2239 {
Guy Schalnat6d764711995-12-19 03:22:19 -06002240 png_bytep sp;
2241 png_bytep dp;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002242 int shift;
Guy Schalnat0d580581995-07-20 02:43:20 -05002243 int d;
2244 int value;
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002245 png_uint_32 i;
2246 png_uint_32 row_width = row_info->width;
Guy Schalnat0d580581995-07-20 02:43:20 -05002247
2248 dp = row;
2249 shift = 4;
2250 d = 0;
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002251 for (i = png_pass_start[pass]; i < row_width;
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002252 i += png_pass_inc[pass])
Guy Schalnat0d580581995-07-20 02:43:20 -05002253 {
2254 sp = row + (png_size_t)(i >> 1);
Glenn Randers-Pehrson61c32d92000-02-04 23:40:16 -06002255 value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;
Guy Schalnat0d580581995-07-20 02:43:20 -05002256 d |= (value << shift);
2257
2258 if (shift == 0)
2259 {
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002260 shift = 4;
2261 *dp++ = (png_byte)d;
Guy Schalnat0d580581995-07-20 02:43:20 -05002262 d = 0;
2263 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002264
Guy Schalnat0d580581995-07-20 02:43:20 -05002265 else
2266 shift -= 4;
2267 }
2268 if (shift != 4)
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002269 *dp = (png_byte)d;
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002270
Guy Schalnat0d580581995-07-20 02:43:20 -05002271 break;
2272 }
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002273
Guy Schalnat0d580581995-07-20 02:43:20 -05002274 default:
2275 {
Guy Schalnat6d764711995-12-19 03:22:19 -06002276 png_bytep sp;
2277 png_bytep dp;
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002278 png_uint_32 i;
2279 png_uint_32 row_width = row_info->width;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002280 png_size_t pixel_bytes;
Guy Schalnat0d580581995-07-20 02:43:20 -05002281
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002282 /* Start at the beginning */
Guy Schalnat0d580581995-07-20 02:43:20 -05002283 dp = row;
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002284
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002285 /* Find out how many bytes each pixel takes up */
Guy Schalnat0d580581995-07-20 02:43:20 -05002286 pixel_bytes = (row_info->pixel_depth >> 3);
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002287
2288 /* Loop through the row, only looking at the pixels that matter */
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002289 for (i = png_pass_start[pass]; i < row_width;
Guy Schalnat0d580581995-07-20 02:43:20 -05002290 i += png_pass_inc[pass])
2291 {
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002292 /* Find out where the original pixel is */
Glenn Randers-Pehrsona357b991998-02-08 20:56:40 -06002293 sp = row + (png_size_t)i * pixel_bytes;
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002294
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002295 /* Move the pixel */
Guy Schalnat0d580581995-07-20 02:43:20 -05002296 if (dp != sp)
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002297 png_memcpy(dp, sp, pixel_bytes);
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002298
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002299 /* Next pixel */
Guy Schalnat0d580581995-07-20 02:43:20 -05002300 dp += pixel_bytes;
2301 }
Guy Schalnatb2e01bd1996-01-26 01:38:47 -06002302 break;
Guy Schalnat0d580581995-07-20 02:43:20 -05002303 }
2304 }
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002305 /* Set new row width */
Guy Schalnat0d580581995-07-20 02:43:20 -05002306 row_info->width = (row_info->width +
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002307 png_pass_inc[pass] - 1 -
2308 png_pass_start[pass]) /
2309 png_pass_inc[pass];
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002310
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002311 row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
2312 row_info->width);
Guy Schalnat0d580581995-07-20 02:43:20 -05002313 }
2314}
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002315#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05002316
Andreas Dilger47a0c421997-05-16 02:46:07 -05002317/* This filters the row, chooses which filter to use, if it has not already
2318 * been specified by the application, and then writes the row out with the
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -06002319 * chosen filter.
2320 */
John Bowler5d567862011-12-24 09:12:00 -06002321static void png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05002322 png_size_t row_bytes);
John Bowlerc5bef942011-05-05 17:35:39 -05002323
Glenn Randers-Pehrson78067772004-11-02 21:49:39 -06002324#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002325#define PNG_HISHIFT 10
Glenn Randers-Pehrsonea3bcd71998-03-07 14:33:00 -06002326#define PNG_LOMASK ((png_uint_32)0xffffL)
2327#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT))
Glenn Randers-Pehrson75294572000-05-06 14:09:57 -05002328void /* PRIVATE */
John Bowler5d567862011-12-24 09:12:00 -06002329png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
Guy Schalnat0d580581995-07-20 02:43:20 -05002330{
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05002331 png_bytep best_row;
Glenn Randers-Pehrsondbd40142009-08-31 08:42:02 -05002332#ifdef PNG_WRITE_FILTER_SUPPORTED
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05002333 png_bytep prev_row, row_buf;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002334 png_uint_32 mins, bpp;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002335 png_byte filter_to_do = png_ptr->do_filter;
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002336 png_size_t row_bytes = row_info->rowbytes;
Glenn Randers-Pehrson9d8b41e2009-07-19 14:45:43 -05002337#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05002338 int num_p_filters = png_ptr->num_prev_filters;
Glenn Randers-Pehrson821b7102010-06-24 16:16:32 -05002339#endif
Glenn Randers-Pehrson9d8b41e2009-07-19 14:45:43 -05002340
2341 png_debug(1, "in png_write_find_filter");
2342
2343#ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Glenn Randers-Pehrson4ace0e12009-07-19 15:04:35 -05002344 if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS)
Glenn Randers-Pehrson9c90d7f2009-07-19 13:11:25 -05002345 {
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002346 /* These will never be selected so we need not test them. */
2347 filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH);
Glenn Randers-Pehrson9c90d7f2009-07-19 13:11:25 -05002348 }
Glenn Randers-Pehrson821b7102010-06-24 16:16:32 -05002349#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05002350
Glenn Randers-Pehrson4bb4d012009-05-20 12:45:29 -05002351 /* Find out how many bytes offset each pixel is */
Glenn Randers-Pehrson272489d2004-08-04 06:34:52 -05002352 bpp = (row_info->pixel_depth + 7) >> 3;
Guy Schalnate5a37791996-06-05 15:50:50 -05002353
2354 prev_row = png_ptr->prev_row;
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05002355#endif
2356 best_row = png_ptr->row_buf;
Glenn Randers-Pehrsondbd40142009-08-31 08:42:02 -05002357#ifdef PNG_WRITE_FILTER_SUPPORTED
Glenn Randers-Pehrson145f5c82008-07-10 09:13:13 -05002358 row_buf = best_row;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002359 mins = PNG_MAXSUM;
Guy Schalnat0d580581995-07-20 02:43:20 -05002360
Andreas Dilger47a0c421997-05-16 02:46:07 -05002361 /* The prediction method we use is to find which method provides the
2362 * smallest value when summing the absolute values of the distances
Glenn Randers-Pehrson8686fff1998-05-21 09:27:50 -05002363 * from zero, using anything >= 128 as negative numbers. This is known
Andreas Dilger47a0c421997-05-16 02:46:07 -05002364 * as the "minimum sum of absolute differences" heuristic. Other
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002365 * heuristics are the "weighted minimum sum of absolute differences"
Andreas Dilger47a0c421997-05-16 02:46:07 -05002366 * (experimental and can in theory improve compression), and the "zlib
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -05002367 * predictive" method (not implemented yet), which does test compressions
2368 * of lines using different filter methods, and then chooses the
2369 * (series of) filter(s) that give minimum compressed data size (VERY
Andreas Dilger47a0c421997-05-16 02:46:07 -05002370 * computationally expensive).
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -05002371 *
2372 * GRR 980525: consider also
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002373 *
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -05002374 * (1) minimum sum of absolute differences from running average (i.e.,
2375 * keep running sum of non-absolute differences & count of bytes)
2376 * [track dispersion, too? restart average if dispersion too large?]
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002377 *
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -05002378 * (1b) minimum sum of absolute differences from sliding average, probably
2379 * with window size <= deflate window (usually 32K)
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002380 *
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -05002381 * (2) minimum sum of squared differences from zero or running average
2382 * (i.e., ~ root-mean-square approach)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002383 */
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002384
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002385
Guy Schalnate5a37791996-06-05 15:50:50 -05002386 /* We don't need to test the 'no filter' case if this is the only filter
Andreas Dilger47a0c421997-05-16 02:46:07 -05002387 * that has been chosen, as it doesn't actually do anything to the data.
2388 */
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002389 if ((filter_to_do & PNG_FILTER_NONE) && filter_to_do != PNG_FILTER_NONE)
Guy Schalnat0d580581995-07-20 02:43:20 -05002390 {
Guy Schalnate5a37791996-06-05 15:50:50 -05002391 png_bytep rp;
2392 png_uint_32 sum = 0;
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002393 png_size_t i;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002394 int v;
Guy Schalnat0d580581995-07-20 02:43:20 -05002395
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002396 for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
Guy Schalnate5a37791996-06-05 15:50:50 -05002397 {
2398 v = *rp;
2399 sum += (v < 128) ? v : 256 - v;
2400 }
Andreas Dilger47a0c421997-05-16 02:46:07 -05002401
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002402#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -05002403 if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
2404 {
2405 png_uint_32 sumhi, sumlo;
Glenn Randers-Pehrsond0dce401998-05-09 10:02:29 -05002406 int j;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002407 sumlo = sum & PNG_LOMASK;
2408 sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */
2409
2410 /* Reduce the sum if we match any of the previous rows */
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002411 for (j = 0; j < num_p_filters; j++)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002412 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002413 if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002414 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002415 sumlo = (sumlo * png_ptr->filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002416 PNG_WEIGHT_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002417
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002418 sumhi = (sumhi * png_ptr->filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002419 PNG_WEIGHT_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002420 }
2421 }
2422
2423 /* Factor in the cost of this filter (this is here for completeness,
2424 * but it makes no sense to have a "cost" for the NONE filter, as
2425 * it has the minimum possible computational cost - none).
2426 */
2427 sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002428 PNG_COST_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002429
Andreas Dilger47a0c421997-05-16 02:46:07 -05002430 sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002431 PNG_COST_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002432
2433 if (sumhi > PNG_HIMASK)
2434 sum = PNG_MAXSUM;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002435
Andreas Dilger47a0c421997-05-16 02:46:07 -05002436 else
2437 sum = (sumhi << PNG_HISHIFT) + sumlo;
2438 }
2439#endif
Guy Schalnate5a37791996-06-05 15:50:50 -05002440 mins = sum;
Guy Schalnat0d580581995-07-20 02:43:20 -05002441 }
2442
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002443 /* Sub filter */
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002444 if (filter_to_do == PNG_FILTER_SUB)
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002445 /* It's the only filter so no testing is needed */
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002446 {
2447 png_bytep rp, lp, dp;
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002448 png_size_t i;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002449
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002450 for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
2451 i++, rp++, dp++)
2452 {
2453 *dp = *rp;
2454 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002455
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002456 for (lp = row_buf + 1; i < row_bytes;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002457 i++, rp++, lp++, dp++)
2458 {
2459 *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
2460 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002461
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002462 best_row = png_ptr->sub_row;
2463 }
2464
2465 else if (filter_to_do & PNG_FILTER_SUB)
Guy Schalnat0d580581995-07-20 02:43:20 -05002466 {
Guy Schalnate5a37791996-06-05 15:50:50 -05002467 png_bytep rp, dp, lp;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002468 png_uint_32 sum = 0, lmins = mins;
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002469 png_size_t i;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002470 int v;
2471
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002472#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002473 /* We temporarily increase the "minimum sum" by the factor we
Andreas Dilger47a0c421997-05-16 02:46:07 -05002474 * would reduce the sum of this filter, so that we can do the
2475 * early exit comparison without scaling the sum each time.
2476 */
2477 if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
2478 {
Glenn Randers-Pehrsond0dce401998-05-09 10:02:29 -05002479 int j;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002480 png_uint_32 lmhi, lmlo;
2481 lmlo = lmins & PNG_LOMASK;
2482 lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
2483
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002484 for (j = 0; j < num_p_filters; j++)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002485 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002486 if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002487 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002488 lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002489 PNG_WEIGHT_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002490
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002491 lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002492 PNG_WEIGHT_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002493 }
2494 }
2495
2496 lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002497 PNG_COST_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002498
Andreas Dilger47a0c421997-05-16 02:46:07 -05002499 lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002500 PNG_COST_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002501
2502 if (lmhi > PNG_HIMASK)
2503 lmins = PNG_MAXSUM;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002504
Andreas Dilger47a0c421997-05-16 02:46:07 -05002505 else
2506 lmins = (lmhi << PNG_HISHIFT) + lmlo;
2507 }
2508#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05002509
Guy Schalnate5a37791996-06-05 15:50:50 -05002510 for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
2511 i++, rp++, dp++)
2512 {
2513 v = *dp = *rp;
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002514
Guy Schalnate5a37791996-06-05 15:50:50 -05002515 sum += (v < 128) ? v : 256 - v;
2516 }
Glenn Randers-Pehrson67439c42010-08-19 07:01:09 -05002517
Glenn Randers-Pehrson5b5dcf82004-07-17 22:45:44 -05002518 for (lp = row_buf + 1; i < row_bytes;
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -06002519 i++, rp++, lp++, dp++)
Guy Schalnate5a37791996-06-05 15:50:50 -05002520 {
2521 v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002522
Guy Schalnate5a37791996-06-05 15:50:50 -05002523 sum += (v < 128) ? v : 256 - v;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002524
2525 if (sum > lmins) /* We are already worse, don't continue. */
2526 break;
Guy Schalnate5a37791996-06-05 15:50:50 -05002527 }
Andreas Dilger47a0c421997-05-16 02:46:07 -05002528
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002529#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -05002530 if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
2531 {
Glenn Randers-Pehrsond0dce401998-05-09 10:02:29 -05002532 int j;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002533 png_uint_32 sumhi, sumlo;
2534 sumlo = sum & PNG_LOMASK;
2535 sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
2536
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002537 for (j = 0; j < num_p_filters; j++)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002538 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002539 if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002540 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002541 sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002542 PNG_WEIGHT_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002543
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002544 sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002545 PNG_WEIGHT_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002546 }
2547 }
2548
2549 sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002550 PNG_COST_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002551
Andreas Dilger47a0c421997-05-16 02:46:07 -05002552 sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002553 PNG_COST_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002554
2555 if (sumhi > PNG_HIMASK)
2556 sum = PNG_MAXSUM;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002557
Andreas Dilger47a0c421997-05-16 02:46:07 -05002558 else
2559 sum = (sumhi << PNG_HISHIFT) + sumlo;
2560 }
2561#endif
2562
Guy Schalnate5a37791996-06-05 15:50:50 -05002563 if (sum < mins)
2564 {
2565 mins = sum;
2566 best_row = png_ptr->sub_row;
2567 }
Guy Schalnat0d580581995-07-20 02:43:20 -05002568 }
2569
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002570 /* Up filter */
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002571 if (filter_to_do == PNG_FILTER_UP)
2572 {
2573 png_bytep rp, dp, pp;
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002574 png_size_t i;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002575
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002576 for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002577 pp = prev_row + 1; i < row_bytes;
2578 i++, rp++, pp++, dp++)
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002579 {
2580 *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
2581 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002582
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002583 best_row = png_ptr->up_row;
2584 }
2585
2586 else if (filter_to_do & PNG_FILTER_UP)
Guy Schalnat0d580581995-07-20 02:43:20 -05002587 {
Guy Schalnate5a37791996-06-05 15:50:50 -05002588 png_bytep rp, dp, pp;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002589 png_uint_32 sum = 0, lmins = mins;
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002590 png_size_t i;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002591 int v;
2592
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002593
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002594#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -05002595 if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
2596 {
Glenn Randers-Pehrsond0dce401998-05-09 10:02:29 -05002597 int j;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002598 png_uint_32 lmhi, lmlo;
2599 lmlo = lmins & PNG_LOMASK;
2600 lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
2601
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002602 for (j = 0; j < num_p_filters; j++)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002603 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002604 if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002605 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002606 lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002607 PNG_WEIGHT_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002608
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002609 lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002610 PNG_WEIGHT_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002611 }
2612 }
2613
2614 lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002615 PNG_COST_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002616
Andreas Dilger47a0c421997-05-16 02:46:07 -05002617 lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002618 PNG_COST_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002619
2620 if (lmhi > PNG_HIMASK)
2621 lmins = PNG_MAXSUM;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002622
Andreas Dilger47a0c421997-05-16 02:46:07 -05002623 else
2624 lmins = (lmhi << PNG_HISHIFT) + lmlo;
2625 }
2626#endif
Guy Schalnate5a37791996-06-05 15:50:50 -05002627
2628 for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002629 pp = prev_row + 1; i < row_bytes; i++)
Guy Schalnate5a37791996-06-05 15:50:50 -05002630 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002631 v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
Guy Schalnate5a37791996-06-05 15:50:50 -05002632
2633 sum += (v < 128) ? v : 256 - v;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002634
2635 if (sum > lmins) /* We are already worse, don't continue. */
2636 break;
Guy Schalnate5a37791996-06-05 15:50:50 -05002637 }
Andreas Dilger47a0c421997-05-16 02:46:07 -05002638
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002639#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -05002640 if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
2641 {
Glenn Randers-Pehrsond0dce401998-05-09 10:02:29 -05002642 int j;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002643 png_uint_32 sumhi, sumlo;
2644 sumlo = sum & PNG_LOMASK;
2645 sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
2646
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002647 for (j = 0; j < num_p_filters; j++)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002648 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002649 if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002650 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002651 sumlo = (sumlo * png_ptr->filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002652 PNG_WEIGHT_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002653
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002654 sumhi = (sumhi * png_ptr->filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002655 PNG_WEIGHT_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002656 }
2657 }
2658
2659 sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002660 PNG_COST_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002661
Andreas Dilger47a0c421997-05-16 02:46:07 -05002662 sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002663 PNG_COST_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002664
2665 if (sumhi > PNG_HIMASK)
2666 sum = PNG_MAXSUM;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002667
Andreas Dilger47a0c421997-05-16 02:46:07 -05002668 else
2669 sum = (sumhi << PNG_HISHIFT) + sumlo;
2670 }
2671#endif
2672
Guy Schalnate5a37791996-06-05 15:50:50 -05002673 if (sum < mins)
2674 {
2675 mins = sum;
2676 best_row = png_ptr->up_row;
2677 }
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002678 }
2679
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05002680 /* Avg filter */
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002681 if (filter_to_do == PNG_FILTER_AVG)
2682 {
2683 png_bytep rp, dp, pp, lp;
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002684 png_uint_32 i;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002685
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002686 for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002687 pp = prev_row + 1; i < bpp; i++)
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002688 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002689 *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002690 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002691
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002692 for (lp = row_buf + 1; i < row_bytes; i++)
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002693 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002694 *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
2695 & 0xff);
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002696 }
2697 best_row = png_ptr->avg_row;
2698 }
2699
2700 else if (filter_to_do & PNG_FILTER_AVG)
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002701 {
Guy Schalnate5a37791996-06-05 15:50:50 -05002702 png_bytep rp, dp, pp, lp;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002703 png_uint_32 sum = 0, lmins = mins;
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002704 png_size_t i;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002705 int v;
2706
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002707#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -05002708 if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
2709 {
Glenn Randers-Pehrsond0dce401998-05-09 10:02:29 -05002710 int j;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002711 png_uint_32 lmhi, lmlo;
2712 lmlo = lmins & PNG_LOMASK;
2713 lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
2714
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002715 for (j = 0; j < num_p_filters; j++)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002716 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002717 if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002718 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002719 lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002720 PNG_WEIGHT_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002721
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002722 lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002723 PNG_WEIGHT_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002724 }
2725 }
2726
2727 lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002728 PNG_COST_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002729
Andreas Dilger47a0c421997-05-16 02:46:07 -05002730 lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002731 PNG_COST_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002732
2733 if (lmhi > PNG_HIMASK)
2734 lmins = PNG_MAXSUM;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002735
Andreas Dilger47a0c421997-05-16 02:46:07 -05002736 else
2737 lmins = (lmhi << PNG_HISHIFT) + lmlo;
2738 }
2739#endif
Guy Schalnate5a37791996-06-05 15:50:50 -05002740
2741 for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002742 pp = prev_row + 1; i < bpp; i++)
Guy Schalnate5a37791996-06-05 15:50:50 -05002743 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002744 v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
Guy Schalnate5a37791996-06-05 15:50:50 -05002745
2746 sum += (v < 128) ? v : 256 - v;
2747 }
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002748
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002749 for (lp = row_buf + 1; i < row_bytes; i++)
Guy Schalnate5a37791996-06-05 15:50:50 -05002750 {
Glenn Randers-Pehrson5c6aeb21998-12-29 11:47:59 -06002751 v = *dp++ =
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002752 (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff);
Guy Schalnate5a37791996-06-05 15:50:50 -05002753
2754 sum += (v < 128) ? v : 256 - v;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002755
2756 if (sum > lmins) /* We are already worse, don't continue. */
2757 break;
Guy Schalnate5a37791996-06-05 15:50:50 -05002758 }
Andreas Dilger47a0c421997-05-16 02:46:07 -05002759
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002760#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -05002761 if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
2762 {
Glenn Randers-Pehrsond0dce401998-05-09 10:02:29 -05002763 int j;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002764 png_uint_32 sumhi, sumlo;
2765 sumlo = sum & PNG_LOMASK;
2766 sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
2767
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002768 for (j = 0; j < num_p_filters; j++)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002769 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002770 if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002771 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002772 sumlo = (sumlo * png_ptr->filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002773 PNG_WEIGHT_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002774
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002775 sumhi = (sumhi * png_ptr->filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002776 PNG_WEIGHT_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002777 }
2778 }
2779
2780 sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002781 PNG_COST_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002782
Andreas Dilger47a0c421997-05-16 02:46:07 -05002783 sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002784 PNG_COST_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002785
2786 if (sumhi > PNG_HIMASK)
2787 sum = PNG_MAXSUM;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002788
Andreas Dilger47a0c421997-05-16 02:46:07 -05002789 else
2790 sum = (sumhi << PNG_HISHIFT) + sumlo;
2791 }
2792#endif
2793
Guy Schalnate5a37791996-06-05 15:50:50 -05002794 if (sum < mins)
2795 {
2796 mins = sum;
2797 best_row = png_ptr->avg_row;
2798 }
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002799 }
2800
Andreas Dilger47a0c421997-05-16 02:46:07 -05002801 /* Paeth filter */
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002802 if (filter_to_do == PNG_FILTER_PAETH)
2803 {
2804 png_bytep rp, dp, pp, cp, lp;
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002805 png_size_t i;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002806
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002807 for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002808 pp = prev_row + 1; i < bpp; i++)
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002809 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002810 *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002811 }
2812
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002813 for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002814 {
2815 int a, b, c, pa, pb, pc, p;
2816
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002817 b = *pp++;
2818 c = *cp++;
2819 a = *lp++;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002820
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002821 p = b - c;
2822 pc = a - c;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002823
2824#ifdef PNG_USE_ABS
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002825 pa = abs(p);
2826 pb = abs(pc);
2827 pc = abs(p + pc);
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002828#else
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002829 pa = p < 0 ? -p : p;
2830 pb = pc < 0 ? -pc : pc;
2831 pc = (p + pc) < 0 ? -(p + pc) : p + pc;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002832#endif
2833
2834 p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
2835
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002836 *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002837 }
2838 best_row = png_ptr->paeth_row;
2839 }
2840
2841 else if (filter_to_do & PNG_FILTER_PAETH)
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002842 {
Guy Schalnate5a37791996-06-05 15:50:50 -05002843 png_bytep rp, dp, pp, cp, lp;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002844 png_uint_32 sum = 0, lmins = mins;
Glenn Randers-Pehrsonbcb3aac2010-09-10 22:05:27 -05002845 png_size_t i;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002846 int v;
2847
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002848#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -05002849 if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
2850 {
Glenn Randers-Pehrsond0dce401998-05-09 10:02:29 -05002851 int j;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002852 png_uint_32 lmhi, lmlo;
2853 lmlo = lmins & PNG_LOMASK;
2854 lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
2855
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002856 for (j = 0; j < num_p_filters; j++)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002857 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002858 if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002859 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002860 lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002861 PNG_WEIGHT_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002862
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002863 lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002864 PNG_WEIGHT_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002865 }
2866 }
2867
2868 lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002869 PNG_COST_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002870
Andreas Dilger47a0c421997-05-16 02:46:07 -05002871 lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002872 PNG_COST_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002873
2874 if (lmhi > PNG_HIMASK)
2875 lmins = PNG_MAXSUM;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002876
Andreas Dilger47a0c421997-05-16 02:46:07 -05002877 else
2878 lmins = (lmhi << PNG_HISHIFT) + lmlo;
2879 }
2880#endif
Guy Schalnate5a37791996-06-05 15:50:50 -05002881
2882 for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002883 pp = prev_row + 1; i < bpp; i++)
Guy Schalnate5a37791996-06-05 15:50:50 -05002884 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002885 v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
Guy Schalnate5a37791996-06-05 15:50:50 -05002886
2887 sum += (v < 128) ? v : 256 - v;
2888 }
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002889
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002890 for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
Guy Schalnate5a37791996-06-05 15:50:50 -05002891 {
2892 int a, b, c, pa, pb, pc, p;
2893
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002894 b = *pp++;
2895 c = *cp++;
2896 a = *lp++;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002897
2898#ifndef PNG_SLOW_PAETH
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002899 p = b - c;
2900 pc = a - c;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002901#ifdef PNG_USE_ABS
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002902 pa = abs(p);
2903 pb = abs(pc);
2904 pc = abs(p + pc);
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002905#else
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002906 pa = p < 0 ? -p : p;
2907 pb = pc < 0 ? -pc : pc;
2908 pc = (p + pc) < 0 ? -(p + pc) : p + pc;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002909#endif
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002910 p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
2911#else /* PNG_SLOW_PAETH */
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002912 p = a + b - c;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002913 pa = abs(p - a);
2914 pb = abs(p - b);
2915 pc = abs(p - c);
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002916
Guy Schalnate5a37791996-06-05 15:50:50 -05002917 if (pa <= pb && pa <= pc)
2918 p = a;
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002919
Guy Schalnate5a37791996-06-05 15:50:50 -05002920 else if (pb <= pc)
2921 p = b;
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002922
Guy Schalnate5a37791996-06-05 15:50:50 -05002923 else
2924 p = c;
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002925#endif /* PNG_SLOW_PAETH */
Guy Schalnate5a37791996-06-05 15:50:50 -05002926
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002927 v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
Guy Schalnate5a37791996-06-05 15:50:50 -05002928
2929 sum += (v < 128) ? v : 256 - v;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002930
2931 if (sum > lmins) /* We are already worse, don't continue. */
2932 break;
Guy Schalnate5a37791996-06-05 15:50:50 -05002933 }
Andreas Dilger47a0c421997-05-16 02:46:07 -05002934
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002935#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -05002936 if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
2937 {
Glenn Randers-Pehrsond0dce401998-05-09 10:02:29 -05002938 int j;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002939 png_uint_32 sumhi, sumlo;
2940 sumlo = sum & PNG_LOMASK;
2941 sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
2942
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002943 for (j = 0; j < num_p_filters; j++)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002944 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002945 if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002946 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002947 sumlo = (sumlo * png_ptr->filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002948 PNG_WEIGHT_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002949
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002950 sumhi = (sumhi * png_ptr->filter_weights[j]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002951 PNG_WEIGHT_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002952 }
2953 }
2954
2955 sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002956 PNG_COST_SHIFT;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002957
Andreas Dilger47a0c421997-05-16 02:46:07 -05002958 sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
Glenn Randers-Pehrson16908a12010-03-06 07:34:28 -06002959 PNG_COST_SHIFT;
Andreas Dilger47a0c421997-05-16 02:46:07 -05002960
2961 if (sumhi > PNG_HIMASK)
2962 sum = PNG_MAXSUM;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002963
Andreas Dilger47a0c421997-05-16 02:46:07 -05002964 else
2965 sum = (sumhi << PNG_HISHIFT) + sumlo;
2966 }
2967#endif
2968
Guy Schalnate5a37791996-06-05 15:50:50 -05002969 if (sum < mins)
2970 {
2971 best_row = png_ptr->paeth_row;
2972 }
Guy Schalnat51f0eb41995-09-26 05:22:39 -05002973 }
Glenn Randers-Pehrsondbd40142009-08-31 08:42:02 -05002974#endif /* PNG_WRITE_FILTER_SUPPORTED */
Glenn Randers-Pehrson896239b1998-04-21 15:03:57 -05002975
Glenn Randers-Pehrsonbb5cb142011-09-22 12:41:58 -05002976 /* Do the actual writing of the filtered row data from the chosen filter. */
2977 png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1);
Andreas Dilger47a0c421997-05-16 02:46:07 -05002978
Glenn Randers-Pehrsondbd40142009-08-31 08:42:02 -05002979#ifdef PNG_WRITE_FILTER_SUPPORTED
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05002980#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
Andreas Dilger47a0c421997-05-16 02:46:07 -05002981 /* Save the type of filter we picked this time for future calculations */
2982 if (png_ptr->num_prev_filters > 0)
2983 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002984 int j;
Glenn Randers-Pehrsonf24daf22010-05-06 09:44:04 -05002985
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002986 for (j = 1; j < num_p_filters; j++)
Andreas Dilger47a0c421997-05-16 02:46:07 -05002987 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002988 png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1];
Andreas Dilger47a0c421997-05-16 02:46:07 -05002989 }
Glenn Randers-Pehrsonccadcae2010-10-23 17:29:13 -05002990
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -05002991 png_ptr->prev_filters[j] = best_row[0];
Andreas Dilger47a0c421997-05-16 02:46:07 -05002992 }
2993#endif
Glenn Randers-Pehrsondbd40142009-08-31 08:42:02 -05002994#endif /* PNG_WRITE_FILTER_SUPPORTED */
Guy Schalnate5a37791996-06-05 15:50:50 -05002995}
2996
2997
Andreas Dilger47a0c421997-05-16 02:46:07 -05002998/* Do the actual writing of a previously filtered row. */
John Bowlerc5bef942011-05-05 17:35:39 -05002999static void
John Bowler5d567862011-12-24 09:12:00 -06003000png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row,
John Bowlerb5d00512012-03-09 09:15:18 -06003001 png_size_t full_row_length/*includes filter byte*/)
Guy Schalnate5a37791996-06-05 15:50:50 -05003002{
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05003003 png_debug(1, "in png_write_filtered_row");
Glenn Randers-Pehrson3358a982009-08-13 18:04:26 -05003004
Glenn Randers-Pehrson51650b82008-08-05 07:44:42 -05003005 png_debug1(2, "filter = %d", filtered_row[0]);
Glenn Randers-Pehrson5e5c1e12000-11-10 12:26:19 -06003006
John Bowlerb5d00512012-03-09 09:15:18 -06003007 png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH);
Guy Schalnate5a37791996-06-05 15:50:50 -05003008
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05003009 /* Swap the current and previous rows */
Andreas Dilger47a0c421997-05-16 02:46:07 -05003010 if (png_ptr->prev_row != NULL)
Guy Schalnatc21f90c1996-06-17 16:24:45 -05003011 {
3012 png_bytep tptr;
3013
3014 tptr = png_ptr->prev_row;
3015 png_ptr->prev_row = png_ptr->row_buf;
3016 png_ptr->row_buf = tptr;
3017 }
3018
Glenn Randers-Pehrson glennrp@comcast.netb1c0d332009-05-15 20:39:34 -05003019 /* Finish row - updates counters and flushes zlib if last row */
Guy Schalnate5a37791996-06-05 15:50:50 -05003020 png_write_finish_row(png_ptr);
3021
Glenn Randers-Pehrsone26c0952009-09-23 11:22:08 -05003022#ifdef PNG_WRITE_FLUSH_SUPPORTED
Guy Schalnate5a37791996-06-05 15:50:50 -05003023 png_ptr->flush_rows++;
3024
3025 if (png_ptr->flush_dist > 0 &&
3026 png_ptr->flush_rows >= png_ptr->flush_dist)
Guy Schalnat0d580581995-07-20 02:43:20 -05003027 {
Guy Schalnate5a37791996-06-05 15:50:50 -05003028 png_write_flush(png_ptr);
Guy Schalnat0d580581995-07-20 02:43:20 -05003029 }
Glenn Randers-Pehrson166c5a31999-12-10 09:43:02 -06003030#endif
Guy Schalnat0d580581995-07-20 02:43:20 -05003031}
Glenn Randers-Pehrson3097f612001-05-07 14:52:45 -05003032#endif /* PNG_WRITE_SUPPORTED */