1 
2 /* pngwutil.c - utilities to write a PNG file
3  *
4  * Last changed in libpng 1.2.56 [December 17, 2015]
5  * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
6  * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
7  * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
8  *
9  * This code is released under the libpng license.
10  * For conditions of distribution and use, see the disclaimer
11  * and license in png.h
12  */
13 
14 #define PNG_INTERNAL
15 #define PNG_NO_PEDANTIC_WARNINGS
16 #include "png.h"
17 #ifdef PNG_WRITE_SUPPORTED
18 
19 /* 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  */
23 void PNGAPI
png_save_uint_32(png_bytep buf,png_uint_32 i)24 png_save_uint_32(png_bytep buf, png_uint_32 i)
25 {
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 
32 /* The png_save_int_32 function assumes integers are stored in two's
33  * complement format.  If this isn't the case, then this routine needs to
34  * be modified to write data in two's complement format.
35  */
36 void PNGAPI
png_save_int_32(png_bytep buf,png_int_32 i)37 png_save_int_32(png_bytep buf, png_int_32 i)
38 {
39    buf[0] = (png_byte)((i >> 24) & 0xff);
40    buf[1] = (png_byte)((i >> 16) & 0xff);
41    buf[2] = (png_byte)((i >> 8) & 0xff);
42    buf[3] = (png_byte)(i & 0xff);
43 }
44 
45 /* Place a 16-bit number into a buffer in PNG byte order.
46  * The parameter is declared unsigned int, not png_uint_16,
47  * just to avoid potential problems on pre-ANSI C compilers.
48  */
49 void PNGAPI
png_save_uint_16(png_bytep buf,unsigned int i)50 png_save_uint_16(png_bytep buf, unsigned int i)
51 {
52    buf[0] = (png_byte)((i >> 8) & 0xff);
53    buf[1] = (png_byte)(i & 0xff);
54 }
55 
56 /* Simple function to write the signature.  If we have already written
57  * the magic bytes of the signature, or more likely, the PNG stream is
58  * being embedded into another stream and doesn't need its own signature,
59  * we should call png_set_sig_bytes() to tell libpng how many of the
60  * bytes have already been written.
61  */
62 void /* PRIVATE */
png_write_sig(png_structp png_ptr)63 png_write_sig(png_structp png_ptr)
64 {
65    png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
66 
67    /* Write the rest of the 8 byte signature */
68    png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes],
69       (png_size_t)(8 - png_ptr->sig_bytes));
70    if (png_ptr->sig_bytes < 3)
71       png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
72 }
73 
74 /* Write a PNG chunk all at once.  The type is an array of ASCII characters
75  * representing the chunk name.  The array must be at least 4 bytes in
76  * length, and does not need to be null terminated.  To be safe, pass the
77  * pre-defined chunk names here, and if you need a new one, define it
78  * where the others are defined.  The length is the length of the data.
79  * All the data must be present.  If that is not possible, use the
80  * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()
81  * functions instead.
82  */
83 void PNGAPI
png_write_chunk(png_structp png_ptr,png_bytep chunk_name,png_bytep data,png_size_t length)84 png_write_chunk(png_structp png_ptr, png_bytep chunk_name,
85    png_bytep data, png_size_t length)
86 {
87    if (png_ptr == NULL)
88       return;
89    png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length);
90    png_write_chunk_data(png_ptr, data, (png_size_t)length);
91    png_write_chunk_end(png_ptr);
92 }
93 
94 /* Write the start of a PNG chunk.  The type is the chunk type.
95  * The total_length is the sum of the lengths of all the data you will be
96  * passing in png_write_chunk_data().
97  */
98 void PNGAPI
png_write_chunk_start(png_structp png_ptr,png_bytep chunk_name,png_uint_32 length)99 png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name,
100    png_uint_32 length)
101 {
102    png_byte buf[8];
103 
104    png_debug2(0, "Writing %s chunk, length = %lu", chunk_name,
105       (unsigned long)length);
106 
107    if (png_ptr == NULL)
108       return;
109 
110 
111    /* Write the length and the chunk name */
112    png_save_uint_32(buf, length);
113    png_memcpy(buf + 4, chunk_name, 4);
114    png_write_data(png_ptr, buf, (png_size_t)8);
115    /* Put the chunk name into png_ptr->chunk_name */
116    png_memcpy(png_ptr->chunk_name, chunk_name, 4);
117    /* Reset the crc and run it over the chunk name */
118    png_reset_crc(png_ptr);
119    png_calculate_crc(png_ptr, chunk_name, (png_size_t)4);
120 }
121 
122 /* Write the data of a PNG chunk started with png_write_chunk_start().
123  * Note that multiple calls to this function are allowed, and that the
124  * sum of the lengths from these calls *must* add up to the total_length
125  * given to png_write_chunk_start().
126  */
127 void PNGAPI
png_write_chunk_data(png_structp png_ptr,png_bytep data,png_size_t length)128 png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length)
129 {
130    /* Write the data, and run the CRC over it */
131    if (png_ptr == NULL)
132       return;
133    if (data != NULL && length > 0)
134    {
135       png_write_data(png_ptr, data, length);
136       /* Update the CRC after writing the data,
137        * in case that the user I/O routine alters it.
138        */
139       png_calculate_crc(png_ptr, data, length);
140    }
141 }
142 
143 /* Finish a chunk started with png_write_chunk_start(). */
144 void PNGAPI
png_write_chunk_end(png_structp png_ptr)145 png_write_chunk_end(png_structp png_ptr)
146 {
147    png_byte buf[4];
148 
149    if (png_ptr == NULL) return;
150 
151    /* Write the crc in a single operation */
152    png_save_uint_32(buf, png_ptr->crc);
153 
154    png_write_data(png_ptr, buf, (png_size_t)4);
155 }
156 
157 #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED)
158 /* This pair of functions encapsulates the operation of (a) compressing a
159  * text string, and (b) issuing it later as a series of chunk data writes.
160  * The compression_state structure is shared context for these functions
161  * set up by the caller in order to make the whole mess thread-safe.
162  */
163 
164 typedef struct
165 {
166    char *input;   /* The uncompressed input data */
167    int input_len;   /* Its length */
168    int num_output_ptr; /* Number of output pointers used */
169    int max_output_ptr; /* Size of output_ptr */
170    png_charpp output_ptr; /* Array of pointers to output */
171 } compression_state;
172 
173 /* Compress given text into storage in the png_ptr structure */
174 static int /* PRIVATE */
png_text_compress(png_structp png_ptr,png_charp text,png_size_t text_len,int compression,compression_state * comp)175 png_text_compress(png_structp png_ptr,
176         png_charp text, png_size_t text_len, int compression,
177         compression_state *comp)
178 {
179    int ret;
180 
181    comp->num_output_ptr = 0;
182    comp->max_output_ptr = 0;
183    comp->output_ptr = NULL;
184    comp->input = NULL;
185    comp->input_len = 0;
186 
187    /* We may just want to pass the text right through */
188    if (compression == PNG_TEXT_COMPRESSION_NONE)
189    {
190        comp->input = text;
191        comp->input_len = text_len;
192        return((int)text_len);
193    }
194 
195    if (compression >= PNG_TEXT_COMPRESSION_LAST)
196    {
197 #if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE)
198       char msg[50];
199       png_snprintf(msg, 50, "Unknown compression type %d", compression);
200       png_warning(png_ptr, msg);
201 #else
202       png_warning(png_ptr, "Unknown compression type");
203 #endif
204    }
205 
206    /* We can't write the chunk until we find out how much data we have,
207     * which means we need to run the compressor first and save the
208     * output.  This shouldn't be a problem, as the vast majority of
209     * comments should be reasonable, but we will set up an array of
210     * malloc'd pointers to be sure.
211     *
212     * If we knew the application was well behaved, we could simplify this
213     * greatly by assuming we can always malloc an output buffer large
214     * enough to hold the compressed text ((1001 * text_len / 1000) + 12)
215     * and malloc this directly.  The only time this would be a bad idea is
216     * if we can't malloc more than 64K and we have 64K of random input
217     * data, or if the input string is incredibly large (although this
218     * wouldn't cause a failure, just a slowdown due to swapping).
219     */
220 
221    /* Set up the compression buffers */
222    png_ptr->zstream.avail_in = (uInt)text_len;
223    png_ptr->zstream.next_in = (Bytef *)text;
224    png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
225    png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf;
226 
227    /* This is the same compression loop as in png_write_row() */
228    do
229    {
230       /* Compress the data */
231       ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
232       if (ret != Z_OK)
233       {
234          /* Error */
235          if (png_ptr->zstream.msg != NULL)
236             png_error(png_ptr, png_ptr->zstream.msg);
237          else
238             png_error(png_ptr, "zlib error");
239       }
240       /* Check to see if we need more room */
241       if (!(png_ptr->zstream.avail_out))
242       {
243          /* Make sure the output array has room */
244          if (comp->num_output_ptr >= comp->max_output_ptr)
245          {
246             int old_max;
247 
248             old_max = comp->max_output_ptr;
249             comp->max_output_ptr = comp->num_output_ptr + 4;
250             if (comp->output_ptr != NULL)
251             {
252                png_charpp old_ptr;
253 
254                old_ptr = comp->output_ptr;
255                comp->output_ptr = (png_charpp)png_malloc(png_ptr,
256                   (png_uint_32)
257                   (comp->max_output_ptr * png_sizeof(png_charp)));
258                png_memcpy(comp->output_ptr, old_ptr, old_max
259                   * png_sizeof(png_charp));
260                png_free(png_ptr, old_ptr);
261             }
262             else
263                comp->output_ptr = (png_charpp)png_malloc(png_ptr,
264                   (png_uint_32)
265                   (comp->max_output_ptr * png_sizeof(png_charp)));
266          }
267 
268          /* Save the data */
269          comp->output_ptr[comp->num_output_ptr] =
270             (png_charp)png_malloc(png_ptr,
271             (png_uint_32)png_ptr->zbuf_size);
272          png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
273             png_ptr->zbuf_size);
274          comp->num_output_ptr++;
275 
276          /* and reset the buffer */
277          png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
278          png_ptr->zstream.next_out = png_ptr->zbuf;
279       }
280    /* Continue until we don't have any more to compress */
281    } while (png_ptr->zstream.avail_in);
282 
283    /* Finish the compression */
284    do
285    {
286       /* Tell zlib we are finished */
287       ret = deflate(&png_ptr->zstream, Z_FINISH);
288 
289       if (ret == Z_OK)
290       {
291          /* Check to see if we need more room */
292          if (!(png_ptr->zstream.avail_out))
293          {
294             /* Check to make sure our output array has room */
295             if (comp->num_output_ptr >= comp->max_output_ptr)
296             {
297                int old_max;
298 
299                old_max = comp->max_output_ptr;
300                comp->max_output_ptr = comp->num_output_ptr + 4;
301                if (comp->output_ptr != NULL)
302                {
303                   png_charpp old_ptr;
304 
305                   old_ptr = comp->output_ptr;
306                   /* This could be optimized to realloc() */
307                   comp->output_ptr = (png_charpp)png_malloc(png_ptr,
308                      (png_uint_32)(comp->max_output_ptr *
309                      png_sizeof(png_charp)));
310                   png_memcpy(comp->output_ptr, old_ptr,
311                      old_max * png_sizeof(png_charp));
312                   png_free(png_ptr, old_ptr);
313                }
314                else
315                   comp->output_ptr = (png_charpp)png_malloc(png_ptr,
316                      (png_uint_32)(comp->max_output_ptr *
317                      png_sizeof(png_charp)));
318             }
319 
320             /* Save the data */
321             comp->output_ptr[comp->num_output_ptr] =
322                (png_charp)png_malloc(png_ptr,
323                (png_uint_32)png_ptr->zbuf_size);
324             png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
325                png_ptr->zbuf_size);
326             comp->num_output_ptr++;
327 
328             /* and reset the buffer pointers */
329             png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
330             png_ptr->zstream.next_out = png_ptr->zbuf;
331          }
332       }
333       else if (ret != Z_STREAM_END)
334       {
335          /* We got an error */
336          if (png_ptr->zstream.msg != NULL)
337             png_error(png_ptr, png_ptr->zstream.msg);
338          else
339             png_error(png_ptr, "zlib error");
340       }
341    } while (ret != Z_STREAM_END);
342 
343    /* Text length is number of buffers plus last buffer */
344    text_len = png_ptr->zbuf_size * comp->num_output_ptr;
345    if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
346       text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out;
347 
348    return((int)text_len);
349 }
350 
351 /* Ship the compressed text out via chunk writes */
352 static void /* PRIVATE */
png_write_compressed_data_out(png_structp png_ptr,compression_state * comp)353 png_write_compressed_data_out(png_structp png_ptr, compression_state *comp)
354 {
355    int i;
356 
357    /* Handle the no-compression case */
358    if (comp->input)
359    {
360       png_write_chunk_data(png_ptr, (png_bytep)comp->input,
361                             (png_size_t)comp->input_len);
362       return;
363    }
364 
365    /* Write saved output buffers, if any */
366    for (i = 0; i < comp->num_output_ptr; i++)
367    {
368       png_write_chunk_data(png_ptr, (png_bytep)comp->output_ptr[i],
369          (png_size_t)png_ptr->zbuf_size);
370       png_free(png_ptr, comp->output_ptr[i]);
371        comp->output_ptr[i]=NULL;
372    }
373    if (comp->max_output_ptr != 0)
374       png_free(png_ptr, comp->output_ptr);
375        comp->output_ptr=NULL;
376    /* Write anything left in zbuf */
377    if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size)
378       png_write_chunk_data(png_ptr, png_ptr->zbuf,
379          (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out));
380 
381    /* Reset zlib for another zTXt/iTXt or image data */
382    deflateReset(&png_ptr->zstream);
383    png_ptr->zstream.data_type = Z_BINARY;
384 }
385 #endif
386 
387 /* Write the IHDR chunk, and update the png_struct with the necessary
388  * information.  Note that the rest of this code depends upon this
389  * information being correct.
390  */
391 void /* PRIVATE */
png_write_IHDR(png_structp png_ptr,png_uint_32 width,png_uint_32 height,int bit_depth,int color_type,int compression_type,int filter_type,int interlace_type)392 png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height,
393    int bit_depth, int color_type, int compression_type, int filter_type,
394    int interlace_type)
395 {
396 #ifdef PNG_USE_LOCAL_ARRAYS
397    PNG_IHDR;
398 #endif
399    int ret;
400 
401    png_byte buf[13]; /* Buffer to store the IHDR info */
402 
403    png_debug(1, "in png_write_IHDR");
404 
405    /* Check that we have valid input data from the application info */
406    switch (color_type)
407    {
408       case PNG_COLOR_TYPE_GRAY:
409          switch (bit_depth)
410          {
411             case 1:
412             case 2:
413             case 4:
414             case 8:
415             case 16: png_ptr->channels = 1; break;
416             default: png_error(png_ptr,
417                          "Invalid bit depth for grayscale image");
418          }
419          break;
420       case PNG_COLOR_TYPE_RGB:
421          if (bit_depth != 8 && bit_depth != 16)
422             png_error(png_ptr, "Invalid bit depth for RGB image");
423          png_ptr->channels = 3;
424          break;
425       case PNG_COLOR_TYPE_PALETTE:
426          switch (bit_depth)
427          {
428             case 1:
429             case 2:
430             case 4:
431             case 8: png_ptr->channels = 1; break;
432             default: png_error(png_ptr, "Invalid bit depth for paletted image");
433          }
434          break;
435       case PNG_COLOR_TYPE_GRAY_ALPHA:
436          if (bit_depth != 8 && bit_depth != 16)
437             png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");
438          png_ptr->channels = 2;
439          break;
440       case PNG_COLOR_TYPE_RGB_ALPHA:
441          if (bit_depth != 8 && bit_depth != 16)
442             png_error(png_ptr, "Invalid bit depth for RGBA image");
443          png_ptr->channels = 4;
444          break;
445       default:
446          png_error(png_ptr, "Invalid image color type specified");
447    }
448 
449    if (compression_type != PNG_COMPRESSION_TYPE_BASE)
450    {
451       png_warning(png_ptr, "Invalid compression type specified");
452       compression_type = PNG_COMPRESSION_TYPE_BASE;
453    }
454 
455    /* Write filter_method 64 (intrapixel differencing) only if
456     * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
457     * 2. Libpng did not write a PNG signature (this filter_method is only
458     *    used in PNG datastreams that are embedded in MNG datastreams) and
459     * 3. The application called png_permit_mng_features with a mask that
460     *    included PNG_FLAG_MNG_FILTER_64 and
461     * 4. The filter_method is 64 and
462     * 5. The color_type is RGB or RGBA
463     */
464    if (
465 #ifdef PNG_MNG_FEATURES_SUPPORTED
466       !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
467       ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) &&
468       (color_type == PNG_COLOR_TYPE_RGB ||
469        color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
470       (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) &&
471 #endif
472       filter_type != PNG_FILTER_TYPE_BASE)
473    {
474       png_warning(png_ptr, "Invalid filter type specified");
475       filter_type = PNG_FILTER_TYPE_BASE;
476    }
477 
478 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
479    if (interlace_type != PNG_INTERLACE_NONE &&
480       interlace_type != PNG_INTERLACE_ADAM7)
481    {
482       png_warning(png_ptr, "Invalid interlace type specified");
483       interlace_type = PNG_INTERLACE_ADAM7;
484    }
485 #else
486    interlace_type=PNG_INTERLACE_NONE;
487 #endif
488 
489    /* Save the relevent information */
490    png_ptr->bit_depth = (png_byte)bit_depth;
491    png_ptr->color_type = (png_byte)color_type;
492    png_ptr->interlaced = (png_byte)interlace_type;
493 #ifdef PNG_MNG_FEATURES_SUPPORTED
494    png_ptr->filter_type = (png_byte)filter_type;
495 #endif
496    png_ptr->compression_type = (png_byte)compression_type;
497    png_ptr->width = width;
498    png_ptr->height = height;
499 
500    png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels);
501    png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);
502    /* Set the usr info, so any transformations can modify it */
503    png_ptr->usr_width = png_ptr->width;
504    png_ptr->usr_bit_depth = png_ptr->bit_depth;
505    png_ptr->usr_channels = png_ptr->channels;
506 
507    /* Pack the header information into the buffer */
508    png_save_uint_32(buf, width);
509    png_save_uint_32(buf + 4, height);
510    buf[8] = (png_byte)bit_depth;
511    buf[9] = (png_byte)color_type;
512    buf[10] = (png_byte)compression_type;
513    buf[11] = (png_byte)filter_type;
514    buf[12] = (png_byte)interlace_type;
515 
516    /* Write the chunk */
517    png_write_chunk(png_ptr, (png_bytep)png_IHDR, buf, (png_size_t)13);
518 
519    /* Initialize zlib with PNG info */
520    png_ptr->zstream.zalloc = png_zalloc;
521    png_ptr->zstream.zfree = png_zfree;
522    png_ptr->zstream.opaque = (voidpf)png_ptr;
523    if (!(png_ptr->do_filter))
524    {
525       if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
526          png_ptr->bit_depth < 8)
527          png_ptr->do_filter = PNG_FILTER_NONE;
528       else
529          png_ptr->do_filter = PNG_ALL_FILTERS;
530    }
531    if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY))
532    {
533       if (png_ptr->do_filter != PNG_FILTER_NONE)
534          png_ptr->zlib_strategy = Z_FILTERED;
535       else
536          png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY;
537    }
538    if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL))
539       png_ptr->zlib_level = Z_DEFAULT_COMPRESSION;
540    if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL))
541       png_ptr->zlib_mem_level = 8;
542    if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS))
543       png_ptr->zlib_window_bits = 15;
544    if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD))
545       png_ptr->zlib_method = 8;
546    ret = deflateInit2(&png_ptr->zstream, png_ptr->zlib_level,
547          png_ptr->zlib_method, png_ptr->zlib_window_bits,
548          png_ptr->zlib_mem_level, png_ptr->zlib_strategy);
549    if (ret != Z_OK)
550    {
551       if (ret == Z_VERSION_ERROR) png_error(png_ptr,
552           "zlib failed to initialize compressor -- version error");
553       if (ret == Z_STREAM_ERROR) png_error(png_ptr,
554            "zlib failed to initialize compressor -- stream error");
555       if (ret == Z_MEM_ERROR) png_error(png_ptr,
556            "zlib failed to initialize compressor -- mem error");
557       png_error(png_ptr, "zlib failed to initialize compressor");
558    }
559    png_ptr->zstream.next_out = png_ptr->zbuf;
560    png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
561    /* libpng is not interested in zstream.data_type */
562    /* Set it to a predefined value, to avoid its evaluation inside zlib */
563    png_ptr->zstream.data_type = Z_BINARY;
564 
565    png_ptr->mode = PNG_HAVE_IHDR;
566 }
567 
568 /* Write the palette.  We are careful not to trust png_color to be in the
569  * correct order for PNG, so people can redefine it to any convenient
570  * structure.
571  */
572 void /* PRIVATE */
png_write_PLTE(png_structp png_ptr,png_colorp palette,png_uint_32 num_pal)573 png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal)
574 {
575 #ifdef PNG_USE_LOCAL_ARRAYS
576    PNG_PLTE;
577 #endif
578    png_uint_32 max_palette_length, i;
579    png_colorp pal_ptr;
580    png_byte buf[3];
581 
582    png_debug(1, "in png_write_PLTE");
583 
584    max_palette_length = (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ?
585       (1 << png_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH;
586 
587    if ((
588 #ifdef PNG_MNG_FEATURES_SUPPORTED
589         !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) &&
590 #endif
591        num_pal == 0) || num_pal > max_palette_length)
592    {
593      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
594      {
595         png_error(png_ptr, "Invalid number of colors in palette");
596      }
597      else
598      {
599         png_warning(png_ptr, "Invalid number of colors in palette");
600         return;
601      }
602    }
603 
604    if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))
605    {
606       png_warning(png_ptr,
607         "Ignoring request to write a PLTE chunk in grayscale PNG");
608       return;
609    }
610 
611    png_ptr->num_palette = (png_uint_16)num_pal;
612    png_debug1(3, "num_palette = %d", png_ptr->num_palette);
613 
614    png_write_chunk_start(png_ptr, (png_bytep)png_PLTE,
615      (png_uint_32)(num_pal * 3));
616 #ifdef PNG_POINTER_INDEXING_SUPPORTED
617    for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)
618    {
619       buf[0] = pal_ptr->red;
620       buf[1] = pal_ptr->green;
621       buf[2] = pal_ptr->blue;
622       png_write_chunk_data(png_ptr, buf, (png_size_t)3);
623    }
624 #else
625    /* This is a little slower but some buggy compilers need to do this
626     * instead
627     */
628    pal_ptr=palette;
629    for (i = 0; i < num_pal; i++)
630    {
631       buf[0] = pal_ptr[i].red;
632       buf[1] = pal_ptr[i].green;
633       buf[2] = pal_ptr[i].blue;
634       png_write_chunk_data(png_ptr, buf, (png_size_t)3);
635    }
636 #endif
637    png_write_chunk_end(png_ptr);
638    png_ptr->mode |= PNG_HAVE_PLTE;
639 }
640 
641 /* Write an IDAT chunk */
642 void /* PRIVATE */
png_write_IDAT(png_structp png_ptr,png_bytep data,png_size_t length)643 png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length)
644 {
645 #ifdef PNG_USE_LOCAL_ARRAYS
646    PNG_IDAT;
647 #endif
648 
649    png_debug(1, "in png_write_IDAT");
650 
651    /* Optimize the CMF field in the zlib stream. */
652    /* This hack of the zlib stream is compliant to the stream specification. */
653    if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
654        png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
655    {
656       unsigned int z_cmf = data[0];  /* zlib compression method and flags */
657       if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
658       {
659          /* Avoid memory underflows and multiplication overflows.
660           *
661           * The conditions below are practically always satisfied;
662           * however, they still must be checked.
663           */
664          if (length >= 2 &&
665              png_ptr->height < 16384 && png_ptr->width < 16384)
666          {
667             png_uint_32 uncompressed_idat_size = png_ptr->height *
668                ((png_ptr->width *
669                png_ptr->channels * png_ptr->bit_depth + 15) >> 3);
670             unsigned int z_cinfo = z_cmf >> 4;
671             unsigned int half_z_window_size = 1 << (z_cinfo + 7);
672             while (uncompressed_idat_size <= half_z_window_size &&
673                    half_z_window_size >= 256)
674             {
675                z_cinfo--;
676                half_z_window_size >>= 1;
677             }
678             z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
679             if (data[0] != (png_byte)z_cmf)
680             {
681                data[0] = (png_byte)z_cmf;
682                data[1] &= 0xe0;
683                data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f);
684             }
685          }
686       }
687       else
688          png_error(png_ptr,
689             "Invalid zlib compression method or flags in IDAT");
690    }
691 
692    png_write_chunk(png_ptr, (png_bytep)png_IDAT, data, length);
693    png_ptr->mode |= PNG_HAVE_IDAT;
694 }
695 
696 /* Write an IEND chunk */
697 void /* PRIVATE */
png_write_IEND(png_structp png_ptr)698 png_write_IEND(png_structp png_ptr)
699 {
700 #ifdef PNG_USE_LOCAL_ARRAYS
701    PNG_IEND;
702 #endif
703 
704    png_debug(1, "in png_write_IEND");
705 
706    png_write_chunk(png_ptr, (png_bytep)png_IEND, png_bytep_NULL,
707      (png_size_t)0);
708    png_ptr->mode |= PNG_HAVE_IEND;
709 }
710 
711 #ifdef PNG_WRITE_gAMA_SUPPORTED
712 /* Write a gAMA chunk */
713 #ifdef PNG_FLOATING_POINT_SUPPORTED
714 void /* PRIVATE */
png_write_gAMA(png_structp png_ptr,double file_gamma)715 png_write_gAMA(png_structp png_ptr, double file_gamma)
716 {
717 #ifdef PNG_USE_LOCAL_ARRAYS
718    PNG_gAMA;
719 #endif
720    png_uint_32 igamma;
721    png_byte buf[4];
722 
723    png_debug(1, "in png_write_gAMA");
724 
725    /* file_gamma is saved in 1/100,000ths */
726    igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5);
727    png_save_uint_32(buf, igamma);
728    png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4);
729 }
730 #endif
731 #ifdef PNG_FIXED_POINT_SUPPORTED
732 void /* PRIVATE */
png_write_gAMA_fixed(png_structp png_ptr,png_fixed_point file_gamma)733 png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma)
734 {
735 #ifdef PNG_USE_LOCAL_ARRAYS
736    PNG_gAMA;
737 #endif
738    png_byte buf[4];
739 
740    png_debug(1, "in png_write_gAMA");
741 
742    /* file_gamma is saved in 1/100,000ths */
743    png_save_uint_32(buf, (png_uint_32)file_gamma);
744    png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4);
745 }
746 #endif
747 #endif
748 
749 #ifdef PNG_WRITE_sRGB_SUPPORTED
750 /* Write a sRGB chunk */
751 void /* PRIVATE */
png_write_sRGB(png_structp png_ptr,int srgb_intent)752 png_write_sRGB(png_structp png_ptr, int srgb_intent)
753 {
754 #ifdef PNG_USE_LOCAL_ARRAYS
755    PNG_sRGB;
756 #endif
757    png_byte buf[1];
758 
759    png_debug(1, "in png_write_sRGB");
760 
761    if (srgb_intent >= PNG_sRGB_INTENT_LAST)
762          png_warning(png_ptr,
763             "Invalid sRGB rendering intent specified");
764    buf[0]=(png_byte)srgb_intent;
765    png_write_chunk(png_ptr, (png_bytep)png_sRGB, buf, (png_size_t)1);
766 }
767 #endif
768 
769 #ifdef PNG_WRITE_iCCP_SUPPORTED
770 /* Write an iCCP chunk */
771 void /* PRIVATE */
png_write_iCCP(png_structp png_ptr,png_charp name,int compression_type,png_charp profile,int profile_len)772 png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type,
773    png_charp profile, int profile_len)
774 {
775 #ifdef PNG_USE_LOCAL_ARRAYS
776    PNG_iCCP;
777 #endif
778    png_size_t name_len;
779    png_charp new_name;
780    compression_state comp;
781    int embedded_profile_len = 0;
782 
783    png_debug(1, "in png_write_iCCP");
784 
785    comp.num_output_ptr = 0;
786    comp.max_output_ptr = 0;
787    comp.output_ptr = NULL;
788    comp.input = NULL;
789    comp.input_len = 0;
790 
791    if ((name_len = png_check_keyword(png_ptr, name,
792       &new_name)) == 0)
793       return;
794 
795    if (compression_type != PNG_COMPRESSION_TYPE_BASE)
796       png_warning(png_ptr, "Unknown compression type in iCCP chunk");
797 
798    if (profile == NULL)
799       profile_len = 0;
800 
801    if (profile_len > 3)
802       embedded_profile_len =
803           ((*( (png_bytep)profile    ))<<24) |
804           ((*( (png_bytep)profile + 1))<<16) |
805           ((*( (png_bytep)profile + 2))<< 8) |
806           ((*( (png_bytep)profile + 3))    );
807 
808    if (embedded_profile_len < 0)
809    {
810       png_warning(png_ptr,
811         "Embedded profile length in iCCP chunk is negative");
812       png_free(png_ptr, new_name);
813       return;
814    }
815 
816    if (profile_len < embedded_profile_len)
817    {
818       png_warning(png_ptr,
819         "Embedded profile length too large in iCCP chunk");
820       png_free(png_ptr, new_name);
821       return;
822    }
823 
824    if (profile_len > embedded_profile_len)
825    {
826       png_warning(png_ptr,
827         "Truncating profile to actual length in iCCP chunk");
828       profile_len = embedded_profile_len;
829    }
830 
831    if (profile_len)
832       profile_len = png_text_compress(png_ptr, profile,
833         (png_size_t)profile_len, PNG_COMPRESSION_TYPE_BASE, &comp);
834 
835    /* Make sure we include the NULL after the name and the compression type */
836    png_write_chunk_start(png_ptr, (png_bytep)png_iCCP,
837           (png_uint_32)(name_len + profile_len + 2));
838    new_name[name_len + 1] = 0x00;
839    png_write_chunk_data(png_ptr, (png_bytep)new_name,
840      (png_size_t)(name_len + 2));
841 
842    if (profile_len)
843       png_write_compressed_data_out(png_ptr, &comp);
844 
845    png_write_chunk_end(png_ptr);
846    png_free(png_ptr, new_name);
847 }
848 #endif
849 
850 #ifdef PNG_WRITE_sPLT_SUPPORTED
851 /* Write a sPLT chunk */
852 void /* PRIVATE */
png_write_sPLT(png_structp png_ptr,png_sPLT_tp spalette)853 png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette)
854 {
855 #ifdef PNG_USE_LOCAL_ARRAYS
856    PNG_sPLT;
857 #endif
858    png_size_t name_len;
859    png_charp new_name;
860    png_byte entrybuf[10];
861    int entry_size = (spalette->depth == 8 ? 6 : 10);
862    int palette_size = entry_size * spalette->nentries;
863    png_sPLT_entryp ep;
864 #ifndef PNG_POINTER_INDEXING_SUPPORTED
865    int i;
866 #endif
867 
868    png_debug(1, "in png_write_sPLT");
869 
870    if ((name_len = png_check_keyword(png_ptr,spalette->name, &new_name))==0)
871       return;
872 
873    /* Make sure we include the NULL after the name */
874    png_write_chunk_start(png_ptr, (png_bytep)png_sPLT,
875      (png_uint_32)(name_len + 2 + palette_size));
876    png_write_chunk_data(png_ptr, (png_bytep)new_name,
877      (png_size_t)(name_len + 1));
878    png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, (png_size_t)1);
879 
880    /* Loop through each palette entry, writing appropriately */
881 #ifdef PNG_POINTER_INDEXING_SUPPORTED
882    for (ep = spalette->entries; ep<spalette->entries + spalette->nentries; ep++)
883    {
884       if (spalette->depth == 8)
885       {
886           entrybuf[0] = (png_byte)ep->red;
887           entrybuf[1] = (png_byte)ep->green;
888           entrybuf[2] = (png_byte)ep->blue;
889           entrybuf[3] = (png_byte)ep->alpha;
890           png_save_uint_16(entrybuf + 4, ep->frequency);
891       }
892       else
893       {
894           png_save_uint_16(entrybuf + 0, ep->red);
895           png_save_uint_16(entrybuf + 2, ep->green);
896           png_save_uint_16(entrybuf + 4, ep->blue);
897           png_save_uint_16(entrybuf + 6, ep->alpha);
898           png_save_uint_16(entrybuf + 8, ep->frequency);
899       }
900       png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size);
901    }
902 #else
903    ep=spalette->entries;
904    for (i=0; i>spalette->nentries; i++)
905    {
906       if (spalette->depth == 8)
907       {
908           entrybuf[0] = (png_byte)ep[i].red;
909           entrybuf[1] = (png_byte)ep[i].green;
910           entrybuf[2] = (png_byte)ep[i].blue;
911           entrybuf[3] = (png_byte)ep[i].alpha;
912           png_save_uint_16(entrybuf + 4, ep[i].frequency);
913       }
914       else
915       {
916           png_save_uint_16(entrybuf + 0, ep[i].red);
917           png_save_uint_16(entrybuf + 2, ep[i].green);
918           png_save_uint_16(entrybuf + 4, ep[i].blue);
919           png_save_uint_16(entrybuf + 6, ep[i].alpha);
920           png_save_uint_16(entrybuf + 8, ep[i].frequency);
921       }
922       png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size);
923    }
924 #endif
925 
926    png_write_chunk_end(png_ptr);
927    png_free(png_ptr, new_name);
928 }
929 #endif
930 
931 #ifdef PNG_WRITE_sBIT_SUPPORTED
932 /* Write the sBIT chunk */
933 void /* PRIVATE */
png_write_sBIT(png_structp png_ptr,png_color_8p sbit,int color_type)934 png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type)
935 {
936 #ifdef PNG_USE_LOCAL_ARRAYS
937    PNG_sBIT;
938 #endif
939    png_byte buf[4];
940    png_size_t size;
941 
942    png_debug(1, "in png_write_sBIT");
943 
944    /* Make sure we don't depend upon the order of PNG_COLOR_8 */
945    if (color_type & PNG_COLOR_MASK_COLOR)
946    {
947       png_byte maxbits;
948 
949       maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 :
950                 png_ptr->usr_bit_depth);
951       if (sbit->red == 0 || sbit->red > maxbits ||
952           sbit->green == 0 || sbit->green > maxbits ||
953           sbit->blue == 0 || sbit->blue > maxbits)
954       {
955          png_warning(png_ptr, "Invalid sBIT depth specified");
956          return;
957       }
958       buf[0] = sbit->red;
959       buf[1] = sbit->green;
960       buf[2] = sbit->blue;
961       size = 3;
962    }
963    else
964    {
965       if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)
966       {
967          png_warning(png_ptr, "Invalid sBIT depth specified");
968          return;
969       }
970       buf[0] = sbit->gray;
971       size = 1;
972    }
973 
974    if (color_type & PNG_COLOR_MASK_ALPHA)
975    {
976       if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)
977       {
978          png_warning(png_ptr, "Invalid sBIT depth specified");
979          return;
980       }
981       buf[size++] = sbit->alpha;
982    }
983 
984    png_write_chunk(png_ptr, (png_bytep)png_sBIT, buf, size);
985 }
986 #endif
987 
988 #ifdef PNG_WRITE_cHRM_SUPPORTED
989 /* Write the cHRM chunk */
990 #ifdef PNG_FLOATING_POINT_SUPPORTED
991 void /* PRIVATE */
png_write_cHRM(png_structp png_ptr,double white_x,double white_y,double red_x,double red_y,double green_x,double green_y,double blue_x,double blue_y)992 png_write_cHRM(png_structp png_ptr, double white_x, double white_y,
993    double red_x, double red_y, double green_x, double green_y,
994    double blue_x, double blue_y)
995 {
996 #ifdef PNG_USE_LOCAL_ARRAYS
997    PNG_cHRM;
998 #endif
999    png_byte buf[32];
1000 
1001    png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y,
1002       int_green_x, int_green_y, int_blue_x, int_blue_y;
1003 
1004    png_debug(1, "in png_write_cHRM");
1005 
1006    int_white_x = (png_uint_32)(white_x * 100000.0 + 0.5);
1007    int_white_y = (png_uint_32)(white_y * 100000.0 + 0.5);
1008    int_red_x   = (png_uint_32)(red_x   * 100000.0 + 0.5);
1009    int_red_y   = (png_uint_32)(red_y   * 100000.0 + 0.5);
1010    int_green_x = (png_uint_32)(green_x * 100000.0 + 0.5);
1011    int_green_y = (png_uint_32)(green_y * 100000.0 + 0.5);
1012    int_blue_x  = (png_uint_32)(blue_x  * 100000.0 + 0.5);
1013    int_blue_y  = (png_uint_32)(blue_y  * 100000.0 + 0.5);
1014 
1015 #ifdef PNG_CHECK_cHRM_SUPPORTED
1016    if (png_check_cHRM_fixed(png_ptr, int_white_x, int_white_y,
1017       int_red_x, int_red_y, int_green_x, int_green_y, int_blue_x, int_blue_y))
1018 #endif
1019    {
1020       /* Each value is saved in 1/100,000ths */
1021 
1022       png_save_uint_32(buf, int_white_x);
1023       png_save_uint_32(buf + 4, int_white_y);
1024 
1025       png_save_uint_32(buf + 8, int_red_x);
1026       png_save_uint_32(buf + 12, int_red_y);
1027 
1028       png_save_uint_32(buf + 16, int_green_x);
1029       png_save_uint_32(buf + 20, int_green_y);
1030 
1031       png_save_uint_32(buf + 24, int_blue_x);
1032       png_save_uint_32(buf + 28, int_blue_y);
1033 
1034       png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32);
1035    }
1036 }
1037 #endif
1038 #ifdef PNG_FIXED_POINT_SUPPORTED
1039 void /* PRIVATE */
png_write_cHRM_fixed(png_structp png_ptr,png_fixed_point white_x,png_fixed_point white_y,png_fixed_point red_x,png_fixed_point red_y,png_fixed_point green_x,png_fixed_point green_y,png_fixed_point blue_x,png_fixed_point blue_y)1040 png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x,
1041    png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y,
1042    png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x,
1043    png_fixed_point blue_y)
1044 {
1045 #ifdef PNG_USE_LOCAL_ARRAYS
1046    PNG_cHRM;
1047 #endif
1048    png_byte buf[32];
1049 
1050    png_debug(1, "in png_write_cHRM");
1051 
1052    /* Each value is saved in 1/100,000ths */
1053 #ifdef PNG_CHECK_cHRM_SUPPORTED
1054    if (png_check_cHRM_fixed(png_ptr, white_x, white_y, red_x, red_y,
1055       green_x, green_y, blue_x, blue_y))
1056 #endif
1057    {
1058       png_save_uint_32(buf, (png_uint_32)white_x);
1059       png_save_uint_32(buf + 4, (png_uint_32)white_y);
1060 
1061       png_save_uint_32(buf + 8, (png_uint_32)red_x);
1062       png_save_uint_32(buf + 12, (png_uint_32)red_y);
1063 
1064       png_save_uint_32(buf + 16, (png_uint_32)green_x);
1065       png_save_uint_32(buf + 20, (png_uint_32)green_y);
1066 
1067       png_save_uint_32(buf + 24, (png_uint_32)blue_x);
1068       png_save_uint_32(buf + 28, (png_uint_32)blue_y);
1069 
1070       png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32);
1071    }
1072 }
1073 #endif
1074 #endif
1075 
1076 #ifdef PNG_WRITE_tRNS_SUPPORTED
1077 /* Write the tRNS chunk */
1078 void /* PRIVATE */
png_write_tRNS(png_structp png_ptr,png_bytep trans,png_color_16p tran,int num_trans,int color_type)1079 png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran,
1080    int num_trans, int color_type)
1081 {
1082 #ifdef PNG_USE_LOCAL_ARRAYS
1083    PNG_tRNS;
1084 #endif
1085    png_byte buf[6];
1086 
1087    png_debug(1, "in png_write_tRNS");
1088 
1089    if (color_type == PNG_COLOR_TYPE_PALETTE)
1090    {
1091       if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette)
1092       {
1093          png_warning(png_ptr, "Invalid number of transparent colors specified");
1094          return;
1095       }
1096       /* Write the chunk out as it is */
1097       png_write_chunk(png_ptr, (png_bytep)png_tRNS, trans,
1098         (png_size_t)num_trans);
1099    }
1100    else if (color_type == PNG_COLOR_TYPE_GRAY)
1101    {
1102       /* One 16 bit value */
1103       if (tran->gray >= (1 << png_ptr->bit_depth))
1104       {
1105          png_warning(png_ptr,
1106            "Ignoring attempt to write tRNS chunk out-of-range for bit_depth");
1107          return;
1108       }
1109       png_save_uint_16(buf, tran->gray);
1110       png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)2);
1111    }
1112    else if (color_type == PNG_COLOR_TYPE_RGB)
1113    {
1114       /* Three 16 bit values */
1115       png_save_uint_16(buf, tran->red);
1116       png_save_uint_16(buf + 2, tran->green);
1117       png_save_uint_16(buf + 4, tran->blue);
1118       if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
1119       {
1120          png_warning(png_ptr,
1121            "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");
1122          return;
1123       }
1124       png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)6);
1125    }
1126    else
1127    {
1128       png_warning(png_ptr, "Can't write tRNS with an alpha channel");
1129    }
1130 }
1131 #endif
1132 
1133 #ifdef PNG_WRITE_bKGD_SUPPORTED
1134 /* Write the background chunk */
1135 void /* PRIVATE */
png_write_bKGD(png_structp png_ptr,png_color_16p back,int color_type)1136 png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type)
1137 {
1138 #ifdef PNG_USE_LOCAL_ARRAYS
1139    PNG_bKGD;
1140 #endif
1141    png_byte buf[6];
1142 
1143    png_debug(1, "in png_write_bKGD");
1144 
1145    if (color_type == PNG_COLOR_TYPE_PALETTE)
1146    {
1147       if (
1148 #ifdef PNG_MNG_FEATURES_SUPPORTED
1149           (png_ptr->num_palette ||
1150           (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) &&
1151 #endif
1152          back->index >= png_ptr->num_palette)
1153       {
1154          png_warning(png_ptr, "Invalid background palette index");
1155          return;
1156       }
1157       buf[0] = back->index;
1158       png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)1);
1159    }
1160    else if (color_type & PNG_COLOR_MASK_COLOR)
1161    {
1162       png_save_uint_16(buf, back->red);
1163       png_save_uint_16(buf + 2, back->green);
1164       png_save_uint_16(buf + 4, back->blue);
1165       if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
1166       {
1167          png_warning(png_ptr,
1168            "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8");
1169          return;
1170       }
1171       png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)6);
1172    }
1173    else
1174    {
1175       if (back->gray >= (1 << png_ptr->bit_depth))
1176       {
1177          png_warning(png_ptr,
1178            "Ignoring attempt to write bKGD chunk out-of-range for bit_depth");
1179          return;
1180       }
1181       png_save_uint_16(buf, back->gray);
1182       png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)2);
1183    }
1184 }
1185 #endif
1186 
1187 #ifdef PNG_WRITE_hIST_SUPPORTED
1188 /* Write the histogram */
1189 void /* PRIVATE */
png_write_hIST(png_structp png_ptr,png_uint_16p hist,int num_hist)1190 png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist)
1191 {
1192 #ifdef PNG_USE_LOCAL_ARRAYS
1193    PNG_hIST;
1194 #endif
1195    int i;
1196    png_byte buf[3];
1197 
1198    png_debug(1, "in png_write_hIST");
1199 
1200    if (num_hist > (int)png_ptr->num_palette)
1201    {
1202       png_debug2(3, "num_hist = %d, num_palette = %d", num_hist,
1203          png_ptr->num_palette);
1204       png_warning(png_ptr, "Invalid number of histogram entries specified");
1205       return;
1206    }
1207 
1208    png_write_chunk_start(png_ptr, (png_bytep)png_hIST,
1209      (png_uint_32)(num_hist * 2));
1210    for (i = 0; i < num_hist; i++)
1211    {
1212       png_save_uint_16(buf, hist[i]);
1213       png_write_chunk_data(png_ptr, buf, (png_size_t)2);
1214    }
1215    png_write_chunk_end(png_ptr);
1216 }
1217 #endif
1218 
1219 #ifdef PNG_WRITE_tEXt_SUPPORTED
1220 /* Write a tEXt chunk */
1221 void /* PRIVATE */
png_write_tEXt(png_structp png_ptr,png_charp key,png_charp text,png_size_t text_len)1222 png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text,
1223    png_size_t text_len)
1224 {
1225 #ifdef PNG_USE_LOCAL_ARRAYS
1226    PNG_tEXt;
1227 #endif
1228    png_size_t key_len;
1229    png_charp new_key;
1230 
1231    png_debug(1, "in png_write_tEXt");
1232 
1233    if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0)
1234       return;
1235 
1236    if (text == NULL || *text == '\0')
1237       text_len = 0;
1238    else
1239       text_len = png_strlen(text);
1240 
1241    /* Make sure we include the 0 after the key */
1242    png_write_chunk_start(png_ptr, (png_bytep)png_tEXt,
1243       (png_uint_32)(key_len + text_len + 1));
1244    /*
1245     * We leave it to the application to meet PNG-1.0 requirements on the
1246     * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
1247     * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.
1248     * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
1249     */
1250    png_write_chunk_data(png_ptr, (png_bytep)new_key,
1251      (png_size_t)(key_len + 1));
1252    if (text_len)
1253       png_write_chunk_data(png_ptr, (png_bytep)text, (png_size_t)text_len);
1254 
1255    png_write_chunk_end(png_ptr);
1256    png_free(png_ptr, new_key);
1257 }
1258 #endif
1259 
1260 #ifdef PNG_WRITE_zTXt_SUPPORTED
1261 /* Write a compressed text chunk */
1262 void /* PRIVATE */
png_write_zTXt(png_structp png_ptr,png_charp key,png_charp text,png_size_t text_len,int compression)1263 png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text,
1264    png_size_t text_len, int compression)
1265 {
1266 #ifdef PNG_USE_LOCAL_ARRAYS
1267    PNG_zTXt;
1268 #endif
1269    png_size_t key_len;
1270    char buf[1];
1271    png_charp new_key;
1272    compression_state comp;
1273 
1274    png_debug(1, "in png_write_zTXt");
1275 
1276    comp.num_output_ptr = 0;
1277    comp.max_output_ptr = 0;
1278    comp.output_ptr = NULL;
1279    comp.input = NULL;
1280    comp.input_len = 0;
1281 
1282    if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0)
1283    {
1284       png_free(png_ptr, new_key);
1285       return;
1286    }
1287 
1288    if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE)
1289    {
1290       png_write_tEXt(png_ptr, new_key, text, (png_size_t)0);
1291       png_free(png_ptr, new_key);
1292       return;
1293    }
1294 
1295    text_len = png_strlen(text);
1296 
1297    /* Compute the compressed data; do it now for the length */
1298    text_len = png_text_compress(png_ptr, text, text_len, compression,
1299        &comp);
1300 
1301    /* Write start of chunk */
1302    png_write_chunk_start(png_ptr, (png_bytep)png_zTXt,
1303      (png_uint_32)(key_len+text_len + 2));
1304    /* Write key */
1305    png_write_chunk_data(png_ptr, (png_bytep)new_key,
1306      (png_size_t)(key_len + 1));
1307    png_free(png_ptr, new_key);
1308 
1309    buf[0] = (png_byte)compression;
1310    /* Write compression */
1311    png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1);
1312    /* Write the compressed data */
1313    png_write_compressed_data_out(png_ptr, &comp);
1314 
1315    /* Close the chunk */
1316    png_write_chunk_end(png_ptr);
1317 }
1318 #endif
1319 
1320 #ifdef PNG_WRITE_iTXt_SUPPORTED
1321 /* Write an iTXt chunk */
1322 void /* PRIVATE */
png_write_iTXt(png_structp png_ptr,int compression,png_charp key,png_charp lang,png_charp lang_key,png_charp text)1323 png_write_iTXt(png_structp png_ptr, int compression, png_charp key,
1324     png_charp lang, png_charp lang_key, png_charp text)
1325 {
1326 #ifdef PNG_USE_LOCAL_ARRAYS
1327    PNG_iTXt;
1328 #endif
1329    png_size_t lang_len, key_len, lang_key_len, text_len;
1330    png_charp new_lang;
1331    png_charp new_key = NULL;
1332    png_byte cbuf[2];
1333    compression_state comp;
1334 
1335    png_debug(1, "in png_write_iTXt");
1336 
1337    comp.num_output_ptr = 0;
1338    comp.max_output_ptr = 0;
1339    comp.output_ptr = NULL;
1340    comp.input = NULL;
1341 
1342    if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0)
1343       return;
1344 
1345    if ((lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0)
1346    {
1347       png_warning(png_ptr, "Empty language field in iTXt chunk");
1348       new_lang = NULL;
1349       lang_len = 0;
1350    }
1351 
1352    if (lang_key == NULL)
1353       lang_key_len = 0;
1354    else
1355       lang_key_len = png_strlen(lang_key);
1356 
1357    if (text == NULL)
1358       text_len = 0;
1359    else
1360       text_len = png_strlen(text);
1361 
1362    /* Compute the compressed data; do it now for the length */
1363    text_len = png_text_compress(png_ptr, text, text_len, compression-2,
1364       &comp);
1365 
1366 
1367    /* Make sure we include the compression flag, the compression byte,
1368     * and the NULs after the key, lang, and lang_key parts */
1369 
1370    png_write_chunk_start(png_ptr, (png_bytep)png_iTXt,
1371           (png_uint_32)(
1372         5 /* comp byte, comp flag, terminators for key, lang and lang_key */
1373         + key_len
1374         + lang_len
1375         + lang_key_len
1376         + text_len));
1377 
1378    /* We leave it to the application to meet PNG-1.0 requirements on the
1379     * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
1380     * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.
1381     * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
1382     */
1383    png_write_chunk_data(png_ptr, (png_bytep)new_key,
1384      (png_size_t)(key_len + 1));
1385 
1386    /* Set the compression flag */
1387    if (compression == PNG_ITXT_COMPRESSION_NONE || \
1388        compression == PNG_TEXT_COMPRESSION_NONE)
1389        cbuf[0] = 0;
1390    else /* compression == PNG_ITXT_COMPRESSION_zTXt */
1391        cbuf[0] = 1;
1392    /* Set the compression method */
1393    cbuf[1] = 0;
1394    png_write_chunk_data(png_ptr, cbuf, (png_size_t)2);
1395 
1396    cbuf[0] = 0;
1397    png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf),
1398      (png_size_t)(lang_len + 1));
1399    png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf),
1400      (png_size_t)(lang_key_len + 1));
1401    png_write_compressed_data_out(png_ptr, &comp);
1402 
1403    png_write_chunk_end(png_ptr);
1404    png_free(png_ptr, new_key);
1405    png_free(png_ptr, new_lang);
1406 }
1407 #endif
1408 
1409 #ifdef PNG_WRITE_oFFs_SUPPORTED
1410 /* Write the oFFs chunk */
1411 void /* PRIVATE */
png_write_oFFs(png_structp png_ptr,png_int_32 x_offset,png_int_32 y_offset,int unit_type)1412 png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset,
1413    int unit_type)
1414 {
1415 #ifdef PNG_USE_LOCAL_ARRAYS
1416    PNG_oFFs;
1417 #endif
1418    png_byte buf[9];
1419 
1420    png_debug(1, "in png_write_oFFs");
1421 
1422    if (unit_type >= PNG_OFFSET_LAST)
1423       png_warning(png_ptr, "Unrecognized unit type for oFFs chunk");
1424 
1425    png_save_int_32(buf, x_offset);
1426    png_save_int_32(buf + 4, y_offset);
1427    buf[8] = (png_byte)unit_type;
1428 
1429    png_write_chunk(png_ptr, (png_bytep)png_oFFs, buf, (png_size_t)9);
1430 }
1431 #endif
1432 #ifdef PNG_WRITE_pCAL_SUPPORTED
1433 /* Write the pCAL chunk (described in the PNG extensions document) */
1434 void /* PRIVATE */
png_write_pCAL(png_structp png_ptr,png_charp purpose,png_int_32 X0,png_int_32 X1,int type,int nparams,png_charp units,png_charpp params)1435 png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0,
1436    png_int_32 X1, int type, int nparams, png_charp units, png_charpp params)
1437 {
1438 #ifdef PNG_USE_LOCAL_ARRAYS
1439    PNG_pCAL;
1440 #endif
1441    png_size_t purpose_len, units_len, total_len;
1442    png_uint_32p params_len;
1443    png_byte buf[10];
1444    png_charp new_purpose;
1445    int i;
1446 
1447    png_debug1(1, "in png_write_pCAL (%d parameters)", nparams);
1448 
1449    if (type >= PNG_EQUATION_LAST)
1450       png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
1451 
1452    purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1;
1453    png_debug1(3, "pCAL purpose length = %d", (int)purpose_len);
1454    units_len = png_strlen(units) + (nparams == 0 ? 0 : 1);
1455    png_debug1(3, "pCAL units length = %d", (int)units_len);
1456    total_len = purpose_len + units_len + 10;
1457 
1458    params_len = (png_uint_32p)png_malloc(png_ptr,
1459       (png_uint_32)(nparams * png_sizeof(png_uint_32)));
1460 
1461    /* Find the length of each parameter, making sure we don't count the
1462       null terminator for the last parameter. */
1463    for (i = 0; i < nparams; i++)
1464    {
1465       params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
1466       png_debug2(3, "pCAL parameter %d length = %lu", i,
1467         (unsigned long) params_len[i]);
1468       total_len += (png_size_t)params_len[i];
1469    }
1470 
1471    png_debug1(3, "pCAL total length = %d", (int)total_len);
1472    png_write_chunk_start(png_ptr, (png_bytep)png_pCAL, (png_uint_32)total_len);
1473    png_write_chunk_data(png_ptr, (png_bytep)new_purpose,
1474      (png_size_t)purpose_len);
1475    png_save_int_32(buf, X0);
1476    png_save_int_32(buf + 4, X1);
1477    buf[8] = (png_byte)type;
1478    buf[9] = (png_byte)nparams;
1479    png_write_chunk_data(png_ptr, buf, (png_size_t)10);
1480    png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len);
1481 
1482    png_free(png_ptr, new_purpose);
1483 
1484    for (i = 0; i < nparams; i++)
1485    {
1486       png_write_chunk_data(png_ptr, (png_bytep)params[i],
1487          (png_size_t)params_len[i]);
1488    }
1489 
1490    png_free(png_ptr, params_len);
1491    png_write_chunk_end(png_ptr);
1492 }
1493 #endif
1494 
1495 #ifdef PNG_WRITE_sCAL_SUPPORTED
1496 /* Write the sCAL chunk */
1497 #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
1498 void /* PRIVATE */
png_write_sCAL(png_structp png_ptr,int unit,double width,double height)1499 png_write_sCAL(png_structp png_ptr, int unit, double width, double height)
1500 {
1501 #ifdef PNG_USE_LOCAL_ARRAYS
1502    PNG_sCAL;
1503 #endif
1504    char buf[64];
1505    png_size_t total_len;
1506 
1507    png_debug(1, "in png_write_sCAL");
1508 
1509    buf[0] = (char)unit;
1510 #ifdef _WIN32_WCE
1511 /* sprintf() function is not supported on WindowsCE */
1512    {
1513       wchar_t wc_buf[32];
1514       size_t wc_len;
1515       swprintf(wc_buf, TEXT("%12.12e"), width);
1516       wc_len = wcslen(wc_buf);
1517       WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + 1, wc_len, NULL,
1518           NULL);
1519       total_len = wc_len + 2;
1520       swprintf(wc_buf, TEXT("%12.12e"), height);
1521       wc_len = wcslen(wc_buf);
1522       WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + total_len, wc_len,
1523          NULL, NULL);
1524       total_len += wc_len;
1525    }
1526 #else
1527    png_snprintf(buf + 1, 63, "%12.12e", width);
1528    total_len = 1 + png_strlen(buf + 1) + 1;
1529    png_snprintf(buf + total_len, 64-total_len, "%12.12e", height);
1530    total_len += png_strlen(buf + total_len);
1531 #endif
1532 
1533    png_debug1(3, "sCAL total length = %u", (unsigned int)total_len);
1534    png_write_chunk(png_ptr, (png_bytep)png_sCAL, (png_bytep)buf, total_len);
1535 }
1536 #else
1537 #ifdef PNG_FIXED_POINT_SUPPORTED
1538 void /* PRIVATE */
png_write_sCAL_s(png_structp png_ptr,int unit,png_charp width,png_charp height)1539 png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width,
1540    png_charp height)
1541 {
1542 #ifdef PNG_USE_LOCAL_ARRAYS
1543    PNG_sCAL;
1544 #endif
1545    png_byte buf[64];
1546    png_size_t wlen, hlen, total_len;
1547 
1548    png_debug(1, "in png_write_sCAL_s");
1549 
1550    wlen = png_strlen(width);
1551    hlen = png_strlen(height);
1552    total_len = wlen + hlen + 2;
1553    if (total_len > 64)
1554    {
1555       png_warning(png_ptr, "Can't write sCAL (buffer too small)");
1556       return;
1557    }
1558 
1559    buf[0] = (png_byte)unit;
1560    png_memcpy(buf + 1, width, wlen + 1);      /* Append the '\0' here */
1561    png_memcpy(buf + wlen + 2, height, hlen);  /* Do NOT append the '\0' here */
1562 
1563    png_debug1(3, "sCAL total length = %u", (unsigned int)total_len);
1564    png_write_chunk(png_ptr, (png_bytep)png_sCAL, buf, total_len);
1565 }
1566 #endif
1567 #endif
1568 #endif
1569 
1570 #ifdef PNG_WRITE_pHYs_SUPPORTED
1571 /* Write the pHYs chunk */
1572 void /* PRIVATE */
png_write_pHYs(png_structp png_ptr,png_uint_32 x_pixels_per_unit,png_uint_32 y_pixels_per_unit,int unit_type)1573 png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit,
1574    png_uint_32 y_pixels_per_unit,
1575    int unit_type)
1576 {
1577 #ifdef PNG_USE_LOCAL_ARRAYS
1578    PNG_pHYs;
1579 #endif
1580    png_byte buf[9];
1581 
1582    png_debug(1, "in png_write_pHYs");
1583 
1584    if (unit_type >= PNG_RESOLUTION_LAST)
1585       png_warning(png_ptr, "Unrecognized unit type for pHYs chunk");
1586 
1587    png_save_uint_32(buf, x_pixels_per_unit);
1588    png_save_uint_32(buf + 4, y_pixels_per_unit);
1589    buf[8] = (png_byte)unit_type;
1590 
1591    png_write_chunk(png_ptr, (png_bytep)png_pHYs, buf, (png_size_t)9);
1592 }
1593 #endif
1594 
1595 #ifdef PNG_WRITE_tIME_SUPPORTED
1596 /* Write the tIME chunk.  Use either png_convert_from_struct_tm()
1597  * or png_convert_from_time_t(), or fill in the structure yourself.
1598  */
1599 void /* PRIVATE */
png_write_tIME(png_structp png_ptr,png_timep mod_time)1600 png_write_tIME(png_structp png_ptr, png_timep mod_time)
1601 {
1602 #ifdef PNG_USE_LOCAL_ARRAYS
1603    PNG_tIME;
1604 #endif
1605    png_byte buf[7];
1606 
1607    png_debug(1, "in png_write_tIME");
1608 
1609    if (mod_time->month  > 12 || mod_time->month  < 1 ||
1610        mod_time->day    > 31 || mod_time->day    < 1 ||
1611        mod_time->hour   > 23 || mod_time->second > 60)
1612    {
1613       png_warning(png_ptr, "Invalid time specified for tIME chunk");
1614       return;
1615    }
1616 
1617    png_save_uint_16(buf, mod_time->year);
1618    buf[2] = mod_time->month;
1619    buf[3] = mod_time->day;
1620    buf[4] = mod_time->hour;
1621    buf[5] = mod_time->minute;
1622    buf[6] = mod_time->second;
1623 
1624    png_write_chunk(png_ptr, (png_bytep)png_tIME, buf, (png_size_t)7);
1625 }
1626 #endif
1627 
1628 /* Initializes the row writing capability of libpng */
1629 void /* PRIVATE */
png_write_start_row(png_structp png_ptr)1630 png_write_start_row(png_structp png_ptr)
1631 {
1632 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
1633 #ifndef PNG_USE_GLOBAL_ARRAYS
1634    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
1635 
1636    /* Start of interlace block */
1637    int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
1638 
1639    /* Offset to next interlace block */
1640    int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
1641 
1642    /* Start of interlace block in the y direction */
1643    int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
1644 
1645    /* Offset to next interlace block in the y direction */
1646    int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
1647 #endif
1648 #endif
1649 
1650    png_size_t buf_size;
1651 
1652    png_debug(1, "in png_write_start_row");
1653 
1654    buf_size = (png_size_t)(PNG_ROWBYTES(
1655       png_ptr->usr_channels*png_ptr->usr_bit_depth, png_ptr->width) + 1);
1656 
1657    /* Set up row buffer */
1658    png_ptr->row_buf = (png_bytep)png_malloc(png_ptr,
1659      (png_uint_32)buf_size);
1660    png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;
1661 
1662 #ifdef PNG_WRITE_FILTER_SUPPORTED
1663    /* Set up filtering buffer, if using this filter */
1664    if (png_ptr->do_filter & PNG_FILTER_SUB)
1665    {
1666       png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
1667          (png_uint_32)(png_ptr->rowbytes + 1));
1668       png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
1669    }
1670 
1671    /* We only need to keep the previous row if we are using one of these. */
1672    if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH))
1673    {
1674       /* Set up previous row buffer */
1675       png_ptr->prev_row = (png_bytep)png_calloc(png_ptr,
1676          (png_uint_32)buf_size);
1677 
1678       if (png_ptr->do_filter & PNG_FILTER_UP)
1679       {
1680          png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
1681             (png_uint_32)(png_ptr->rowbytes + 1));
1682          png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
1683       }
1684 
1685       if (png_ptr->do_filter & PNG_FILTER_AVG)
1686       {
1687          png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
1688             (png_uint_32)(png_ptr->rowbytes + 1));
1689          png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
1690       }
1691 
1692       if (png_ptr->do_filter & PNG_FILTER_PAETH)
1693       {
1694          png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
1695             (png_uint_32)(png_ptr->rowbytes + 1));
1696          png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
1697       }
1698    }
1699 #endif /* PNG_WRITE_FILTER_SUPPORTED */
1700 
1701 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
1702    /* If interlaced, we need to set up width and height of pass */
1703    if (png_ptr->interlaced)
1704    {
1705       if (!(png_ptr->transformations & PNG_INTERLACE))
1706       {
1707          png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
1708             png_pass_ystart[0]) / png_pass_yinc[0];
1709          png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 -
1710             png_pass_start[0]) / png_pass_inc[0];
1711       }
1712       else
1713       {
1714          png_ptr->num_rows = png_ptr->height;
1715          png_ptr->usr_width = png_ptr->width;
1716       }
1717    }
1718    else
1719 #endif
1720    {
1721       png_ptr->num_rows = png_ptr->height;
1722       png_ptr->usr_width = png_ptr->width;
1723    }
1724    png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
1725    png_ptr->zstream.next_out = png_ptr->zbuf;
1726 }
1727 
1728 /* Internal use only.  Called when finished processing a row of data. */
1729 void /* PRIVATE */
png_write_finish_row(png_structp png_ptr)1730 png_write_finish_row(png_structp png_ptr)
1731 {
1732 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
1733 #ifndef PNG_USE_GLOBAL_ARRAYS
1734    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
1735 
1736    /* Start of interlace block */
1737    int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
1738 
1739    /* Offset to next interlace block */
1740    int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
1741 
1742    /* Start of interlace block in the y direction */
1743    int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
1744 
1745    /* Offset to next interlace block in the y direction */
1746    int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
1747 #endif
1748 #endif
1749 
1750    int ret;
1751 
1752    png_debug(1, "in png_write_finish_row");
1753 
1754    /* Next row */
1755    png_ptr->row_number++;
1756 
1757    /* See if we are done */
1758    if (png_ptr->row_number < png_ptr->num_rows)
1759       return;
1760 
1761 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
1762    /* If interlaced, go to next pass */
1763    if (png_ptr->interlaced)
1764    {
1765       png_ptr->row_number = 0;
1766       if (png_ptr->transformations & PNG_INTERLACE)
1767       {
1768          png_ptr->pass++;
1769       }
1770       else
1771       {
1772          /* Loop until we find a non-zero width or height pass */
1773          do
1774          {
1775             png_ptr->pass++;
1776             if (png_ptr->pass >= 7)
1777                break;
1778             png_ptr->usr_width = (png_ptr->width +
1779                png_pass_inc[png_ptr->pass] - 1 -
1780                png_pass_start[png_ptr->pass]) /
1781                png_pass_inc[png_ptr->pass];
1782             png_ptr->num_rows = (png_ptr->height +
1783                png_pass_yinc[png_ptr->pass] - 1 -
1784                png_pass_ystart[png_ptr->pass]) /
1785                png_pass_yinc[png_ptr->pass];
1786             if (png_ptr->transformations & PNG_INTERLACE)
1787                break;
1788          } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);
1789 
1790       }
1791 
1792       /* Reset the row above the image for the next pass */
1793       if (png_ptr->pass < 7)
1794       {
1795          if (png_ptr->prev_row != NULL)
1796             png_memset(png_ptr->prev_row, 0,
1797                (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels*
1798                png_ptr->usr_bit_depth, png_ptr->width)) + 1);
1799          return;
1800       }
1801    }
1802 #endif
1803 
1804    /* If we get here, we've just written the last row, so we need
1805       to flush the compressor */
1806    do
1807    {
1808       /* Tell the compressor we are done */
1809       ret = deflate(&png_ptr->zstream, Z_FINISH);
1810       /* Check for an error */
1811       if (ret == Z_OK)
1812       {
1813          /* Check to see if we need more room */
1814          if (!(png_ptr->zstream.avail_out))
1815          {
1816             png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
1817             png_ptr->zstream.next_out = png_ptr->zbuf;
1818             png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
1819          }
1820       }
1821       else if (ret != Z_STREAM_END)
1822       {
1823          if (png_ptr->zstream.msg != NULL)
1824             png_error(png_ptr, png_ptr->zstream.msg);
1825          else
1826             png_error(png_ptr, "zlib error");
1827       }
1828    } while (ret != Z_STREAM_END);
1829 
1830    /* Write any extra space */
1831    if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
1832    {
1833       png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size -
1834          png_ptr->zstream.avail_out);
1835    }
1836 
1837    deflateReset(&png_ptr->zstream);
1838    png_ptr->zstream.data_type = Z_BINARY;
1839 }
1840 
1841 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
1842 /* Pick out the correct pixels for the interlace pass.
1843  * The basic idea here is to go through the row with a source
1844  * pointer and a destination pointer (sp and dp), and copy the
1845  * correct pixels for the pass.  As the row gets compacted,
1846  * sp will always be >= dp, so we should never overwrite anything.
1847  * See the default: case for the easiest code to understand.
1848  */
1849 void /* PRIVATE */
png_do_write_interlace(png_row_infop row_info,png_bytep row,int pass)1850 png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
1851 {
1852    /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
1853 
1854 #ifndef PNG_USE_GLOBAL_ARRAYS
1855    /* Start of interlace block */
1856    int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
1857 
1858    /* Offset to next interlace block */
1859    int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
1860 #endif
1861 
1862    png_debug(1, "in png_do_write_interlace");
1863 
1864    /* We don't have to do anything on the last pass (6) */
1865 #ifdef PNG_USELESS_TESTS_SUPPORTED
1866    if (row != NULL && row_info != NULL && pass < 6)
1867 #else
1868    if (pass < 6)
1869 #endif
1870    {
1871       /* Each pixel depth is handled separately */
1872       switch (row_info->pixel_depth)
1873       {
1874          case 1:
1875          {
1876             png_bytep sp;
1877             png_bytep dp;
1878             int shift;
1879             int d;
1880             int value;
1881             png_uint_32 i;
1882             png_uint_32 row_width = row_info->width;
1883 
1884             dp = row;
1885             d = 0;
1886             shift = 7;
1887             for (i = png_pass_start[pass]; i < row_width;
1888                i += png_pass_inc[pass])
1889             {
1890                sp = row + (png_size_t)(i >> 3);
1891                value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;
1892                d |= (value << shift);
1893 
1894                if (shift == 0)
1895                {
1896                   shift = 7;
1897                   *dp++ = (png_byte)d;
1898                   d = 0;
1899                }
1900                else
1901                   shift--;
1902 
1903             }
1904             if (shift != 7)
1905                *dp = (png_byte)d;
1906             break;
1907          }
1908          case 2:
1909          {
1910             png_bytep sp;
1911             png_bytep dp;
1912             int shift;
1913             int d;
1914             int value;
1915             png_uint_32 i;
1916             png_uint_32 row_width = row_info->width;
1917 
1918             dp = row;
1919             shift = 6;
1920             d = 0;
1921             for (i = png_pass_start[pass]; i < row_width;
1922                i += png_pass_inc[pass])
1923             {
1924                sp = row + (png_size_t)(i >> 2);
1925                value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;
1926                d |= (value << shift);
1927 
1928                if (shift == 0)
1929                {
1930                   shift = 6;
1931                   *dp++ = (png_byte)d;
1932                   d = 0;
1933                }
1934                else
1935                   shift -= 2;
1936             }
1937             if (shift != 6)
1938                    *dp = (png_byte)d;
1939             break;
1940          }
1941          case 4:
1942          {
1943             png_bytep sp;
1944             png_bytep dp;
1945             int shift;
1946             int d;
1947             int value;
1948             png_uint_32 i;
1949             png_uint_32 row_width = row_info->width;
1950 
1951             dp = row;
1952             shift = 4;
1953             d = 0;
1954             for (i = png_pass_start[pass]; i < row_width;
1955                i += png_pass_inc[pass])
1956             {
1957                sp = row + (png_size_t)(i >> 1);
1958                value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;
1959                d |= (value << shift);
1960 
1961                if (shift == 0)
1962                {
1963                   shift = 4;
1964                   *dp++ = (png_byte)d;
1965                   d = 0;
1966                }
1967                else
1968                   shift -= 4;
1969             }
1970             if (shift != 4)
1971                *dp = (png_byte)d;
1972             break;
1973          }
1974          default:
1975          {
1976             png_bytep sp;
1977             png_bytep dp;
1978             png_uint_32 i;
1979             png_uint_32 row_width = row_info->width;
1980             png_size_t pixel_bytes;
1981 
1982             /* Start at the beginning */
1983             dp = row;
1984             /* Find out how many bytes each pixel takes up */
1985             pixel_bytes = (row_info->pixel_depth >> 3);
1986             /* Loop through the row, only looking at the pixels that
1987                matter */
1988             for (i = png_pass_start[pass]; i < row_width;
1989                i += png_pass_inc[pass])
1990             {
1991                /* Find out where the original pixel is */
1992                sp = row + (png_size_t)i * pixel_bytes;
1993                /* Move the pixel */
1994                if (dp != sp)
1995                   png_memcpy(dp, sp, pixel_bytes);
1996                /* Next pixel */
1997                dp += pixel_bytes;
1998             }
1999             break;
2000          }
2001       }
2002       /* Set new row width */
2003       row_info->width = (row_info->width +
2004          png_pass_inc[pass] - 1 -
2005          png_pass_start[pass]) /
2006          png_pass_inc[pass];
2007          row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
2008             row_info->width);
2009    }
2010 }
2011 #endif
2012 
2013 /* This filters the row, chooses which filter to use, if it has not already
2014  * been specified by the application, and then writes the row out with the
2015  * chosen filter.
2016  */
2017 #define PNG_MAXSUM (((png_uint_32)(-1)) >> 1)
2018 #define PNG_HISHIFT 10
2019 #define PNG_LOMASK ((png_uint_32)0xffffL)
2020 #define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT))
2021 void /* PRIVATE */
png_write_find_filter(png_structp png_ptr,png_row_infop row_info)2022 png_write_find_filter(png_structp png_ptr, png_row_infop row_info)
2023 {
2024    png_bytep best_row;
2025 #ifdef PNG_WRITE_FILTER_SUPPORTED
2026    png_bytep prev_row, row_buf;
2027    png_uint_32 mins, bpp;
2028    png_byte filter_to_do = png_ptr->do_filter;
2029    png_uint_32 row_bytes = row_info->rowbytes;
2030 
2031    png_debug(1, "in png_write_find_filter");
2032 
2033    /* Find out how many bytes offset each pixel is */
2034    bpp = (row_info->pixel_depth + 7) >> 3;
2035 
2036    prev_row = png_ptr->prev_row;
2037 #endif
2038    best_row = png_ptr->row_buf;
2039 #ifdef PNG_WRITE_FILTER_SUPPORTED
2040    row_buf = best_row;
2041    mins = PNG_MAXSUM;
2042 
2043    /* The prediction method we use is to find which method provides the
2044     * smallest value when summing the absolute values of the distances
2045     * from zero, using anything >= 128 as negative numbers.  This is known
2046     * as the "minimum sum of absolute differences" heuristic.  Other
2047     * heuristics are the "weighted minimum sum of absolute differences"
2048     * (experimental and can in theory improve compression), and the "zlib
2049     * predictive" method (not implemented yet), which does test compressions
2050     * of lines using different filter methods, and then chooses the
2051     * (series of) filter(s) that give minimum compressed data size (VERY
2052     * computationally expensive).
2053     *
2054     * GRR 980525:  consider also
2055     *   (1) minimum sum of absolute differences from running average (i.e.,
2056     *       keep running sum of non-absolute differences & count of bytes)
2057     *       [track dispersion, too?  restart average if dispersion too large?]
2058     *  (1b) minimum sum of absolute differences from sliding average, probably
2059     *       with window size <= deflate window (usually 32K)
2060     *   (2) minimum sum of squared differences from zero or running average
2061     *       (i.e., ~ root-mean-square approach)
2062     */
2063 
2064 
2065    /* We don't need to test the 'no filter' case if this is the only filter
2066     * that has been chosen, as it doesn't actually do anything to the data.
2067     */
2068    if ((filter_to_do & PNG_FILTER_NONE) &&
2069        filter_to_do != PNG_FILTER_NONE)
2070    {
2071       png_bytep rp;
2072       png_uint_32 sum = 0;
2073       png_uint_32 i;
2074       int v;
2075 
2076       for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
2077       {
2078          v = *rp;
2079          sum += (v < 128) ? v : 256 - v;
2080       }
2081 
2082       mins = sum;
2083    }
2084 
2085    /* Sub filter */
2086    if (filter_to_do == PNG_FILTER_SUB)
2087    /* It's the only filter so no testing is needed */
2088    {
2089       png_bytep rp, lp, dp;
2090       png_uint_32 i;
2091       for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
2092            i++, rp++, dp++)
2093       {
2094          *dp = *rp;
2095       }
2096       for (lp = row_buf + 1; i < row_bytes;
2097          i++, rp++, lp++, dp++)
2098       {
2099          *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
2100       }
2101       best_row = png_ptr->sub_row;
2102    }
2103 
2104    else if (filter_to_do & PNG_FILTER_SUB)
2105    {
2106       png_bytep rp, dp, lp;
2107       png_uint_32 sum = 0, lmins = mins;
2108       png_uint_32 i;
2109       int v;
2110 
2111       for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
2112            i++, rp++, dp++)
2113       {
2114          v = *dp = *rp;
2115 
2116          sum += (v < 128) ? v : 256 - v;
2117       }
2118       for (lp = row_buf + 1; i < row_bytes;
2119          i++, rp++, lp++, dp++)
2120       {
2121          v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
2122 
2123          sum += (v < 128) ? v : 256 - v;
2124 
2125          if (sum > lmins)  /* We are already worse, don't continue. */
2126             break;
2127       }
2128 
2129       if (sum < mins)
2130       {
2131          mins = sum;
2132          best_row = png_ptr->sub_row;
2133       }
2134    }
2135 
2136    /* Up filter */
2137    if (filter_to_do == PNG_FILTER_UP)
2138    {
2139       png_bytep rp, dp, pp;
2140       png_uint_32 i;
2141 
2142       for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
2143            pp = prev_row + 1; i < row_bytes;
2144            i++, rp++, pp++, dp++)
2145       {
2146          *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
2147       }
2148       best_row = png_ptr->up_row;
2149    }
2150 
2151    else if (filter_to_do & PNG_FILTER_UP)
2152    {
2153       png_bytep rp, dp, pp;
2154       png_uint_32 sum = 0, lmins = mins;
2155       png_uint_32 i;
2156       int v;
2157 
2158       for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
2159            pp = prev_row + 1; i < row_bytes; i++)
2160       {
2161          v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
2162 
2163          sum += (v < 128) ? v : 256 - v;
2164 
2165          if (sum > lmins)  /* We are already worse, don't continue. */
2166             break;
2167       }
2168 
2169       if (sum < mins)
2170       {
2171          mins = sum;
2172          best_row = png_ptr->up_row;
2173       }
2174    }
2175 
2176    /* Avg filter */
2177    if (filter_to_do == PNG_FILTER_AVG)
2178    {
2179       png_bytep rp, dp, pp, lp;
2180       png_uint_32 i;
2181       for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
2182            pp = prev_row + 1; i < bpp; i++)
2183       {
2184          *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
2185       }
2186       for (lp = row_buf + 1; i < row_bytes; i++)
2187       {
2188          *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
2189                  & 0xff);
2190       }
2191       best_row = png_ptr->avg_row;
2192    }
2193 
2194    else if (filter_to_do & PNG_FILTER_AVG)
2195    {
2196       png_bytep rp, dp, pp, lp;
2197       png_uint_32 sum = 0, lmins = mins;
2198       png_uint_32 i;
2199       int v;
2200 
2201       for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
2202            pp = prev_row + 1; i < bpp; i++)
2203       {
2204          v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
2205 
2206          sum += (v < 128) ? v : 256 - v;
2207       }
2208       for (lp = row_buf + 1; i < row_bytes; i++)
2209       {
2210          v = *dp++ =
2211           (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff);
2212 
2213          sum += (v < 128) ? v : 256 - v;
2214 
2215          if (sum > lmins)  /* We are already worse, don't continue. */
2216             break;
2217       }
2218 
2219       if (sum < mins)
2220       {
2221          mins = sum;
2222          best_row = png_ptr->avg_row;
2223       }
2224    }
2225 
2226    /* Paeth filter */
2227    if (filter_to_do == PNG_FILTER_PAETH)
2228    {
2229       png_bytep rp, dp, pp, cp, lp;
2230       png_uint_32 i;
2231       for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
2232            pp = prev_row + 1; i < bpp; i++)
2233       {
2234          *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
2235       }
2236 
2237       for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
2238       {
2239          int a, b, c, pa, pb, pc, p;
2240 
2241          b = *pp++;
2242          c = *cp++;
2243          a = *lp++;
2244 
2245          p = b - c;
2246          pc = a - c;
2247 
2248 #ifdef PNG_USE_ABS
2249          pa = abs(p);
2250          pb = abs(pc);
2251          pc = abs(p + pc);
2252 #else
2253          pa = p < 0 ? -p : p;
2254          pb = pc < 0 ? -pc : pc;
2255          pc = (p + pc) < 0 ? -(p + pc) : p + pc;
2256 #endif
2257 
2258          p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
2259 
2260          *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
2261       }
2262       best_row = png_ptr->paeth_row;
2263    }
2264 
2265    else if (filter_to_do & PNG_FILTER_PAETH)
2266    {
2267       png_bytep rp, dp, pp, cp, lp;
2268       png_uint_32 sum = 0, lmins = mins;
2269       png_uint_32 i;
2270       int v;
2271 
2272       for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
2273            pp = prev_row + 1; i < bpp; i++)
2274       {
2275          v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
2276 
2277          sum += (v < 128) ? v : 256 - v;
2278       }
2279 
2280       for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
2281       {
2282          int a, b, c, pa, pb, pc, p;
2283 
2284          b = *pp++;
2285          c = *cp++;
2286          a = *lp++;
2287 
2288 #ifndef PNG_SLOW_PAETH
2289          p = b - c;
2290          pc = a - c;
2291 #ifdef PNG_USE_ABS
2292          pa = abs(p);
2293          pb = abs(pc);
2294          pc = abs(p + pc);
2295 #else
2296          pa = p < 0 ? -p : p;
2297          pb = pc < 0 ? -pc : pc;
2298          pc = (p + pc) < 0 ? -(p + pc) : p + pc;
2299 #endif
2300          p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
2301 #else /* PNG_SLOW_PAETH */
2302          p = a + b - c;
2303          pa = abs(p - a);
2304          pb = abs(p - b);
2305          pc = abs(p - c);
2306          if (pa <= pb && pa <= pc)
2307             p = a;
2308          else if (pb <= pc)
2309             p = b;
2310          else
2311             p = c;
2312 #endif /* PNG_SLOW_PAETH */
2313 
2314          v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
2315 
2316          sum += (v < 128) ? v : 256 - v;
2317 
2318          if (sum > lmins)  /* We are already worse, don't continue. */
2319             break;
2320       }
2321 
2322       if (sum < mins)
2323       {
2324          best_row = png_ptr->paeth_row;
2325       }
2326    }
2327 #endif /* PNG_WRITE_FILTER_SUPPORTED */
2328    /* Do the actual writing of the filtered row data from the chosen filter. */
2329 
2330    png_write_filtered_row(png_ptr, best_row);
2331 }
2332 
2333 
2334 /* Do the actual writing of a previously filtered row. */
2335 void /* PRIVATE */
png_write_filtered_row(png_structp png_ptr,png_bytep filtered_row)2336 png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row)
2337 {
2338    png_debug(1, "in png_write_filtered_row");
2339 
2340    png_debug1(2, "filter = %d", filtered_row[0]);
2341    /* Set up the zlib input buffer */
2342 
2343    png_ptr->zstream.next_in = filtered_row;
2344    png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1;
2345    /* Repeat until we have compressed all the data */
2346    do
2347    {
2348       int ret; /* Return of zlib */
2349 
2350       /* Compress the data */
2351       ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
2352       /* Check for compression errors */
2353       if (ret != Z_OK)
2354       {
2355          if (png_ptr->zstream.msg != NULL)
2356             png_error(png_ptr, png_ptr->zstream.msg);
2357          else
2358             png_error(png_ptr, "zlib error");
2359       }
2360 
2361       /* See if it is time to write another IDAT */
2362       if (!(png_ptr->zstream.avail_out))
2363       {
2364          /* Write the IDAT and reset the zlib output buffer */
2365          png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
2366          png_ptr->zstream.next_out = png_ptr->zbuf;
2367          png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
2368       }
2369    /* Repeat until all data has been compressed */
2370    } while (png_ptr->zstream.avail_in);
2371 
2372    /* Swap the current and previous rows */
2373    if (png_ptr->prev_row != NULL)
2374    {
2375       png_bytep tptr;
2376 
2377       tptr = png_ptr->prev_row;
2378       png_ptr->prev_row = png_ptr->row_buf;
2379       png_ptr->row_buf = tptr;
2380    }
2381 
2382    /* Finish row - updates counters and flushes zlib if last row */
2383    png_write_finish_row(png_ptr);
2384 
2385 #ifdef PNG_WRITE_FLUSH_SUPPORTED
2386    png_ptr->flush_rows++;
2387 
2388    if (png_ptr->flush_dist > 0 &&
2389        png_ptr->flush_rows >= png_ptr->flush_dist)
2390    {
2391       png_write_flush(png_ptr);
2392    }
2393 #endif
2394 }
2395 #endif /* PNG_WRITE_SUPPORTED */
2396