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