1
2/* pngwrite.c - general routines to write a PNG file
3 *
4 * libpng 1.2.7 - September 12, 2004
5 * For conditions of distribution and use, see copyright notice in png.h
6 * Copyright (c) 1998-2004 Glenn Randers-Pehrson
7 * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
8 * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
9 */
10
11/* get internal access to png.h */
12#define PNG_INTERNAL
13#include "png.h"
14#ifdef PNG_WRITE_SUPPORTED
15
16/* Writes all the PNG information.  This is the suggested way to use the
17 * library.  If you have a new chunk to add, make a function to write it,
18 * and put it in the correct location here.  If you want the chunk written
19 * after the image data, put it in png_write_end().  I strongly encourage
20 * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
21 * the chunk, as that will keep the code from breaking if you want to just
22 * write a plain PNG file.  If you have long comments, I suggest writing
23 * them in png_write_end(), and compressing them.
24 */
25void PNGAPI
26png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr)
27{
28   png_debug(1, "in png_write_info_before_PLTE\n");
29   if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
30   {
31   png_write_sig(png_ptr); /* write PNG signature */
32#if defined(PNG_MNG_FEATURES_SUPPORTED)
33   if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&(png_ptr->mng_features_permitted))
34   {
35      png_warning(png_ptr,"MNG features are not allowed in a PNG datastream\n");
36      png_ptr->mng_features_permitted=0;
37   }
38#endif
39   /* write IHDR information. */
40   png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
41      info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
42      info_ptr->filter_type,
43#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
44      info_ptr->interlace_type);
45#else
46      0);
47#endif
48   /* the rest of these check to see if the valid field has the appropriate
49      flag set, and if it does, writes the chunk. */
50#if defined(PNG_WRITE_gAMA_SUPPORTED)
51   if (info_ptr->valid & PNG_INFO_gAMA)
52   {
53#  ifdef PNG_FLOATING_POINT_SUPPORTED
54      png_write_gAMA(png_ptr, info_ptr->gamma);
55#else
56#ifdef PNG_FIXED_POINT_SUPPORTED
57      png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma);
58#  endif
59#endif
60   }
61#endif
62#if defined(PNG_WRITE_sRGB_SUPPORTED)
63   if (info_ptr->valid & PNG_INFO_sRGB)
64      png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent);
65#endif
66#if defined(PNG_WRITE_iCCP_SUPPORTED)
67   if (info_ptr->valid & PNG_INFO_iCCP)
68      png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE,
69                     info_ptr->iccp_profile, (int)info_ptr->iccp_proflen);
70#endif
71#if defined(PNG_WRITE_sBIT_SUPPORTED)
72   if (info_ptr->valid & PNG_INFO_sBIT)
73      png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
74#endif
75#if defined(PNG_WRITE_cHRM_SUPPORTED)
76   if (info_ptr->valid & PNG_INFO_cHRM)
77   {
78#ifdef PNG_FLOATING_POINT_SUPPORTED
79      png_write_cHRM(png_ptr,
80         info_ptr->x_white, info_ptr->y_white,
81         info_ptr->x_red, info_ptr->y_red,
82         info_ptr->x_green, info_ptr->y_green,
83         info_ptr->x_blue, info_ptr->y_blue);
84#else
85#  ifdef PNG_FIXED_POINT_SUPPORTED
86      png_write_cHRM_fixed(png_ptr,
87         info_ptr->int_x_white, info_ptr->int_y_white,
88         info_ptr->int_x_red, info_ptr->int_y_red,
89         info_ptr->int_x_green, info_ptr->int_y_green,
90         info_ptr->int_x_blue, info_ptr->int_y_blue);
91#  endif
92#endif
93   }
94#endif
95#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
96   if (info_ptr->unknown_chunks_num)
97   {
98       png_unknown_chunk *up;
99
100       png_debug(5, "writing extra chunks\n");
101
102       for (up = info_ptr->unknown_chunks;
103            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
104            up++)
105       {
106         int keep=png_handle_as_unknown(png_ptr, up->name);
107         if (keep != PNG_HANDLE_CHUNK_NEVER &&
108            up->location && !(up->location & PNG_HAVE_PLTE) &&
109            !(up->location & PNG_HAVE_IDAT) &&
110            ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
111            (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
112         {
113            png_write_chunk(png_ptr, up->name, up->data, up->size);
114         }
115       }
116   }
117#endif
118      png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
119   }
120}
121
122void PNGAPI
123png_write_info(png_structp png_ptr, png_infop info_ptr)
124{
125#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
126   int i;
127#endif
128
129   png_debug(1, "in png_write_info\n");
130
131   png_write_info_before_PLTE(png_ptr, info_ptr);
132
133   if (info_ptr->valid & PNG_INFO_PLTE)
134      png_write_PLTE(png_ptr, info_ptr->palette,
135         (png_uint_32)info_ptr->num_palette);
136   else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
137      png_error(png_ptr, "Valid palette required for paletted images\n");
138
139#if defined(PNG_WRITE_tRNS_SUPPORTED)
140   if (info_ptr->valid & PNG_INFO_tRNS)
141      {
142#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
143         /* invert the alpha channel (in tRNS) */
144         if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&
145            info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
146         {
147            int j;
148            for (j=0; j<(int)info_ptr->num_trans; j++)
149               info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]);
150         }
151#endif
152      png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values),
153         info_ptr->num_trans, info_ptr->color_type);
154      }
155#endif
156#if defined(PNG_WRITE_bKGD_SUPPORTED)
157   if (info_ptr->valid & PNG_INFO_bKGD)
158      png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
159#endif
160#if defined(PNG_WRITE_hIST_SUPPORTED)
161   if (info_ptr->valid & PNG_INFO_hIST)
162      png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
163#endif
164#if defined(PNG_WRITE_oFFs_SUPPORTED)
165   if (info_ptr->valid & PNG_INFO_oFFs)
166      png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
167         info_ptr->offset_unit_type);
168#endif
169#if defined(PNG_WRITE_pCAL_SUPPORTED)
170   if (info_ptr->valid & PNG_INFO_pCAL)
171      png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
172         info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
173         info_ptr->pcal_units, info_ptr->pcal_params);
174#endif
175#if defined(PNG_WRITE_sCAL_SUPPORTED)
176   if (info_ptr->valid & PNG_INFO_sCAL)
177#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO)
178      png_write_sCAL(png_ptr, (int)info_ptr->scal_unit,
179          info_ptr->scal_pixel_width, info_ptr->scal_pixel_height);
180#else
181#ifdef PNG_FIXED_POINT_SUPPORTED
182      png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
183          info_ptr->scal_s_width, info_ptr->scal_s_height);
184#else
185      png_warning(png_ptr,
186          "png_write_sCAL not supported; sCAL chunk not written.\n");
187#endif
188#endif
189#endif
190#if defined(PNG_WRITE_pHYs_SUPPORTED)
191   if (info_ptr->valid & PNG_INFO_pHYs)
192      png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
193         info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
194#endif
195#if defined(PNG_WRITE_tIME_SUPPORTED)
196   if (info_ptr->valid & PNG_INFO_tIME)
197   {
198      png_write_tIME(png_ptr, &(info_ptr->mod_time));
199      png_ptr->mode |= PNG_WROTE_tIME;
200   }
201#endif
202#if defined(PNG_WRITE_sPLT_SUPPORTED)
203   if (info_ptr->valid & PNG_INFO_sPLT)
204     for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
205       png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
206#endif
207#if defined(PNG_WRITE_TEXT_SUPPORTED)
208   /* Check to see if we need to write text chunks */
209   for (i = 0; i < info_ptr->num_text; i++)
210   {
211      png_debug2(2, "Writing header text chunk %d, type %d\n", i,
212         info_ptr->text[i].compression);
213      /* an internationalized chunk? */
214      if (info_ptr->text[i].compression > 0)
215      {
216#if defined(PNG_WRITE_iTXt_SUPPORTED)
217          /* write international chunk */
218          png_write_iTXt(png_ptr,
219                         info_ptr->text[i].compression,
220                         info_ptr->text[i].key,
221                         info_ptr->text[i].lang,
222                         info_ptr->text[i].lang_key,
223                         info_ptr->text[i].text);
224#else
225          png_warning(png_ptr, "Unable to write international text\n");
226#endif
227          /* Mark this chunk as written */
228          info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
229      }
230      /* If we want a compressed text chunk */
231      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
232      {
233#if defined(PNG_WRITE_zTXt_SUPPORTED)
234         /* write compressed chunk */
235         png_write_zTXt(png_ptr, info_ptr->text[i].key,
236            info_ptr->text[i].text, 0,
237            info_ptr->text[i].compression);
238#else
239         png_warning(png_ptr, "Unable to write compressed text\n");
240#endif
241         /* Mark this chunk as written */
242         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
243      }
244      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
245      {
246#if defined(PNG_WRITE_tEXt_SUPPORTED)
247         /* write uncompressed chunk */
248         png_write_tEXt(png_ptr, info_ptr->text[i].key,
249                         info_ptr->text[i].text,
250                         0);
251#else
252         png_warning(png_ptr, "Unable to write uncompressed text\n");
253#endif
254         /* Mark this chunk as written */
255         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
256      }
257   }
258#endif
259#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
260   if (info_ptr->unknown_chunks_num)
261   {
262       png_unknown_chunk *up;
263
264       png_debug(5, "writing extra chunks\n");
265
266       for (up = info_ptr->unknown_chunks;
267            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
268            up++)
269       {
270         int keep=png_handle_as_unknown(png_ptr, up->name);
271         if (keep != PNG_HANDLE_CHUNK_NEVER &&
272            up->location && (up->location & PNG_HAVE_PLTE) &&
273            !(up->location & PNG_HAVE_IDAT) &&
274            ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
275            (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
276         {
277            png_write_chunk(png_ptr, up->name, up->data, up->size);
278         }
279       }
280   }
281#endif
282}
283
284/* Writes the end of the PNG file.  If you don't want to write comments or
285 * time information, you can pass NULL for info.  If you already wrote these
286 * in png_write_info(), do not write them again here.  If you have long
287 * comments, I suggest writing them here, and compressing them.
288 */
289void PNGAPI
290png_write_end(png_structp png_ptr, png_infop info_ptr)
291{
292   png_debug(1, "in png_write_end\n");
293   if (!(png_ptr->mode & PNG_HAVE_IDAT))
294      png_error(png_ptr, "No IDATs written into file");
295
296   /* see if user wants us to write information chunks */
297   if (info_ptr != NULL)
298   {
299#if defined(PNG_WRITE_TEXT_SUPPORTED)
300      int i; /* local index variable */
301#endif
302#if defined(PNG_WRITE_tIME_SUPPORTED)
303      /* check to see if user has supplied a time chunk */
304      if ((info_ptr->valid & PNG_INFO_tIME) &&
305         !(png_ptr->mode & PNG_WROTE_tIME))
306         png_write_tIME(png_ptr, &(info_ptr->mod_time));
307#endif
308#if defined(PNG_WRITE_TEXT_SUPPORTED)
309      /* loop through comment chunks */
310      for (i = 0; i < info_ptr->num_text; i++)
311      {
312         png_debug2(2, "Writing trailer text chunk %d, type %d\n", i,
313            info_ptr->text[i].compression);
314         /* an internationalized chunk? */
315         if (info_ptr->text[i].compression > 0)
316         {
317#if defined(PNG_WRITE_iTXt_SUPPORTED)
318             /* write international chunk */
319             png_write_iTXt(png_ptr,
320                         info_ptr->text[i].compression,
321                         info_ptr->text[i].key,
322                         info_ptr->text[i].lang,
323                         info_ptr->text[i].lang_key,
324                         info_ptr->text[i].text);
325#else
326             png_warning(png_ptr, "Unable to write international text\n");
327#endif
328             /* Mark this chunk as written */
329             info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
330         }
331         else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
332         {
333#if defined(PNG_WRITE_zTXt_SUPPORTED)
334            /* write compressed chunk */
335            png_write_zTXt(png_ptr, info_ptr->text[i].key,
336               info_ptr->text[i].text, 0,
337               info_ptr->text[i].compression);
338#else
339            png_warning(png_ptr, "Unable to write compressed text\n");
340#endif
341            /* Mark this chunk as written */
342            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
343         }
344         else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
345         {
346#if defined(PNG_WRITE_tEXt_SUPPORTED)
347            /* write uncompressed chunk */
348            png_write_tEXt(png_ptr, info_ptr->text[i].key,
349               info_ptr->text[i].text, 0);
350#else
351            png_warning(png_ptr, "Unable to write uncompressed text\n");
352#endif
353
354            /* Mark this chunk as written */
355            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
356         }
357      }
358#endif
359#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
360   if (info_ptr->unknown_chunks_num)
361   {
362       png_unknown_chunk *up;
363
364       png_debug(5, "writing extra chunks\n");
365
366       for (up = info_ptr->unknown_chunks;
367            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
368            up++)
369       {
370         int keep=png_handle_as_unknown(png_ptr, up->name);
371         if (keep != PNG_HANDLE_CHUNK_NEVER &&
372            up->location && (up->location & PNG_AFTER_IDAT) &&
373            ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
374            (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
375         {
376            png_write_chunk(png_ptr, up->name, up->data, up->size);
377         }
378       }
379   }
380#endif
381   }
382
383   png_ptr->mode |= PNG_AFTER_IDAT;
384
385   /* write end of PNG file */
386   png_write_IEND(png_ptr);
387#if 0
388/* This flush, added in libpng-1.0.8,  causes some applications to crash
389   because they do not set png_ptr->output_flush_fn */
390   png_flush(png_ptr);
391#endif
392}
393
394#if defined(PNG_WRITE_tIME_SUPPORTED)
395#if !defined(_WIN32_WCE)
396/* "time.h" functions are not supported on WindowsCE */
397void PNGAPI
398png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime)
399{
400   png_debug(1, "in png_convert_from_struct_tm\n");
401   ptime->year = (png_uint_16)(1900 + ttime->tm_year);
402   ptime->month = (png_byte)(ttime->tm_mon + 1);
403   ptime->day = (png_byte)ttime->tm_mday;
404   ptime->hour = (png_byte)ttime->tm_hour;
405   ptime->minute = (png_byte)ttime->tm_min;
406   ptime->second = (png_byte)ttime->tm_sec;
407}
408
409void PNGAPI
410png_convert_from_time_t(png_timep ptime, time_t ttime)
411{
412   struct tm *tbuf;
413
414   png_debug(1, "in png_convert_from_time_t\n");
415   tbuf = gmtime(&ttime);
416   png_convert_from_struct_tm(ptime, tbuf);
417}
418#endif
419#endif
420
421/* Initialize png_ptr structure, and allocate any memory needed */
422png_structp PNGAPI
423png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr,
424   png_error_ptr error_fn, png_error_ptr warn_fn)
425{
426#ifdef PNG_USER_MEM_SUPPORTED
427   return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
428      warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL));
429}
430
431/* Alternate initialize png_ptr structure, and allocate any memory needed */
432png_structp PNGAPI
433png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr,
434   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
435   png_malloc_ptr malloc_fn, png_free_ptr free_fn)
436{
437#endif /* PNG_USER_MEM_SUPPORTED */
438   png_structp png_ptr;
439#ifdef PNG_SETJMP_SUPPORTED
440#ifdef USE_FAR_KEYWORD
441   jmp_buf jmpbuf;
442#endif
443#endif
444   int i;
445   png_debug(1, "in png_create_write_struct\n");
446#ifdef PNG_USER_MEM_SUPPORTED
447   png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
448      (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr);
449#else
450   png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
451#endif /* PNG_USER_MEM_SUPPORTED */
452   if (png_ptr == NULL)
453      return (NULL);
454
455#if !defined(PNG_1_0_X)
456#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
457   png_init_mmx_flags(png_ptr);   /* 1.2.0 addition */
458#endif
459#endif /* PNG_1_0_X */
460
461   /* added at libpng-1.2.6 */
462#ifdef PNG_SET_USER_LIMITS_SUPPORTED
463   png_ptr->user_width_max=PNG_USER_WIDTH_MAX;
464   png_ptr->user_height_max=PNG_USER_HEIGHT_MAX;
465#endif
466
467#ifdef PNG_SETJMP_SUPPORTED
468#ifdef USE_FAR_KEYWORD
469   if (setjmp(jmpbuf))
470#else
471   if (setjmp(png_ptr->jmpbuf))
472#endif
473   {
474      png_free(png_ptr, png_ptr->zbuf);
475      png_ptr->zbuf=NULL;
476      png_destroy_struct(png_ptr);
477      return (NULL);
478   }
479#ifdef USE_FAR_KEYWORD
480   png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf));
481#endif
482#endif
483
484#ifdef PNG_USER_MEM_SUPPORTED
485   png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
486#endif /* PNG_USER_MEM_SUPPORTED */
487   png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
488
489   i=0;
490   do
491   {
492     if(user_png_ver[i] != png_libpng_ver[i])
493        png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
494   } while (png_libpng_ver[i++]);
495
496   if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
497   {
498     /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
499      * we must recompile any applications that use any older library version.
500      * For versions after libpng 1.0, we will be compatible, so we need
501      * only check the first digit.
502      */
503     if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
504         (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
505         (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
506     {
507#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
508        char msg[80];
509        if (user_png_ver)
510        {
511          sprintf(msg, "Application was compiled with png.h from libpng-%.20s",
512             user_png_ver);
513          png_warning(png_ptr, msg);
514        }
515        sprintf(msg, "Application  is  running with png.c from libpng-%.20s",
516           png_libpng_ver);
517        png_warning(png_ptr, msg);
518#endif
519#ifdef PNG_ERROR_NUMBERS_SUPPORTED
520        png_ptr->flags=0;
521#endif
522        png_error(png_ptr,
523           "Incompatible libpng version in application and library");
524     }
525   }
526
527   /* initialize zbuf - compression buffer */
528   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
529   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
530      (png_uint_32)png_ptr->zbuf_size);
531
532   png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL,
533      png_flush_ptr_NULL);
534
535#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
536   png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
537      1, png_doublep_NULL, png_doublep_NULL);
538#endif
539
540#ifdef PNG_SETJMP_SUPPORTED
541/* Applications that neglect to set up their own setjmp() and then encounter
542   a png_error() will longjmp here.  Since the jmpbuf is then meaningless we
543   abort instead of returning. */
544#ifdef USE_FAR_KEYWORD
545   if (setjmp(jmpbuf))
546      PNG_ABORT();
547   png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf));
548#else
549   if (setjmp(png_ptr->jmpbuf))
550      PNG_ABORT();
551#endif
552#endif
553   return (png_ptr);
554}
555
556/* Initialize png_ptr structure, and allocate any memory needed */
557#undef png_write_init
558void PNGAPI
559png_write_init(png_structp png_ptr)
560{
561   /* We only come here via pre-1.0.7-compiled applications */
562   png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0);
563}
564
565void PNGAPI
566png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver,
567   png_size_t png_struct_size, png_size_t png_info_size)
568{
569   /* We only come here via pre-1.0.12-compiled applications */
570#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
571   if(png_sizeof(png_struct) > png_struct_size ||
572      png_sizeof(png_info) > png_info_size)
573   {
574      char msg[80];
575      png_ptr->warning_fn=NULL;
576      if (user_png_ver)
577      {
578        sprintf(msg, "Application was compiled with png.h from libpng-%.20s",
579           user_png_ver);
580        png_warning(png_ptr, msg);
581      }
582      sprintf(msg, "Application  is  running with png.c from libpng-%.20s",
583         png_libpng_ver);
584      png_warning(png_ptr, msg);
585   }
586#endif
587   if(png_sizeof(png_struct) > png_struct_size)
588     {
589       png_ptr->error_fn=NULL;
590#ifdef PNG_ERROR_NUMBERS_SUPPORTED
591       png_ptr->flags=0;
592#endif
593       png_error(png_ptr,
594       "The png struct allocated by the application for writing is too small.");
595     }
596   if(png_sizeof(png_info) > png_info_size)
597     {
598       png_ptr->error_fn=NULL;
599#ifdef PNG_ERROR_NUMBERS_SUPPORTED
600       png_ptr->flags=0;
601#endif
602       png_error(png_ptr,
603       "The info struct allocated by the application for writing is too small.");
604     }
605   png_write_init_3(&png_ptr, user_png_ver, png_struct_size);
606}
607
608
609void PNGAPI
610png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver,
611   png_size_t png_struct_size)
612{
613   png_structp png_ptr=*ptr_ptr;
614#ifdef PNG_SETJMP_SUPPORTED
615   jmp_buf tmp_jmp; /* to save current jump buffer */
616#endif
617   int i = 0;
618   do
619   {
620     if (user_png_ver[i] != png_libpng_ver[i])
621     {
622#ifdef PNG_LEGACY_SUPPORTED
623       png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
624#else
625       png_ptr->warning_fn=NULL;
626       png_warning(png_ptr,
627     "Application uses deprecated png_write_init() and should be recompiled.");
628       break;
629#endif
630     }
631   } while (png_libpng_ver[i++]);
632
633   png_debug(1, "in png_write_init_3\n");
634
635#ifdef PNG_SETJMP_SUPPORTED
636   /* save jump buffer and error functions */
637   png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf));
638#endif
639
640   if (png_sizeof(png_struct) > png_struct_size)
641     {
642       png_destroy_struct(png_ptr);
643       png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
644       *ptr_ptr = png_ptr;
645     }
646
647   /* reset all variables to 0 */
648   png_memset(png_ptr, 0, png_sizeof (png_struct));
649
650   /* added at libpng-1.2.6 */
651#ifdef PNG_SET_USER_LIMITS_SUPPORTED
652   png_ptr->user_width_max=PNG_USER_WIDTH_MAX;
653   png_ptr->user_height_max=PNG_USER_HEIGHT_MAX;
654#endif
655
656#if !defined(PNG_1_0_X)
657#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
658   png_init_mmx_flags(png_ptr);   /* 1.2.0 addition */
659#endif
660#endif /* PNG_1_0_X */
661
662#ifdef PNG_SETJMP_SUPPORTED
663   /* restore jump buffer */
664   png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf));
665#endif
666
667   png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL,
668      png_flush_ptr_NULL);
669
670   /* initialize zbuf - compression buffer */
671   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
672   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
673      (png_uint_32)png_ptr->zbuf_size);
674
675#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
676   png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
677      1, png_doublep_NULL, png_doublep_NULL);
678#endif
679}
680
681/* Write a few rows of image data.  If the image is interlaced,
682 * either you will have to write the 7 sub images, or, if you
683 * have called png_set_interlace_handling(), you will have to
684 * "write" the image seven times.
685 */
686void PNGAPI
687png_write_rows(png_structp png_ptr, png_bytepp row,
688   png_uint_32 num_rows)
689{
690   png_uint_32 i; /* row counter */
691   png_bytepp rp; /* row pointer */
692
693   png_debug(1, "in png_write_rows\n");
694   /* loop through the rows */
695   for (i = 0, rp = row; i < num_rows; i++, rp++)
696   {
697      png_write_row(png_ptr, *rp);
698   }
699}
700
701/* Write the image.  You only need to call this function once, even
702 * if you are writing an interlaced image.
703 */
704void PNGAPI
705png_write_image(png_structp png_ptr, png_bytepp image)
706{
707   png_uint_32 i; /* row index */
708   int pass, num_pass; /* pass variables */
709   png_bytepp rp; /* points to current row */
710
711   png_debug(1, "in png_write_image\n");
712#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
713   /* intialize interlace handling.  If image is not interlaced,
714      this will set pass to 1 */
715   num_pass = png_set_interlace_handling(png_ptr);
716#else
717   num_pass = 1;
718#endif
719   /* loop through passes */
720   for (pass = 0; pass < num_pass; pass++)
721   {
722      /* loop through image */
723      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
724      {
725         png_write_row(png_ptr, *rp);
726      }
727   }
728}
729
730/* called by user to write a row of image data */
731void PNGAPI
732png_write_row(png_structp png_ptr, png_bytep row)
733{
734   png_debug2(1, "in png_write_row (row %ld, pass %d)\n",
735      png_ptr->row_number, png_ptr->pass);
736   /* initialize transformations and other stuff if first time */
737   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
738   {
739   /* make sure we wrote the header info */
740   if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
741      png_error(png_ptr,
742         "png_write_info was never called before png_write_row.");
743
744   /* check for transforms that have been set but were defined out */
745#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
746   if (png_ptr->transformations & PNG_INVERT_MONO)
747      png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined.");
748#endif
749#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
750   if (png_ptr->transformations & PNG_FILLER)
751      png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined.");
752#endif
753#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED)
754   if (png_ptr->transformations & PNG_PACKSWAP)
755      png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined.");
756#endif
757#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
758   if (png_ptr->transformations & PNG_PACK)
759      png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined.");
760#endif
761#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
762   if (png_ptr->transformations & PNG_SHIFT)
763      png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined.");
764#endif
765#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
766   if (png_ptr->transformations & PNG_BGR)
767      png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined.");
768#endif
769#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
770   if (png_ptr->transformations & PNG_SWAP_BYTES)
771      png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined.");
772#endif
773
774      png_write_start_row(png_ptr);
775   }
776
777#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
778   /* if interlaced and not interested in row, return */
779   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
780   {
781      switch (png_ptr->pass)
782      {
783         case 0:
784            if (png_ptr->row_number & 0x07)
785            {
786               png_write_finish_row(png_ptr);
787               return;
788            }
789            break;
790         case 1:
791            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
792            {
793               png_write_finish_row(png_ptr);
794               return;
795            }
796            break;
797         case 2:
798            if ((png_ptr->row_number & 0x07) != 4)
799            {
800               png_write_finish_row(png_ptr);
801               return;
802            }
803            break;
804         case 3:
805            if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)
806            {
807               png_write_finish_row(png_ptr);
808               return;
809            }
810            break;
811         case 4:
812            if ((png_ptr->row_number & 0x03) != 2)
813            {
814               png_write_finish_row(png_ptr);
815               return;
816            }
817            break;
818         case 5:
819            if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)
820            {
821               png_write_finish_row(png_ptr);
822               return;
823            }
824            break;
825         case 6:
826            if (!(png_ptr->row_number & 0x01))
827            {
828               png_write_finish_row(png_ptr);
829               return;
830            }
831            break;
832      }
833   }
834#endif
835
836   /* set up row info for transformations */
837   png_ptr->row_info.color_type = png_ptr->color_type;
838   png_ptr->row_info.width = png_ptr->usr_width;
839   png_ptr->row_info.channels = png_ptr->usr_channels;
840   png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
841   png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
842      png_ptr->row_info.channels);
843
844   png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
845      png_ptr->row_info.width);
846
847   png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type);
848   png_debug1(3, "row_info->width = %lu\n", png_ptr->row_info.width);
849   png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels);
850   png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth);
851   png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth);
852   png_debug1(3, "row_info->rowbytes = %lu\n", png_ptr->row_info.rowbytes);
853
854   /* Copy user's row into buffer, leaving room for filter byte. */
855   png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row,
856      png_ptr->row_info.rowbytes);
857
858#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
859   /* handle interlacing */
860   if (png_ptr->interlaced && png_ptr->pass < 6 &&
861      (png_ptr->transformations & PNG_INTERLACE))
862   {
863      png_do_write_interlace(&(png_ptr->row_info),
864         png_ptr->row_buf + 1, png_ptr->pass);
865      /* this should always get caught above, but still ... */
866      if (!(png_ptr->row_info.width))
867      {
868         png_write_finish_row(png_ptr);
869         return;
870      }
871   }
872#endif
873
874   /* handle other transformations */
875   if (png_ptr->transformations)
876      png_do_write_transformations(png_ptr);
877
878#if defined(PNG_MNG_FEATURES_SUPPORTED)
879   /* Write filter_method 64 (intrapixel differencing) only if
880    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
881    * 2. Libpng did not write a PNG signature (this filter_method is only
882    *    used in PNG datastreams that are embedded in MNG datastreams) and
883    * 3. The application called png_permit_mng_features with a mask that
884    *    included PNG_FLAG_MNG_FILTER_64 and
885    * 4. The filter_method is 64 and
886    * 5. The color_type is RGB or RGBA
887    */
888   if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
889      (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
890   {
891      /* Intrapixel differencing */
892      png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1);
893   }
894#endif
895
896   /* Find a filter if necessary, filter the row and write it out. */
897   png_write_find_filter(png_ptr, &(png_ptr->row_info));
898
899   if (png_ptr->write_row_fn != NULL)
900      (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
901}
902
903#if defined(PNG_WRITE_FLUSH_SUPPORTED)
904/* Set the automatic flush interval or 0 to turn flushing off */
905void PNGAPI
906png_set_flush(png_structp png_ptr, int nrows)
907{
908   png_debug(1, "in png_set_flush\n");
909   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
910}
911
912/* flush the current output buffers now */
913void PNGAPI
914png_write_flush(png_structp png_ptr)
915{
916   int wrote_IDAT;
917
918   png_debug(1, "in png_write_flush\n");
919   /* We have already written out all of the data */
920   if (png_ptr->row_number >= png_ptr->num_rows)
921     return;
922
923   do
924   {
925      int ret;
926
927      /* compress the data */
928      ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
929      wrote_IDAT = 0;
930
931      /* check for compression errors */
932      if (ret != Z_OK)
933      {
934         if (png_ptr->zstream.msg != NULL)
935            png_error(png_ptr, png_ptr->zstream.msg);
936         else
937            png_error(png_ptr, "zlib error");
938      }
939
940      if (!(png_ptr->zstream.avail_out))
941      {
942         /* write the IDAT and reset the zlib output buffer */
943         png_write_IDAT(png_ptr, png_ptr->zbuf,
944                        png_ptr->zbuf_size);
945         png_ptr->zstream.next_out = png_ptr->zbuf;
946         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
947         wrote_IDAT = 1;
948      }
949   } while(wrote_IDAT == 1);
950
951   /* If there is any data left to be output, write it into a new IDAT */
952   if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
953   {
954      /* write the IDAT and reset the zlib output buffer */
955      png_write_IDAT(png_ptr, png_ptr->zbuf,
956                     png_ptr->zbuf_size - png_ptr->zstream.avail_out);
957      png_ptr->zstream.next_out = png_ptr->zbuf;
958      png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
959   }
960   png_ptr->flush_rows = 0;
961   png_flush(png_ptr);
962}
963#endif /* PNG_WRITE_FLUSH_SUPPORTED */
964
965/* free all memory used by the write */
966void PNGAPI
967png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
968{
969   png_structp png_ptr = NULL;
970   png_infop info_ptr = NULL;
971#ifdef PNG_USER_MEM_SUPPORTED
972   png_free_ptr free_fn = NULL;
973   png_voidp mem_ptr = NULL;
974#endif
975
976   png_debug(1, "in png_destroy_write_struct\n");
977   if (png_ptr_ptr != NULL)
978   {
979      png_ptr = *png_ptr_ptr;
980#ifdef PNG_USER_MEM_SUPPORTED
981      free_fn = png_ptr->free_fn;
982      mem_ptr = png_ptr->mem_ptr;
983#endif
984   }
985
986   if (info_ptr_ptr != NULL)
987      info_ptr = *info_ptr_ptr;
988
989   if (info_ptr != NULL)
990   {
991      png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
992
993#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
994      if (png_ptr->num_chunk_list)
995      {
996         png_free(png_ptr, png_ptr->chunk_list);
997         png_ptr->chunk_list=NULL;
998         png_ptr->num_chunk_list=0;
999      }
1000#endif
1001
1002#ifdef PNG_USER_MEM_SUPPORTED
1003      png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn,
1004         (png_voidp)mem_ptr);
1005#else
1006      png_destroy_struct((png_voidp)info_ptr);
1007#endif
1008      *info_ptr_ptr = NULL;
1009   }
1010
1011   if (png_ptr != NULL)
1012   {
1013      png_write_destroy(png_ptr);
1014#ifdef PNG_USER_MEM_SUPPORTED
1015      png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn,
1016         (png_voidp)mem_ptr);
1017#else
1018      png_destroy_struct((png_voidp)png_ptr);
1019#endif
1020      *png_ptr_ptr = NULL;
1021   }
1022}
1023
1024
1025/* Free any memory used in png_ptr struct (old method) */
1026void /* PRIVATE */
1027png_write_destroy(png_structp png_ptr)
1028{
1029#ifdef PNG_SETJMP_SUPPORTED
1030   jmp_buf tmp_jmp; /* save jump buffer */
1031#endif
1032   png_error_ptr error_fn;
1033   png_error_ptr warning_fn;
1034   png_voidp error_ptr;
1035#ifdef PNG_USER_MEM_SUPPORTED
1036   png_free_ptr free_fn;
1037#endif
1038
1039   png_debug(1, "in png_write_destroy\n");
1040   /* free any memory zlib uses */
1041   deflateEnd(&png_ptr->zstream);
1042
1043   /* free our memory.  png_free checks NULL for us. */
1044   png_free(png_ptr, png_ptr->zbuf);
1045   png_free(png_ptr, png_ptr->row_buf);
1046   png_free(png_ptr, png_ptr->prev_row);
1047   png_free(png_ptr, png_ptr->sub_row);
1048   png_free(png_ptr, png_ptr->up_row);
1049   png_free(png_ptr, png_ptr->avg_row);
1050   png_free(png_ptr, png_ptr->paeth_row);
1051
1052#if defined(PNG_TIME_RFC1123_SUPPORTED)
1053   png_free(png_ptr, png_ptr->time_buffer);
1054#endif
1055
1056#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
1057   png_free(png_ptr, png_ptr->prev_filters);
1058   png_free(png_ptr, png_ptr->filter_weights);
1059   png_free(png_ptr, png_ptr->inv_filter_weights);
1060   png_free(png_ptr, png_ptr->filter_costs);
1061   png_free(png_ptr, png_ptr->inv_filter_costs);
1062#endif
1063
1064#ifdef PNG_SETJMP_SUPPORTED
1065   /* reset structure */
1066   png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf));
1067#endif
1068
1069   error_fn = png_ptr->error_fn;
1070   warning_fn = png_ptr->warning_fn;
1071   error_ptr = png_ptr->error_ptr;
1072#ifdef PNG_USER_MEM_SUPPORTED
1073   free_fn = png_ptr->free_fn;
1074#endif
1075
1076   png_memset(png_ptr, 0, png_sizeof (png_struct));
1077
1078   png_ptr->error_fn = error_fn;
1079   png_ptr->warning_fn = warning_fn;
1080   png_ptr->error_ptr = error_ptr;
1081#ifdef PNG_USER_MEM_SUPPORTED
1082   png_ptr->free_fn = free_fn;
1083#endif
1084
1085#ifdef PNG_SETJMP_SUPPORTED
1086   png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf));
1087#endif
1088}
1089
1090/* Allow the application to select one or more row filters to use. */
1091void PNGAPI
1092png_set_filter(png_structp png_ptr, int method, int filters)
1093{
1094   png_debug(1, "in png_set_filter\n");
1095#if defined(PNG_MNG_FEATURES_SUPPORTED)
1096   if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
1097      (method == PNG_INTRAPIXEL_DIFFERENCING))
1098         method = PNG_FILTER_TYPE_BASE;
1099#endif
1100   if (method == PNG_FILTER_TYPE_BASE)
1101   {
1102      switch (filters & (PNG_ALL_FILTERS | 0x07))
1103      {
1104         case 5:
1105         case 6:
1106         case 7: png_warning(png_ptr, "Unknown row filter for method 0");
1107         case PNG_FILTER_VALUE_NONE:  png_ptr->do_filter=PNG_FILTER_NONE; break;
1108         case PNG_FILTER_VALUE_SUB:   png_ptr->do_filter=PNG_FILTER_SUB;  break;
1109         case PNG_FILTER_VALUE_UP:    png_ptr->do_filter=PNG_FILTER_UP;   break;
1110         case PNG_FILTER_VALUE_AVG:   png_ptr->do_filter=PNG_FILTER_AVG;  break;
1111         case PNG_FILTER_VALUE_PAETH: png_ptr->do_filter=PNG_FILTER_PAETH;break;
1112         default: png_ptr->do_filter = (png_byte)filters; break;
1113      }
1114
1115      /* If we have allocated the row_buf, this means we have already started
1116       * with the image and we should have allocated all of the filter buffers
1117       * that have been selected.  If prev_row isn't already allocated, then
1118       * it is too late to start using the filters that need it, since we
1119       * will be missing the data in the previous row.  If an application
1120       * wants to start and stop using particular filters during compression,
1121       * it should start out with all of the filters, and then add and
1122       * remove them after the start of compression.
1123       */
1124      if (png_ptr->row_buf != NULL)
1125      {
1126         if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)
1127         {
1128            png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
1129              (png_ptr->rowbytes + 1));
1130            png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
1131         }
1132
1133         if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)
1134         {
1135            if (png_ptr->prev_row == NULL)
1136            {
1137               png_warning(png_ptr, "Can't add Up filter after starting");
1138               png_ptr->do_filter &= ~PNG_FILTER_UP;
1139            }
1140            else
1141            {
1142               png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
1143                  (png_ptr->rowbytes + 1));
1144               png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
1145            }
1146         }
1147
1148         if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)
1149         {
1150            if (png_ptr->prev_row == NULL)
1151            {
1152               png_warning(png_ptr, "Can't add Average filter after starting");
1153               png_ptr->do_filter &= ~PNG_FILTER_AVG;
1154            }
1155            else
1156            {
1157               png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
1158                  (png_ptr->rowbytes + 1));
1159               png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
1160            }
1161         }
1162
1163         if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&
1164             png_ptr->paeth_row == NULL)
1165         {
1166            if (png_ptr->prev_row == NULL)
1167            {
1168               png_warning(png_ptr, "Can't add Paeth filter after starting");
1169               png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
1170            }
1171            else
1172            {
1173               png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
1174                  (png_ptr->rowbytes + 1));
1175               png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
1176            }
1177         }
1178
1179         if (png_ptr->do_filter == PNG_NO_FILTERS)
1180            png_ptr->do_filter = PNG_FILTER_NONE;
1181      }
1182   }
1183   else
1184      png_error(png_ptr, "Unknown custom filter method");
1185}
1186
1187/* This allows us to influence the way in which libpng chooses the "best"
1188 * filter for the current scanline.  While the "minimum-sum-of-absolute-
1189 * differences metric is relatively fast and effective, there is some
1190 * question as to whether it can be improved upon by trying to keep the
1191 * filtered data going to zlib more consistent, hopefully resulting in
1192 * better compression.
1193 */
1194#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)      /* GRR 970116 */
1195void PNGAPI
1196png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
1197   int num_weights, png_doublep filter_weights,
1198   png_doublep filter_costs)
1199{
1200   int i;
1201
1202   png_debug(1, "in png_set_filter_heuristics\n");
1203   if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST)
1204   {
1205      png_warning(png_ptr, "Unknown filter heuristic method");
1206      return;
1207   }
1208
1209   if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT)
1210   {
1211      heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
1212   }
1213
1214   if (num_weights < 0 || filter_weights == NULL ||
1215      heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
1216   {
1217      num_weights = 0;
1218   }
1219
1220   png_ptr->num_prev_filters = (png_byte)num_weights;
1221   png_ptr->heuristic_method = (png_byte)heuristic_method;
1222
1223   if (num_weights > 0)
1224   {
1225      if (png_ptr->prev_filters == NULL)
1226      {
1227         png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
1228            (png_uint_32)(png_sizeof(png_byte) * num_weights));
1229
1230         /* To make sure that the weighting starts out fairly */
1231         for (i = 0; i < num_weights; i++)
1232         {
1233            png_ptr->prev_filters[i] = 255;
1234         }
1235      }
1236
1237      if (png_ptr->filter_weights == NULL)
1238      {
1239         png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
1240            (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
1241
1242         png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
1243            (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
1244         for (i = 0; i < num_weights; i++)
1245         {
1246            png_ptr->inv_filter_weights[i] =
1247            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
1248         }
1249      }
1250
1251      for (i = 0; i < num_weights; i++)
1252      {
1253         if (filter_weights[i] < 0.0)
1254         {
1255            png_ptr->inv_filter_weights[i] =
1256            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
1257         }
1258         else
1259         {
1260            png_ptr->inv_filter_weights[i] =
1261               (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5);
1262            png_ptr->filter_weights[i] =
1263               (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5);
1264         }
1265      }
1266   }
1267
1268   /* If, in the future, there are other filter methods, this would
1269    * need to be based on png_ptr->filter.
1270    */
1271   if (png_ptr->filter_costs == NULL)
1272   {
1273      png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
1274         (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
1275
1276      png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
1277         (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
1278
1279      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
1280      {
1281         png_ptr->inv_filter_costs[i] =
1282         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
1283      }
1284   }
1285
1286   /* Here is where we set the relative costs of the different filters.  We
1287    * should take the desired compression level into account when setting
1288    * the costs, so that Paeth, for instance, has a high relative cost at low
1289    * compression levels, while it has a lower relative cost at higher
1290    * compression settings.  The filter types are in order of increasing
1291    * relative cost, so it would be possible to do this with an algorithm.
1292    */
1293   for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
1294   {
1295      if (filter_costs == NULL || filter_costs[i] < 0.0)
1296      {
1297         png_ptr->inv_filter_costs[i] =
1298         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
1299      }
1300      else if (filter_costs[i] >= 1.0)
1301      {
1302         png_ptr->inv_filter_costs[i] =
1303            (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5);
1304         png_ptr->filter_costs[i] =
1305            (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5);
1306      }
1307   }
1308}
1309#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
1310
1311void PNGAPI
1312png_set_compression_level(png_structp png_ptr, int level)
1313{
1314   png_debug(1, "in png_set_compression_level\n");
1315   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
1316   png_ptr->zlib_level = level;
1317}
1318
1319void PNGAPI
1320png_set_compression_mem_level(png_structp png_ptr, int mem_level)
1321{
1322   png_debug(1, "in png_set_compression_mem_level\n");
1323   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
1324   png_ptr->zlib_mem_level = mem_level;
1325}
1326
1327void PNGAPI
1328png_set_compression_strategy(png_structp png_ptr, int strategy)
1329{
1330   png_debug(1, "in png_set_compression_strategy\n");
1331   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
1332   png_ptr->zlib_strategy = strategy;
1333}
1334
1335void PNGAPI
1336png_set_compression_window_bits(png_structp png_ptr, int window_bits)
1337{
1338   if (window_bits > 15)
1339      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
1340   else if (window_bits < 8)
1341      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
1342#ifndef WBITS_8_OK
1343   /* avoid libpng bug with 256-byte windows */
1344   if (window_bits == 8)
1345     {
1346       png_warning(png_ptr, "Compression window is being reset to 512");
1347       window_bits=9;
1348     }
1349#endif
1350   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
1351   png_ptr->zlib_window_bits = window_bits;
1352}
1353
1354void PNGAPI
1355png_set_compression_method(png_structp png_ptr, int method)
1356{
1357   png_debug(1, "in png_set_compression_method\n");
1358   if (method != 8)
1359      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
1360   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
1361   png_ptr->zlib_method = method;
1362}
1363
1364void PNGAPI
1365png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
1366{
1367   png_ptr->write_row_fn = write_row_fn;
1368}
1369
1370#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
1371void PNGAPI
1372png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
1373   write_user_transform_fn)
1374{
1375   png_debug(1, "in png_set_write_user_transform_fn\n");
1376   png_ptr->transformations |= PNG_USER_TRANSFORM;
1377   png_ptr->write_user_transform_fn = write_user_transform_fn;
1378}
1379#endif
1380
1381
1382#if defined(PNG_INFO_IMAGE_SUPPORTED)
1383void PNGAPI
1384png_write_png(png_structp png_ptr, png_infop info_ptr,
1385              int transforms, voidp params)
1386{
1387#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
1388   /* invert the alpha channel from opacity to transparency */
1389   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
1390       png_set_invert_alpha(png_ptr);
1391#endif
1392
1393   /* Write the file header information. */
1394   png_write_info(png_ptr, info_ptr);
1395
1396   /* ------ these transformations don't touch the info structure ------- */
1397
1398#if defined(PNG_WRITE_INVERT_SUPPORTED)
1399   /* invert monochrome pixels */
1400   if (transforms & PNG_TRANSFORM_INVERT_MONO)
1401       png_set_invert_mono(png_ptr);
1402#endif
1403
1404#if defined(PNG_WRITE_SHIFT_SUPPORTED)
1405   /* Shift the pixels up to a legal bit depth and fill in
1406    * as appropriate to correctly scale the image.
1407    */
1408   if ((transforms & PNG_TRANSFORM_SHIFT)
1409               && (info_ptr->valid & PNG_INFO_sBIT))
1410       png_set_shift(png_ptr, &info_ptr->sig_bit);
1411#endif
1412
1413#if defined(PNG_WRITE_PACK_SUPPORTED)
1414   /* pack pixels into bytes */
1415   if (transforms & PNG_TRANSFORM_PACKING)
1416       png_set_packing(png_ptr);
1417#endif
1418
1419#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
1420   /* swap location of alpha bytes from ARGB to RGBA */
1421   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
1422       png_set_swap_alpha(png_ptr);
1423#endif
1424
1425#if defined(PNG_WRITE_FILLER_SUPPORTED)
1426   /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into
1427    * RGB (4 channels -> 3 channels). The second parameter is not used.
1428    */
1429   if (transforms & PNG_TRANSFORM_STRIP_FILLER)
1430       png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
1431#endif
1432
1433#if defined(PNG_WRITE_BGR_SUPPORTED)
1434   /* flip BGR pixels to RGB */
1435   if (transforms & PNG_TRANSFORM_BGR)
1436       png_set_bgr(png_ptr);
1437#endif
1438
1439#if defined(PNG_WRITE_SWAP_SUPPORTED)
1440   /* swap bytes of 16-bit files to most significant byte first */
1441   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
1442       png_set_swap(png_ptr);
1443#endif
1444
1445#if defined(PNG_WRITE_PACKSWAP_SUPPORTED)
1446   /* swap bits of 1, 2, 4 bit packed pixel formats */
1447   if (transforms & PNG_TRANSFORM_PACKSWAP)
1448       png_set_packswap(png_ptr);
1449#endif
1450
1451   /* ----------------------- end of transformations ------------------- */
1452
1453   /* write the bits */
1454   if (info_ptr->valid & PNG_INFO_IDAT)
1455       png_write_image(png_ptr, info_ptr->row_pointers);
1456
1457   /* It is REQUIRED to call this to finish writing the rest of the file */
1458   png_write_end(png_ptr, info_ptr);
1459
1460   if(transforms == 0 || params == NULL)
1461      /* quiet compiler warnings */ return;
1462}
1463#endif
1464#endif /* PNG_WRITE_SUPPORTED */
1465