blob: 5afad37ea8eb4186c8a4b5d33504a8c1166c2d73 [file] [log] [blame]
Glenn Randers-Pehrson5c6aeb21998-12-29 11:47:59 -06001
Andreas Dilger47a0c421997-05-16 02:46:07 -05002/* pngwrite.c - general routines to write a PNG file
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -06003 *
Glenn Randers-Pehrsonf8b008c1999-09-18 10:54:36 -05004 * libpng 1.0.4 - September 18, 1999
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -06005 * For conditions of distribution and use, see copyright notice in png.h
6 * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
7 * Copyright (c) 1996, 1997 Andreas Dilger
Glenn Randers-Pehrsonc9442291999-01-06 21:50:16 -06008 * Copyright (c) 1998, 1999 Glenn Randers-Pehrson
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -06009 */
Guy Schalnat0d580581995-07-20 02:43:20 -050010
11/* get internal access to png.h */
12#define PNG_INTERNAL
13#include "png.h"
14
Andreas Dilger47a0c421997-05-16 02:46:07 -050015/* 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
Glenn Randers-Pehrson345bc271998-06-14 14:43:31 -050018 * after the image data, put it in png_write_end(). I strongly encourage
Andreas Dilger47a0c421997-05-16 02:46:07 -050019 * 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 */
Guy Schalnat0d580581995-07-20 02:43:20 -050024void
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060025png_write_info(png_structp png_ptr, png_infop info_ptr)
Guy Schalnat0d580581995-07-20 02:43:20 -050026{
Glenn Randers-Pehrson70e3f541998-01-03 22:40:55 -060027#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
Andreas Dilger47a0c421997-05-16 02:46:07 -050028 int i;
Glenn Randers-Pehrson70e3f541998-01-03 22:40:55 -060029#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -050030
31 png_debug(1, "in png_write_info\n");
Guy Schalnat0d580581995-07-20 02:43:20 -050032 png_write_sig(png_ptr); /* write PNG signature */
33 /* write IHDR information. */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060034 png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
35 info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -060036 info_ptr->filter_type,
37#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
38 info_ptr->interlace_type);
39#else
40 0);
41#endif
Guy Schalnat0d580581995-07-20 02:43:20 -050042 /* the rest of these check to see if the valid field has the appropriate
43 flag set, and if it does, writes the chunk. */
Guy Schalnat51f0eb41995-09-26 05:22:39 -050044#if defined(PNG_WRITE_gAMA_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060045 if (info_ptr->valid & PNG_INFO_gAMA)
46 png_write_gAMA(png_ptr, info_ptr->gamma);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050047#endif
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -060048#if defined(PNG_WRITE_sRGB_SUPPORTED)
49 if (info_ptr->valid & PNG_INFO_sRGB)
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -060050 png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent);
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -060051#endif
Guy Schalnat51f0eb41995-09-26 05:22:39 -050052#if defined(PNG_WRITE_sBIT_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060053 if (info_ptr->valid & PNG_INFO_sBIT)
54 png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050055#endif
56#if defined(PNG_WRITE_cHRM_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060057 if (info_ptr->valid & PNG_INFO_cHRM)
Guy Schalnat0d580581995-07-20 02:43:20 -050058 png_write_cHRM(png_ptr,
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060059 info_ptr->x_white, info_ptr->y_white,
60 info_ptr->x_red, info_ptr->y_red,
61 info_ptr->x_green, info_ptr->y_green,
62 info_ptr->x_blue, info_ptr->y_blue);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050063#endif
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060064 if (info_ptr->valid & PNG_INFO_PLTE)
Andreas Dilger47a0c421997-05-16 02:46:07 -050065 png_write_PLTE(png_ptr, info_ptr->palette,
66 (png_uint_32)info_ptr->num_palette);
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060067 else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
Guy Schalnate5a37791996-06-05 15:50:50 -050068 png_error(png_ptr, "Valid palette required for paletted images\n");
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -060069
Guy Schalnat51f0eb41995-09-26 05:22:39 -050070#if defined(PNG_WRITE_tRNS_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060071 if (info_ptr->valid & PNG_INFO_tRNS)
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -060072 {
73#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
74 /* invert the alpha channel (in tRNS) */
75 if (png_ptr->transformations & PNG_INVERT_ALPHA &&
76 info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
77 {
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -060078 int j;
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -060079 for (j=0; j<(int)info_ptr->num_trans; j++)
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -060080 info_ptr->trans[j] = 255 - info_ptr->trans[j];
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -060081 }
82#endif
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060083 png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values),
84 info_ptr->num_trans, info_ptr->color_type);
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -060085 }
Guy Schalnat51f0eb41995-09-26 05:22:39 -050086#endif
87#if defined(PNG_WRITE_bKGD_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060088 if (info_ptr->valid & PNG_INFO_bKGD)
89 png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050090#endif
91#if defined(PNG_WRITE_hIST_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060092 if (info_ptr->valid & PNG_INFO_hIST)
93 png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050094#endif
Guy Schalnat51f0eb41995-09-26 05:22:39 -050095#if defined(PNG_WRITE_oFFs_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060096 if (info_ptr->valid & PNG_INFO_oFFs)
97 png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
98 info_ptr->offset_unit_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050099#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -0500100#if defined(PNG_WRITE_pCAL_SUPPORTED)
101 if (info_ptr->valid & PNG_INFO_pCAL)
102 png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
103 info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
104 info_ptr->pcal_units, info_ptr->pcal_params);
105#endif
106#if defined(PNG_WRITE_pHYs_SUPPORTED)
107 if (info_ptr->valid & PNG_INFO_pHYs)
108 png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
109 info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
110#endif
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500111#if defined(PNG_WRITE_tIME_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600112 if (info_ptr->valid & PNG_INFO_tIME)
Guy Schalnate5a37791996-06-05 15:50:50 -0500113 {
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600114 png_write_tIME(png_ptr, &(info_ptr->mod_time));
Guy Schalnate5a37791996-06-05 15:50:50 -0500115 png_ptr->flags |= PNG_FLAG_WROTE_tIME;
116 }
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500117#endif
118#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
Guy Schalnate5a37791996-06-05 15:50:50 -0500119 /* Check to see if we need to write text chunks */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500120 for (i = 0; i < info_ptr->num_text; i++)
Guy Schalnat0d580581995-07-20 02:43:20 -0500121 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500122 png_debug2(2, "Writing header text chunk %d, type %d\n", i,
123 info_ptr->text[i].compression);
124 /* If we want a compressed text chunk */
125 if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
126 {
127#if defined(PNG_WRITE_zTXt_SUPPORTED)
128 /* write compressed chunk */
129 png_write_zTXt(png_ptr, info_ptr->text[i].key,
130 info_ptr->text[i].text, info_ptr->text[i].text_length,
131 info_ptr->text[i].compression);
132#else
133 png_warning(png_ptr, "Unable to write compressed text\n");
134#endif
135 /* Mark this chunk as written */
136 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
137 }
138 else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
139 {
140#if defined(PNG_WRITE_tEXt_SUPPORTED)
141 /* write uncompressed chunk */
142 png_write_tEXt(png_ptr, info_ptr->text[i].key,
143 info_ptr->text[i].text, info_ptr->text[i].text_length);
144#else
145 png_warning(png_ptr, "Unable to write uncompressed text\n");
146#endif
147 /* Mark this chunk as written */
148 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
149 }
150 }
151#endif
152}
Guy Schalnat0d580581995-07-20 02:43:20 -0500153
Andreas Dilger47a0c421997-05-16 02:46:07 -0500154/* Writes the end of the PNG file. If you don't want to write comments or
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600155 * time information, you can pass NULL for info. If you already wrote these
156 * in png_write_info(), do not write them again here. If you have long
157 * comments, I suggest writing them here, and compressing them.
158 */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500159void
160png_write_end(png_structp png_ptr, png_infop info_ptr)
161{
162 png_debug(1, "in png_write_end\n");
163 if (!(png_ptr->mode & PNG_HAVE_IDAT))
164 png_error(png_ptr, "No IDATs written into file");
165
166 /* see if user wants us to write information chunks */
167 if (info_ptr != NULL)
168 {
Glenn Randers-Pehrson70e3f541998-01-03 22:40:55 -0600169#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
Andreas Dilger47a0c421997-05-16 02:46:07 -0500170 int i; /* local index variable */
Glenn Randers-Pehrson70e3f541998-01-03 22:40:55 -0600171#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -0500172#if defined(PNG_WRITE_tIME_SUPPORTED)
173 /* check to see if user has supplied a time chunk */
174 if (info_ptr->valid & PNG_INFO_tIME &&
175 !(png_ptr->flags & PNG_FLAG_WROTE_tIME))
176 png_write_tIME(png_ptr, &(info_ptr->mod_time));
177#endif
178#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
179 /* loop through comment chunks */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600180 for (i = 0; i < info_ptr->num_text; i++)
Guy Schalnat0d580581995-07-20 02:43:20 -0500181 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500182 png_debug2(2, "Writing trailer text chunk %d, type %d\n", i,
183 info_ptr->text[i].compression);
184 if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
Guy Schalnat0d580581995-07-20 02:43:20 -0500185 {
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500186#if defined(PNG_WRITE_zTXt_SUPPORTED)
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600187 /* write compressed chunk */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600188 png_write_zTXt(png_ptr, info_ptr->text[i].key,
189 info_ptr->text[i].text, info_ptr->text[i].text_length,
190 info_ptr->text[i].compression);
Guy Schalnate5a37791996-06-05 15:50:50 -0500191#else
192 png_warning(png_ptr, "Unable to write compressed text\n");
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500193#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -0500194 /* Mark this chunk as written */
195 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
Guy Schalnat0d580581995-07-20 02:43:20 -0500196 }
Andreas Dilger47a0c421997-05-16 02:46:07 -0500197 else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
Guy Schalnat0d580581995-07-20 02:43:20 -0500198 {
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500199#if defined(PNG_WRITE_tEXt_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500200 /* write uncompressed chunk */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600201 png_write_tEXt(png_ptr, info_ptr->text[i].key,
202 info_ptr->text[i].text, info_ptr->text[i].text_length);
Guy Schalnate5a37791996-06-05 15:50:50 -0500203#else
204 png_warning(png_ptr, "Unable to write uncompressed text\n");
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500205#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500206
Andreas Dilger47a0c421997-05-16 02:46:07 -0500207 /* Mark this chunk as written */
208 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
Guy Schalnat0d580581995-07-20 02:43:20 -0500209 }
210 }
Guy Schalnat6d764711995-12-19 03:22:19 -0600211#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500212 }
Guy Schalnate5a37791996-06-05 15:50:50 -0500213
214 png_ptr->mode |= PNG_AFTER_IDAT;
215
Andreas Dilger47a0c421997-05-16 02:46:07 -0500216 /* write end of PNG file */
Guy Schalnat0d580581995-07-20 02:43:20 -0500217 png_write_IEND(png_ptr);
218}
219
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500220#if defined(PNG_WRITE_tIME_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500221void
Guy Schalnat6d764711995-12-19 03:22:19 -0600222png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime)
Guy Schalnat0d580581995-07-20 02:43:20 -0500223{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500224 png_debug(1, "in png_convert_from_struct_tm\n");
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600225 ptime->year = (png_uint_16)(1900 + ttime->tm_year);
226 ptime->month = (png_byte)(ttime->tm_mon + 1);
227 ptime->day = (png_byte)ttime->tm_mday;
228 ptime->hour = (png_byte)ttime->tm_hour;
229 ptime->minute = (png_byte)ttime->tm_min;
230 ptime->second = (png_byte)ttime->tm_sec;
Guy Schalnat0d580581995-07-20 02:43:20 -0500231}
232
233void
Guy Schalnat6d764711995-12-19 03:22:19 -0600234png_convert_from_time_t(png_timep ptime, time_t ttime)
Guy Schalnat0d580581995-07-20 02:43:20 -0500235{
236 struct tm *tbuf;
237
Andreas Dilger47a0c421997-05-16 02:46:07 -0500238 png_debug(1, "in png_convert_from_time_t\n");
Guy Schalnat0d580581995-07-20 02:43:20 -0500239 tbuf = gmtime(&ttime);
240 png_convert_from_struct_tm(ptime, tbuf);
241}
Guy Schalnat6d764711995-12-19 03:22:19 -0600242#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500243
Andreas Dilger47a0c421997-05-16 02:46:07 -0500244/* Initialize png_ptr structure, and allocate any memory needed */
Guy Schalnate5a37791996-06-05 15:50:50 -0500245png_structp
Glenn Randers-Pehrsond0dce401998-05-09 10:02:29 -0500246png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr,
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600247 png_error_ptr error_fn, png_error_ptr warn_fn)
Guy Schalnat0d580581995-07-20 02:43:20 -0500248{
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500249#ifdef PNG_USER_MEM_SUPPORTED
250 return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
251 warn_fn, NULL, NULL, NULL));
252}
253
254/* Alternate initialize png_ptr structure, and allocate any memory needed */
255png_structp
256png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr,
257 png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
258 png_malloc_ptr malloc_fn, png_free_ptr free_fn)
259{
260#endif /* PNG_USER_MEM_SUPPORTED */
Guy Schalnate5a37791996-06-05 15:50:50 -0500261 png_structp png_ptr;
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600262#ifdef USE_FAR_KEYWORD
263 jmp_buf jmpbuf;
264#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -0500265 png_debug(1, "in png_create_write_struct\n");
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500266#ifdef PNG_USER_MEM_SUPPORTED
267 if ((png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
268 (png_malloc_ptr)malloc_fn)) == NULL)
269#else
Guy Schalnate5a37791996-06-05 15:50:50 -0500270 if ((png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG)) == NULL)
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500271#endif /* PNG_USER_MEM_SUPPORTED */
Guy Schalnate5a37791996-06-05 15:50:50 -0500272 {
Glenn Randers-Pehrsonb2120021998-01-31 20:07:59 -0600273 return ((png_structp)NULL);
Guy Schalnate5a37791996-06-05 15:50:50 -0500274 }
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600275#ifdef USE_FAR_KEYWORD
276 if (setjmp(jmpbuf))
277#else
Guy Schalnate5a37791996-06-05 15:50:50 -0500278 if (setjmp(png_ptr->jmpbuf))
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600279#endif
Guy Schalnate5a37791996-06-05 15:50:50 -0500280 {
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600281 png_free(png_ptr, png_ptr->zbuf);
Guy Schalnate5a37791996-06-05 15:50:50 -0500282 png_destroy_struct(png_ptr);
Glenn Randers-Pehrsonb2120021998-01-31 20:07:59 -0600283 return ((png_structp)NULL);
Guy Schalnate5a37791996-06-05 15:50:50 -0500284 }
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600285#ifdef USE_FAR_KEYWORD
286 png_memcpy(png_ptr->jmpbuf,jmpbuf,sizeof(jmp_buf));
287#endif
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500288#ifdef PNG_USER_MEM_SUPPORTED
289 png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
290#endif /* PNG_USER_MEM_SUPPORTED */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600291 png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
Guy Schalnate5a37791996-06-05 15:50:50 -0500292
Andreas Dilger47a0c421997-05-16 02:46:07 -0500293 /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
294 * we must recompile any applications that use any older library version.
295 * For versions after libpng 1.0, we will be compatible, so we need
296 * only check the first digit.
297 */
298 if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
299 (png_libpng_ver[0] == '0' && user_png_ver[2] < '9'))
Guy Schalnate5a37791996-06-05 15:50:50 -0500300 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500301 png_error(png_ptr,
302 "Incompatible libpng version in application and library");
Guy Schalnate5a37791996-06-05 15:50:50 -0500303 }
Guy Schalnat0d580581995-07-20 02:43:20 -0500304
305 /* initialize zbuf - compression buffer */
306 png_ptr->zbuf_size = PNG_ZBUF_SIZE;
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600307 png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
308 (png_uint_32)png_ptr->zbuf_size);
Guy Schalnate5a37791996-06-05 15:50:50 -0500309
310 png_set_write_fn(png_ptr, NULL, NULL, NULL);
311
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600312#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
313 png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
314 1, NULL, NULL);
315#endif
316
Glenn Randers-Pehrsonb2120021998-01-31 20:07:59 -0600317 return ((png_structp)png_ptr);
Guy Schalnate5a37791996-06-05 15:50:50 -0500318}
319
Andreas Dilger47a0c421997-05-16 02:46:07 -0500320/* Initialize png_ptr structure, and allocate any memory needed */
Guy Schalnate5a37791996-06-05 15:50:50 -0500321void
322png_write_init(png_structp png_ptr)
323{
324 jmp_buf tmp_jmp; /* to save current jump buffer */
325
Andreas Dilger47a0c421997-05-16 02:46:07 -0500326 png_debug(1, "in png_write_init\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500327 /* save jump buffer and error functions */
328 png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
329
330 /* reset all variables to 0 */
331 png_memset(png_ptr, 0, sizeof (png_struct));
332
333 /* restore jump buffer */
334 png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
335
336 /* initialize zbuf - compression buffer */
337 png_ptr->zbuf_size = PNG_ZBUF_SIZE;
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600338 png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
339 (png_uint_32)png_ptr->zbuf_size);
Guy Schalnate5a37791996-06-05 15:50:50 -0500340 png_set_write_fn(png_ptr, NULL, NULL, NULL);
Andreas Dilger47a0c421997-05-16 02:46:07 -0500341
342#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
343 png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
344 1, NULL, NULL);
345#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500346}
347
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600348/* Write a few rows of image data. If the image is interlaced,
349 * either you will have to write the 7 sub images, or, if you
350 * have called png_set_interlace_handling(), you will have to
351 * "write" the image seven times.
352 */
Guy Schalnat0d580581995-07-20 02:43:20 -0500353void
Guy Schalnat6d764711995-12-19 03:22:19 -0600354png_write_rows(png_structp png_ptr, png_bytepp row,
Guy Schalnat0d580581995-07-20 02:43:20 -0500355 png_uint_32 num_rows)
356{
357 png_uint_32 i; /* row counter */
Guy Schalnat6d764711995-12-19 03:22:19 -0600358 png_bytepp rp; /* row pointer */
Guy Schalnat0d580581995-07-20 02:43:20 -0500359
Andreas Dilger47a0c421997-05-16 02:46:07 -0500360 png_debug(1, "in png_write_rows\n");
Guy Schalnat0d580581995-07-20 02:43:20 -0500361 /* loop through the rows */
362 for (i = 0, rp = row; i < num_rows; i++, rp++)
363 {
364 png_write_row(png_ptr, *rp);
365 }
366}
367
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600368/* Write the image. You only need to call this function once, even
369 * if you are writing an interlaced image.
370 */
Guy Schalnat0d580581995-07-20 02:43:20 -0500371void
Guy Schalnat6d764711995-12-19 03:22:19 -0600372png_write_image(png_structp png_ptr, png_bytepp image)
Guy Schalnat0d580581995-07-20 02:43:20 -0500373{
374 png_uint_32 i; /* row index */
375 int pass, num_pass; /* pass variables */
Guy Schalnat6d764711995-12-19 03:22:19 -0600376 png_bytepp rp; /* points to current row */
Guy Schalnat0d580581995-07-20 02:43:20 -0500377
Andreas Dilger47a0c421997-05-16 02:46:07 -0500378 png_debug(1, "in png_write_image\n");
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -0600379#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500380 /* intialize interlace handling. If image is not interlaced,
381 this will set pass to 1 */
382 num_pass = png_set_interlace_handling(png_ptr);
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -0600383#else
384 num_pass = 1;
385#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500386 /* loop through passes */
387 for (pass = 0; pass < num_pass; pass++)
388 {
389 /* loop through image */
390 for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
391 {
392 png_write_row(png_ptr, *rp);
393 }
394 }
395}
396
Guy Schalnate5a37791996-06-05 15:50:50 -0500397/* called by user to write a row of image data */
Guy Schalnat0d580581995-07-20 02:43:20 -0500398void
Guy Schalnat6d764711995-12-19 03:22:19 -0600399png_write_row(png_structp png_ptr, png_bytep row)
Guy Schalnat0d580581995-07-20 02:43:20 -0500400{
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600401 png_debug2(1, "in png_write_row (row %ld, pass %d)\n",
402 png_ptr->row_number, png_ptr->pass);
Guy Schalnat0d580581995-07-20 02:43:20 -0500403 /* initialize transformations and other stuff if first time */
Guy Schalnat6d764711995-12-19 03:22:19 -0600404 if (png_ptr->row_number == 0 && png_ptr->pass == 0)
Guy Schalnat0d580581995-07-20 02:43:20 -0500405 {
Glenn Randers-Pehrson1d963611998-05-02 12:52:25 -0500406 /* check for transforms that have been set but were defined out */
407#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
408 if (png_ptr->transformations & PNG_INVERT_MONO)
409 png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined.");
410#endif
411#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
412 if (png_ptr->transformations & PNG_FILLER)
413 png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined.");
414#endif
415#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED)
416 if (png_ptr->transformations & PNG_PACKSWAP)
417 png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined.");
418#endif
419#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
420 if (png_ptr->transformations & PNG_PACK)
421 png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined.");
422#endif
423#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
424 if (png_ptr->transformations & PNG_SHIFT)
425 png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined.");
426#endif
427#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
428 if (png_ptr->transformations & PNG_BGR)
429 png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined.");
430#endif
431#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
432 if (png_ptr->transformations & PNG_SWAP_BYTES)
433 png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined.");
434#endif
435
Guy Schalnat0d580581995-07-20 02:43:20 -0500436 png_write_start_row(png_ptr);
437 }
438
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500439#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500440 /* if interlaced and not interested in row, return */
441 if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
442 {
443 switch (png_ptr->pass)
444 {
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600445 case 0:
Guy Schalnat0d580581995-07-20 02:43:20 -0500446 if (png_ptr->row_number & 7)
447 {
448 png_write_finish_row(png_ptr);
449 return;
450 }
451 break;
452 case 1:
453 if ((png_ptr->row_number & 7) || png_ptr->width < 5)
454 {
455 png_write_finish_row(png_ptr);
456 return;
457 }
458 break;
459 case 2:
460 if ((png_ptr->row_number & 7) != 4)
461 {
462 png_write_finish_row(png_ptr);
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600463 return;
Guy Schalnat0d580581995-07-20 02:43:20 -0500464 }
465 break;
466 case 3:
467 if ((png_ptr->row_number & 3) || png_ptr->width < 3)
468 {
469 png_write_finish_row(png_ptr);
470 return;
471 }
472 break;
473 case 4:
474 if ((png_ptr->row_number & 3) != 2)
475 {
476 png_write_finish_row(png_ptr);
477 return;
478 }
479 break;
480 case 5:
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600481 if ((png_ptr->row_number & 1) || png_ptr->width < 2)
Guy Schalnat0d580581995-07-20 02:43:20 -0500482 {
483 png_write_finish_row(png_ptr);
484 return;
485 }
486 break;
487 case 6:
488 if (!(png_ptr->row_number & 1))
489 {
490 png_write_finish_row(png_ptr);
491 return;
492 }
493 break;
494 }
495 }
Guy Schalnat6d764711995-12-19 03:22:19 -0600496#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500497
498 /* set up row info for transformations */
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600499 png_ptr->row_info.color_type = png_ptr->color_type;
Guy Schalnat0d580581995-07-20 02:43:20 -0500500 png_ptr->row_info.width = png_ptr->usr_width;
501 png_ptr->row_info.channels = png_ptr->usr_channels;
502 png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600503 png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
504 png_ptr->row_info.channels);
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600505
Guy Schalnat0d580581995-07-20 02:43:20 -0500506 png_ptr->row_info.rowbytes = ((png_ptr->row_info.width *
507 (png_uint_32)png_ptr->row_info.pixel_depth + 7) >> 3);
508
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600509 png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type);
510 png_debug1(3, "row_info->width = %d\n", png_ptr->row_info.width);
511 png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels);
512 png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth);
513 png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth);
514 png_debug1(3, "row_info->rowbytes = %d\n", png_ptr->row_info.rowbytes);
Andreas Dilger47a0c421997-05-16 02:46:07 -0500515
516 /* Copy user's row into buffer, leaving room for filter byte. */
Glenn Randers-Pehrsona357b991998-02-08 20:56:40 -0600517 png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row,
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600518 png_ptr->row_info.rowbytes);
Guy Schalnat0d580581995-07-20 02:43:20 -0500519
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500520#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500521 /* handle interlacing */
522 if (png_ptr->interlaced && png_ptr->pass < 6 &&
523 (png_ptr->transformations & PNG_INTERLACE))
524 {
525 png_do_write_interlace(&(png_ptr->row_info),
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600526 png_ptr->row_buf + 1, png_ptr->pass);
Guy Schalnat0d580581995-07-20 02:43:20 -0500527 /* this should always get caught above, but still ... */
528 if (!(png_ptr->row_info.width))
529 {
530 png_write_finish_row(png_ptr);
531 return;
532 }
533 }
Guy Schalnat6d764711995-12-19 03:22:19 -0600534#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500535
536 /* handle other transformations */
537 if (png_ptr->transformations)
538 png_do_write_transformations(png_ptr);
539
Andreas Dilger47a0c421997-05-16 02:46:07 -0500540 /* Find a filter if necessary, filter the row and write it out. */
Guy Schalnate5a37791996-06-05 15:50:50 -0500541 png_write_find_filter(png_ptr, &(png_ptr->row_info));
Glenn Randers-Pehrson08a33431998-03-07 06:06:55 -0600542
543 if (png_ptr->write_row_fn != NULL)
544 (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
Guy Schalnat0d580581995-07-20 02:43:20 -0500545}
546
Guy Schalnat0f716451995-11-28 11:22:13 -0600547#if defined(PNG_WRITE_FLUSH_SUPPORTED)
548/* Set the automatic flush interval or 0 to turn flushing off */
549void
Guy Schalnat4ee97b01996-01-16 01:51:56 -0600550png_set_flush(png_structp png_ptr, int nrows)
Guy Schalnat0f716451995-11-28 11:22:13 -0600551{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500552 png_debug(1, "in png_set_flush\n");
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600553 png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
Guy Schalnat0f716451995-11-28 11:22:13 -0600554}
555
556/* flush the current output buffers now */
557void
Guy Schalnat4ee97b01996-01-16 01:51:56 -0600558png_write_flush(png_structp png_ptr)
Guy Schalnat0f716451995-11-28 11:22:13 -0600559{
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600560 int wrote_IDAT;
Guy Schalnat0f716451995-11-28 11:22:13 -0600561
Andreas Dilger47a0c421997-05-16 02:46:07 -0500562 png_debug(1, "in png_write_flush\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500563 /* We have already written out all of the data */
564 if (png_ptr->row_number >= png_ptr->num_rows)
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600565 return;
Guy Schalnat0f716451995-11-28 11:22:13 -0600566
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600567 do
568 {
569 int ret;
Guy Schalnat0f716451995-11-28 11:22:13 -0600570
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600571 /* compress the data */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600572 ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600573 wrote_IDAT = 0;
Guy Schalnat0f716451995-11-28 11:22:13 -0600574
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600575 /* check for compression errors */
576 if (ret != Z_OK)
577 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500578 if (png_ptr->zstream.msg != NULL)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600579 png_error(png_ptr, png_ptr->zstream.msg);
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600580 else
581 png_error(png_ptr, "zlib error");
582 }
Guy Schalnat0f716451995-11-28 11:22:13 -0600583
Andreas Dilger47a0c421997-05-16 02:46:07 -0500584 if (!(png_ptr->zstream.avail_out))
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600585 {
586 /* write the IDAT and reset the zlib output buffer */
587 png_write_IDAT(png_ptr, png_ptr->zbuf,
588 png_ptr->zbuf_size);
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600589 png_ptr->zstream.next_out = png_ptr->zbuf;
590 png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600591 wrote_IDAT = 1;
592 }
593 } while(wrote_IDAT == 1);
Guy Schalnat0f716451995-11-28 11:22:13 -0600594
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600595 /* If there is any data left to be output, write it into a new IDAT */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600596 if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600597 {
598 /* write the IDAT and reset the zlib output buffer */
599 png_write_IDAT(png_ptr, png_ptr->zbuf,
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600600 png_ptr->zbuf_size - png_ptr->zstream.avail_out);
601 png_ptr->zstream.next_out = png_ptr->zbuf;
602 png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600603 }
604 png_ptr->flush_rows = 0;
605 png_flush(png_ptr);
Guy Schalnat0f716451995-11-28 11:22:13 -0600606}
607#endif /* PNG_WRITE_FLUSH_SUPPORTED */
608
Guy Schalnate5a37791996-06-05 15:50:50 -0500609/* free all memory used by the write */
610void
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600611png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
Guy Schalnate5a37791996-06-05 15:50:50 -0500612{
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600613 png_structp png_ptr = NULL;
614 png_infop info_ptr = NULL;
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500615#ifdef PNG_USER_MEM_SUPPORTED
616 png_free_ptr free_fn = NULL;
617#endif
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600618
Andreas Dilger47a0c421997-05-16 02:46:07 -0500619 png_debug(1, "in png_destroy_write_struct\n");
620 if (png_ptr_ptr != NULL)
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500621 {
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600622 png_ptr = *png_ptr_ptr;
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500623#ifdef PNG_USER_MEM_SUPPORTED
624 free_fn = png_ptr->free_fn;
625#endif
626 }
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600627
Andreas Dilger47a0c421997-05-16 02:46:07 -0500628 if (info_ptr_ptr != NULL)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600629 info_ptr = *info_ptr_ptr;
630
Andreas Dilger47a0c421997-05-16 02:46:07 -0500631 if (info_ptr != NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500632 {
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600633#ifdef PNG_WRITE_tEXt_SUPPORTED
634 png_free(png_ptr, info_ptr->text);
635#endif
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -0600636#if defined(PNG_READ_pCAL_SUPPORTED)
637 png_free(png_ptr, info_ptr->pcal_purpose);
638 png_free(png_ptr, info_ptr->pcal_units);
639 if (info_ptr->pcal_params != NULL)
640 {
641 int i;
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600642 for (i = 0; i < (int)info_ptr->pcal_nparams; i++)
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -0600643 {
644 png_free(png_ptr, info_ptr->pcal_params[i]);
645 }
646 png_free(png_ptr, info_ptr->pcal_params);
647 }
648#endif
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500649#ifdef PNG_USER_MEM_SUPPORTED
650 png_destroy_struct_2((png_voidp)info_ptr, free_fn);
651#else
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600652 png_destroy_struct((png_voidp)info_ptr);
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500653#endif
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600654 *info_ptr_ptr = (png_infop)NULL;
Guy Schalnate5a37791996-06-05 15:50:50 -0500655 }
Guy Schalnat6d764711995-12-19 03:22:19 -0600656
Andreas Dilger47a0c421997-05-16 02:46:07 -0500657 if (png_ptr != NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500658 {
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600659 png_write_destroy(png_ptr);
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500660#ifdef PNG_USER_MEM_SUPPORTED
661 png_destroy_struct_2((png_voidp)png_ptr, free_fn);
662#else
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600663 png_destroy_struct((png_voidp)png_ptr);
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500664#endif
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600665 *png_ptr_ptr = (png_structp)NULL;
Guy Schalnate5a37791996-06-05 15:50:50 -0500666 }
667}
668
669
Andreas Dilger47a0c421997-05-16 02:46:07 -0500670/* Free any memory used in png_ptr struct (old method) */
Guy Schalnat0d580581995-07-20 02:43:20 -0500671void
Guy Schalnat6d764711995-12-19 03:22:19 -0600672png_write_destroy(png_structp png_ptr)
Guy Schalnat0d580581995-07-20 02:43:20 -0500673{
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600674 jmp_buf tmp_jmp; /* save jump buffer */
Guy Schalnate5a37791996-06-05 15:50:50 -0500675 png_error_ptr error_fn;
676 png_error_ptr warning_fn;
677 png_voidp error_ptr;
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500678#ifdef PNG_USER_MEM_SUPPORTED
679 png_free_ptr free_fn;
680#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500681
Andreas Dilger47a0c421997-05-16 02:46:07 -0500682 png_debug(1, "in png_write_destroy\n");
Guy Schalnat0d580581995-07-20 02:43:20 -0500683 /* free any memory zlib uses */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600684 deflateEnd(&png_ptr->zstream);
Guy Schalnate5a37791996-06-05 15:50:50 -0500685
Guy Schalnat0d580581995-07-20 02:43:20 -0500686 /* free our memory. png_free checks NULL for us. */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600687 png_free(png_ptr, png_ptr->zbuf);
688 png_free(png_ptr, png_ptr->row_buf);
689 png_free(png_ptr, png_ptr->prev_row);
690 png_free(png_ptr, png_ptr->sub_row);
691 png_free(png_ptr, png_ptr->up_row);
692 png_free(png_ptr, png_ptr->avg_row);
693 png_free(png_ptr, png_ptr->paeth_row);
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -0600694#if defined(PNG_TIME_RFC1123_SUPPORTED)
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600695 png_free(png_ptr, png_ptr->time_buffer);
Glenn Randers-Pehrsonc4a2ae61998-01-16 22:06:18 -0600696#endif /* PNG_TIME_RFC1123_SUPPORTED */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500697#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
698 png_free(png_ptr, png_ptr->prev_filters);
699 png_free(png_ptr, png_ptr->filter_weights);
700 png_free(png_ptr, png_ptr->inv_filter_weights);
701 png_free(png_ptr, png_ptr->filter_costs);
702 png_free(png_ptr, png_ptr->inv_filter_costs);
703#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
Guy Schalnate5a37791996-06-05 15:50:50 -0500704
Guy Schalnat0d580581995-07-20 02:43:20 -0500705 /* reset structure */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500706 png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
Guy Schalnate5a37791996-06-05 15:50:50 -0500707
708 error_fn = png_ptr->error_fn;
709 warning_fn = png_ptr->warning_fn;
710 error_ptr = png_ptr->error_ptr;
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500711#ifdef PNG_USER_MEM_SUPPORTED
712 free_fn = png_ptr->free_fn;
713#endif
Guy Schalnate5a37791996-06-05 15:50:50 -0500714
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500715 png_memset(png_ptr, 0, sizeof (png_struct));
Guy Schalnate5a37791996-06-05 15:50:50 -0500716
717 png_ptr->error_fn = error_fn;
718 png_ptr->warning_fn = warning_fn;
719 png_ptr->error_ptr = error_ptr;
Glenn Randers-Pehrsonf7d1a171998-06-06 15:31:35 -0500720#ifdef PNG_USER_MEM_SUPPORTED
721 png_ptr->free_fn = free_fn;
722#endif
Guy Schalnate5a37791996-06-05 15:50:50 -0500723
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500724 png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
725}
Guy Schalnate5a37791996-06-05 15:50:50 -0500726
Andreas Dilger47a0c421997-05-16 02:46:07 -0500727/* Allow the application to select one or more row filters to use. */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500728void
Guy Schalnate5a37791996-06-05 15:50:50 -0500729png_set_filter(png_structp png_ptr, int method, int filters)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500730{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500731 png_debug(1, "in png_set_filter\n");
732 /* We allow 'method' only for future expansion of the base filter method. */
733 if (method == PNG_FILTER_TYPE_BASE)
Guy Schalnate5a37791996-06-05 15:50:50 -0500734 {
735 switch (filters & (PNG_ALL_FILTERS | 0x07))
736 {
737 case 5:
738 case 6:
Andreas Dilger47a0c421997-05-16 02:46:07 -0500739 case 7: png_warning(png_ptr, "Unknown row filter for method 0");
740 case PNG_FILTER_VALUE_NONE: png_ptr->do_filter=PNG_FILTER_NONE; break;
741 case PNG_FILTER_VALUE_SUB: png_ptr->do_filter=PNG_FILTER_SUB; break;
742 case PNG_FILTER_VALUE_UP: png_ptr->do_filter=PNG_FILTER_UP; break;
743 case PNG_FILTER_VALUE_AVG: png_ptr->do_filter=PNG_FILTER_AVG; break;
744 case PNG_FILTER_VALUE_PAETH: png_ptr->do_filter=PNG_FILTER_PAETH;break;
Guy Schalnate5a37791996-06-05 15:50:50 -0500745 default: png_ptr->do_filter = (png_byte)filters; break;
746 }
747
Andreas Dilger47a0c421997-05-16 02:46:07 -0500748 /* If we have allocated the row_buf, this means we have already started
749 * with the image and we should have allocated all of the filter buffers
750 * that have been selected. If prev_row isn't already allocated, then
751 * it is too late to start using the filters that need it, since we
752 * will be missing the data in the previous row. If an application
753 * wants to start and stop using particular filters during compression,
754 * it should start out with all of the filters, and then add and
755 * remove them after the start of compression.
Guy Schalnate5a37791996-06-05 15:50:50 -0500756 */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500757 if (png_ptr->row_buf != NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500758 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500759 if (png_ptr->do_filter & PNG_FILTER_SUB && png_ptr->sub_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500760 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500761 png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600762 (png_ptr->rowbytes + 1));
Andreas Dilger47a0c421997-05-16 02:46:07 -0500763 png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
Guy Schalnate5a37791996-06-05 15:50:50 -0500764 }
765
Andreas Dilger47a0c421997-05-16 02:46:07 -0500766 if (png_ptr->do_filter & PNG_FILTER_UP && png_ptr->up_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500767 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500768 if (png_ptr->prev_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500769 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500770 png_warning(png_ptr, "Can't add Up filter after starting");
Guy Schalnate5a37791996-06-05 15:50:50 -0500771 png_ptr->do_filter &= ~PNG_FILTER_UP;
772 }
773 else
774 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500775 png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600776 (png_ptr->rowbytes + 1));
Andreas Dilger47a0c421997-05-16 02:46:07 -0500777 png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
Guy Schalnate5a37791996-06-05 15:50:50 -0500778 }
779 }
780
Andreas Dilger47a0c421997-05-16 02:46:07 -0500781 if (png_ptr->do_filter & PNG_FILTER_AVG && png_ptr->avg_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500782 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500783 if (png_ptr->prev_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500784 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500785 png_warning(png_ptr, "Can't add Average filter after starting");
Guy Schalnate5a37791996-06-05 15:50:50 -0500786 png_ptr->do_filter &= ~PNG_FILTER_AVG;
787 }
788 else
789 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500790 png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600791 (png_ptr->rowbytes + 1));
Andreas Dilger47a0c421997-05-16 02:46:07 -0500792 png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
Guy Schalnate5a37791996-06-05 15:50:50 -0500793 }
794 }
795
Andreas Dilger47a0c421997-05-16 02:46:07 -0500796 if (png_ptr->do_filter & PNG_FILTER_PAETH &&
797 png_ptr->paeth_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500798 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500799 if (png_ptr->prev_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500800 {
801 png_warning(png_ptr, "Can't add Paeth filter after starting");
802 png_ptr->do_filter &= ~PNG_FILTER_PAETH;
803 }
804 else
805 {
Glenn Randers-Pehrson46f61e21998-01-30 21:45:12 -0600806 png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600807 (png_ptr->rowbytes + 1));
Andreas Dilger47a0c421997-05-16 02:46:07 -0500808 png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
Guy Schalnate5a37791996-06-05 15:50:50 -0500809 }
810 }
811
812 if (png_ptr->do_filter == PNG_NO_FILTERS)
813 png_ptr->do_filter = PNG_FILTER_NONE;
814 }
815 }
816 else
Andreas Dilger47a0c421997-05-16 02:46:07 -0500817 png_error(png_ptr, "Unknown custom filter method");
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500818}
819
Andreas Dilger47a0c421997-05-16 02:46:07 -0500820/* This allows us to influence the way in which libpng chooses the "best"
821 * filter for the current scanline. While the "minimum-sum-of-absolute-
822 * differences metric is relatively fast and effective, there is some
823 * question as to whether it can be improved upon by trying to keep the
824 * filtered data going to zlib more consistent, hopefully resulting in
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600825 * better compression.
826 */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500827#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* GRR 970116 */
828void
829png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
830 int num_weights, png_doublep filter_weights,
831 png_doublep filter_costs)
832{
833 int i;
834
835 png_debug(1, "in png_set_filter_heuristics\n");
836 if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST)
837 {
838 png_warning(png_ptr, "Unknown filter heuristic method");
839 return;
840 }
841
842 if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT)
843 {
844 heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
845 }
846
847 if (num_weights < 0 || filter_weights == NULL ||
848 heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
849 {
850 num_weights = 0;
851 }
852
853 png_ptr->num_prev_filters = num_weights;
854 png_ptr->heuristic_method = heuristic_method;
855
856 if (num_weights > 0)
857 {
858 if (png_ptr->prev_filters == NULL)
859 {
860 png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600861 (png_uint_32)(sizeof(png_byte) * num_weights));
Andreas Dilger47a0c421997-05-16 02:46:07 -0500862
863 /* To make sure that the weighting starts out fairly */
864 for (i = 0; i < num_weights; i++)
865 {
866 png_ptr->prev_filters[i] = 255;
867 }
868 }
869
870 if (png_ptr->filter_weights == NULL)
871 {
Glenn Randers-Pehrson983ec161998-03-07 11:24:03 -0600872 png_ptr->filter_weights = (png_uint_16p) png_malloc(png_ptr,
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600873 (png_uint_32)(sizeof(png_uint_16) * num_weights));
Andreas Dilger47a0c421997-05-16 02:46:07 -0500874
Glenn Randers-Pehrson983ec161998-03-07 11:24:03 -0600875 png_ptr->inv_filter_weights = (png_uint_16p) png_malloc(png_ptr,
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600876 (png_uint_32)(sizeof(png_uint_16) * num_weights));
Andreas Dilger47a0c421997-05-16 02:46:07 -0500877
878 for (i = 0; i < num_weights; i++)
879 {
880 png_ptr->inv_filter_weights[i] =
881 png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
882 }
883 }
884
885 for (i = 0; i < num_weights; i++)
886 {
887 if (filter_weights[i] < 0.0)
888 {
889 png_ptr->inv_filter_weights[i] =
890 png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
891 }
892 else
893 {
894 png_ptr->inv_filter_weights[i] =
895 (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5);
896 png_ptr->filter_weights[i] =
897 (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5);
898 }
899 }
900 }
901
902 /* If, in the future, there are other filter methods, this would
903 * need to be based on png_ptr->filter.
904 */
905 if (png_ptr->filter_costs == NULL)
906 {
Glenn Randers-Pehrson983ec161998-03-07 11:24:03 -0600907 png_ptr->filter_costs = (png_uint_16p) png_malloc(png_ptr,
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600908 (png_uint_32)(sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
Andreas Dilger47a0c421997-05-16 02:46:07 -0500909
Glenn Randers-Pehrson983ec161998-03-07 11:24:03 -0600910 png_ptr->inv_filter_costs = (png_uint_16p) png_malloc(png_ptr,
Glenn Randers-Pehrson0f881d61998-02-07 10:20:57 -0600911 (png_uint_32)(sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
Andreas Dilger47a0c421997-05-16 02:46:07 -0500912
913 for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
914 {
915 png_ptr->inv_filter_costs[i] =
916 png_ptr->filter_costs[i] = PNG_COST_FACTOR;
917 }
918 }
919
920 /* Here is where we set the relative costs of the different filters. We
921 * should take the desired compression level into account when setting
922 * the costs, so that Paeth, for instance, has a high relative cost at low
923 * compression levels, while it has a lower relative cost at higher
924 * compression settings. The filter types are in order of increasing
925 * relative cost, so it would be possible to do this with an algorithm.
926 */
927 for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
928 {
929 if (filter_costs == NULL || filter_costs[i] < 0.0)
930 {
931 png_ptr->inv_filter_costs[i] =
932 png_ptr->filter_costs[i] = PNG_COST_FACTOR;
933 }
934 else if (filter_costs[i] >= 1.0)
935 {
936 png_ptr->inv_filter_costs[i] =
937 (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5);
938 png_ptr->filter_costs[i] =
939 (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5);
940 }
941 }
942}
943#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
944
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500945void
Guy Schalnat6d764711995-12-19 03:22:19 -0600946png_set_compression_level(png_structp png_ptr, int level)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500947{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500948 png_debug(1, "in png_set_compression_level\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500949 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500950 png_ptr->zlib_level = level;
951}
952
953void
Guy Schalnat6d764711995-12-19 03:22:19 -0600954png_set_compression_mem_level(png_structp png_ptr, int mem_level)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500955{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500956 png_debug(1, "in png_set_compression_mem_level\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500957 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600958 png_ptr->zlib_mem_level = mem_level;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500959}
960
961void
Guy Schalnat6d764711995-12-19 03:22:19 -0600962png_set_compression_strategy(png_structp png_ptr, int strategy)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500963{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500964 png_debug(1, "in png_set_compression_strategy\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500965 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500966 png_ptr->zlib_strategy = strategy;
967}
968
969void
Guy Schalnat6d764711995-12-19 03:22:19 -0600970png_set_compression_window_bits(png_structp png_ptr, int window_bits)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500971{
Guy Schalnate5a37791996-06-05 15:50:50 -0500972 if (window_bits > 15)
973 png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
Glenn Randers-Pehrson4393a9a1999-09-17 12:27:26 -0500974 else if (window_bits < 8)
975 png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
Guy Schalnate5a37791996-06-05 15:50:50 -0500976 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500977 png_ptr->zlib_window_bits = window_bits;
978}
979
980void
Guy Schalnat6d764711995-12-19 03:22:19 -0600981png_set_compression_method(png_structp png_ptr, int method)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500982{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500983 png_debug(1, "in png_set_compression_method\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500984 if (method != 8)
985 png_warning(png_ptr, "Only compression method 8 is supported by PNG");
986 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500987 png_ptr->zlib_method = method;
Guy Schalnat0d580581995-07-20 02:43:20 -0500988}
989
Glenn Randers-Pehrson08a33431998-03-07 06:06:55 -0600990void
991png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
992{
993 png_ptr->write_row_fn = write_row_fn;
994}
995
996#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
997void
998png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
999 write_user_transform_fn)
1000{
1001 png_debug(1, "in png_set_write_user_transform_fn\n");
1002 png_ptr->transformations |= PNG_USER_TRANSFORM;
1003 png_ptr->write_user_transform_fn = write_user_transform_fn;
1004}
1005#endif