blob: 6a7b060e6e19c73917a7943fdf5a057a2e440294 [file] [log] [blame]
Andreas Dilger47a0c421997-05-16 02:46:07 -05001
2/* pngwrite.c - general routines to write a PNG file
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -06003 *
4 * libpng 1.00.97
5 * 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
8 * May 28, 1997
9 */
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
18 * after the image data, put it in png_write_end(). I strongly encurage
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 */
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,
36 info_ptr->filter_type, info_ptr->interlace_type);
Guy Schalnat0d580581995-07-20 02:43:20 -050037 /* the rest of these check to see if the valid field has the appropriate
38 flag set, and if it does, writes the chunk. */
Guy Schalnat51f0eb41995-09-26 05:22:39 -050039#if defined(PNG_WRITE_gAMA_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060040 if (info_ptr->valid & PNG_INFO_gAMA)
41 png_write_gAMA(png_ptr, info_ptr->gamma);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050042#endif
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -060043#if defined(PNG_WRITE_sRGB_SUPPORTED)
44 if (info_ptr->valid & PNG_INFO_sRGB)
45 png_write_sRGB(png_ptr, info_ptr->srgb_intent);
46#endif
Guy Schalnat51f0eb41995-09-26 05:22:39 -050047#if defined(PNG_WRITE_sBIT_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060048 if (info_ptr->valid & PNG_INFO_sBIT)
49 png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050050#endif
51#if defined(PNG_WRITE_cHRM_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060052 if (info_ptr->valid & PNG_INFO_cHRM)
Guy Schalnat0d580581995-07-20 02:43:20 -050053 png_write_cHRM(png_ptr,
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060054 info_ptr->x_white, info_ptr->y_white,
55 info_ptr->x_red, info_ptr->y_red,
56 info_ptr->x_green, info_ptr->y_green,
57 info_ptr->x_blue, info_ptr->y_blue);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050058#endif
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060059 if (info_ptr->valid & PNG_INFO_PLTE)
Andreas Dilger47a0c421997-05-16 02:46:07 -050060 png_write_PLTE(png_ptr, info_ptr->palette,
61 (png_uint_32)info_ptr->num_palette);
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060062 else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
Guy Schalnate5a37791996-06-05 15:50:50 -050063 png_error(png_ptr, "Valid palette required for paletted images\n");
Guy Schalnat51f0eb41995-09-26 05:22:39 -050064#if defined(PNG_WRITE_tRNS_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060065 if (info_ptr->valid & PNG_INFO_tRNS)
66 png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values),
67 info_ptr->num_trans, info_ptr->color_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050068#endif
69#if defined(PNG_WRITE_bKGD_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060070 if (info_ptr->valid & PNG_INFO_bKGD)
71 png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050072#endif
73#if defined(PNG_WRITE_hIST_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060074 if (info_ptr->valid & PNG_INFO_hIST)
75 png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050076#endif
Guy Schalnat51f0eb41995-09-26 05:22:39 -050077#if defined(PNG_WRITE_oFFs_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060078 if (info_ptr->valid & PNG_INFO_oFFs)
79 png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
80 info_ptr->offset_unit_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050081#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -050082#if defined(PNG_WRITE_pCAL_SUPPORTED)
83 if (info_ptr->valid & PNG_INFO_pCAL)
84 png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
85 info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
86 info_ptr->pcal_units, info_ptr->pcal_params);
87#endif
88#if defined(PNG_WRITE_pHYs_SUPPORTED)
89 if (info_ptr->valid & PNG_INFO_pHYs)
90 png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
91 info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
92#endif
Guy Schalnat51f0eb41995-09-26 05:22:39 -050093#if defined(PNG_WRITE_tIME_SUPPORTED)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060094 if (info_ptr->valid & PNG_INFO_tIME)
Guy Schalnate5a37791996-06-05 15:50:50 -050095 {
Andreas Dilger02ad0ef1997-01-17 01:34:35 -060096 png_write_tIME(png_ptr, &(info_ptr->mod_time));
Guy Schalnate5a37791996-06-05 15:50:50 -050097 png_ptr->flags |= PNG_FLAG_WROTE_tIME;
98 }
Guy Schalnat51f0eb41995-09-26 05:22:39 -050099#endif
100#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
Guy Schalnate5a37791996-06-05 15:50:50 -0500101 /* Check to see if we need to write text chunks */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500102 for (i = 0; i < info_ptr->num_text; i++)
Guy Schalnat0d580581995-07-20 02:43:20 -0500103 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500104 png_debug2(2, "Writing header text chunk %d, type %d\n", i,
105 info_ptr->text[i].compression);
106 /* If we want a compressed text chunk */
107 if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
108 {
109#if defined(PNG_WRITE_zTXt_SUPPORTED)
110 /* write compressed chunk */
111 png_write_zTXt(png_ptr, info_ptr->text[i].key,
112 info_ptr->text[i].text, info_ptr->text[i].text_length,
113 info_ptr->text[i].compression);
114#else
115 png_warning(png_ptr, "Unable to write compressed text\n");
116#endif
117 /* Mark this chunk as written */
118 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
119 }
120 else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
121 {
122#if defined(PNG_WRITE_tEXt_SUPPORTED)
123 /* write uncompressed chunk */
124 png_write_tEXt(png_ptr, info_ptr->text[i].key,
125 info_ptr->text[i].text, info_ptr->text[i].text_length);
126#else
127 png_warning(png_ptr, "Unable to write uncompressed text\n");
128#endif
129 /* Mark this chunk as written */
130 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
131 }
132 }
133#endif
134}
Guy Schalnat0d580581995-07-20 02:43:20 -0500135
Andreas Dilger47a0c421997-05-16 02:46:07 -0500136/* Writes the end of the PNG file. If you don't want to write comments or
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600137 * time information, you can pass NULL for info. If you already wrote these
138 * in png_write_info(), do not write them again here. If you have long
139 * comments, I suggest writing them here, and compressing them.
140 */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500141void
142png_write_end(png_structp png_ptr, png_infop info_ptr)
143{
144 png_debug(1, "in png_write_end\n");
145 if (!(png_ptr->mode & PNG_HAVE_IDAT))
146 png_error(png_ptr, "No IDATs written into file");
147
148 /* see if user wants us to write information chunks */
149 if (info_ptr != NULL)
150 {
Glenn Randers-Pehrson70e3f541998-01-03 22:40:55 -0600151#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
Andreas Dilger47a0c421997-05-16 02:46:07 -0500152 int i; /* local index variable */
Glenn Randers-Pehrson70e3f541998-01-03 22:40:55 -0600153#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -0500154#if defined(PNG_WRITE_tIME_SUPPORTED)
155 /* check to see if user has supplied a time chunk */
156 if (info_ptr->valid & PNG_INFO_tIME &&
157 !(png_ptr->flags & PNG_FLAG_WROTE_tIME))
158 png_write_tIME(png_ptr, &(info_ptr->mod_time));
159#endif
160#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
161 /* loop through comment chunks */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600162 for (i = 0; i < info_ptr->num_text; i++)
Guy Schalnat0d580581995-07-20 02:43:20 -0500163 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500164 png_debug2(2, "Writing trailer text chunk %d, type %d\n", i,
165 info_ptr->text[i].compression);
166 if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
Guy Schalnat0d580581995-07-20 02:43:20 -0500167 {
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500168#if defined(PNG_WRITE_zTXt_SUPPORTED)
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600169 /* write compressed chunk */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600170 png_write_zTXt(png_ptr, info_ptr->text[i].key,
171 info_ptr->text[i].text, info_ptr->text[i].text_length,
172 info_ptr->text[i].compression);
Guy Schalnate5a37791996-06-05 15:50:50 -0500173#else
174 png_warning(png_ptr, "Unable to write compressed text\n");
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500175#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -0500176 /* Mark this chunk as written */
177 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
Guy Schalnat0d580581995-07-20 02:43:20 -0500178 }
Andreas Dilger47a0c421997-05-16 02:46:07 -0500179 else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
Guy Schalnat0d580581995-07-20 02:43:20 -0500180 {
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500181#if defined(PNG_WRITE_tEXt_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500182 /* write uncompressed chunk */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600183 png_write_tEXt(png_ptr, info_ptr->text[i].key,
184 info_ptr->text[i].text, info_ptr->text[i].text_length);
Guy Schalnate5a37791996-06-05 15:50:50 -0500185#else
186 png_warning(png_ptr, "Unable to write uncompressed text\n");
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500187#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500188
Andreas Dilger47a0c421997-05-16 02:46:07 -0500189 /* Mark this chunk as written */
190 info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
Guy Schalnat0d580581995-07-20 02:43:20 -0500191 }
192 }
Guy Schalnat6d764711995-12-19 03:22:19 -0600193#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500194 }
Guy Schalnate5a37791996-06-05 15:50:50 -0500195
196 png_ptr->mode |= PNG_AFTER_IDAT;
197
Andreas Dilger47a0c421997-05-16 02:46:07 -0500198 /* write end of PNG file */
Guy Schalnat0d580581995-07-20 02:43:20 -0500199 png_write_IEND(png_ptr);
200}
201
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600202#if defined(PNG_TIME_RFC1152_SUPPORTED)
203/* Convert the supplied time into an RFC 1152 string suitable for use in
204 * a "Creation Time" or other text-based time string.
205 */
206png_charp
207png_convert_to_rfc1152(png_structp png_ptr, png_timep ptime)
208{
209 const char *short_months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
210 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
211
212 if (png_ptr->time_buffer == NULL)
213 {
214 png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, 29*sizeof(char));
215 }
216
Glenn Randers-Pehrson70e3f541998-01-03 22:40:55 -0600217#ifdef USE_FAR_KEYWORD
218 {
219 char near_time_buf[29];
220 sprintf(near_time_buf, "%d %s %d %02d:%02d:%02d +0000",
221 ptime->day % 31, short_months[ptime->month],
222 ptime->year, ptime->hour % 24, ptime->minute % 60,
223 ptime->second % 61);
224 png_memcpy(png_ptr->time_buffer, near_time_buf,
225 29*sizeof(char));
226 }
227#else
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600228 sprintf(png_ptr->time_buffer, "%d %s %d %02d:%02d:%02d +0000",
Glenn Randers-Pehrson70e3f541998-01-03 22:40:55 -0600229 ptime->day % 31, short_months[ptime->month],
230 ptime->year, ptime->hour % 24, ptime->minute % 60,
231 ptime->second % 61);
232#endif
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600233 return png_ptr->time_buffer;
234}
235#endif /* PNG_TIME_RFC1152_SUPPORTED */
236
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500237#if defined(PNG_WRITE_tIME_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500238void
Guy Schalnat6d764711995-12-19 03:22:19 -0600239png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime)
Guy Schalnat0d580581995-07-20 02:43:20 -0500240{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500241 png_debug(1, "in png_convert_from_struct_tm\n");
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600242 ptime->year = (png_uint_16)(1900 + ttime->tm_year);
243 ptime->month = (png_byte)(ttime->tm_mon + 1);
244 ptime->day = (png_byte)ttime->tm_mday;
245 ptime->hour = (png_byte)ttime->tm_hour;
246 ptime->minute = (png_byte)ttime->tm_min;
247 ptime->second = (png_byte)ttime->tm_sec;
Guy Schalnat0d580581995-07-20 02:43:20 -0500248}
249
250void
Guy Schalnat6d764711995-12-19 03:22:19 -0600251png_convert_from_time_t(png_timep ptime, time_t ttime)
Guy Schalnat0d580581995-07-20 02:43:20 -0500252{
253 struct tm *tbuf;
254
Andreas Dilger47a0c421997-05-16 02:46:07 -0500255 png_debug(1, "in png_convert_from_time_t\n");
Guy Schalnat0d580581995-07-20 02:43:20 -0500256 tbuf = gmtime(&ttime);
257 png_convert_from_struct_tm(ptime, tbuf);
258}
Guy Schalnat6d764711995-12-19 03:22:19 -0600259#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500260
Andreas Dilger47a0c421997-05-16 02:46:07 -0500261/* Initialize png_ptr structure, and allocate any memory needed */
Guy Schalnate5a37791996-06-05 15:50:50 -0500262png_structp
263png_create_write_struct(png_const_charp user_png_ver, voidp error_ptr,
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600264 png_error_ptr error_fn, png_error_ptr warn_fn)
Guy Schalnat0d580581995-07-20 02:43:20 -0500265{
Guy Schalnate5a37791996-06-05 15:50:50 -0500266 png_structp png_ptr;
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600267#ifdef USE_FAR_KEYWORD
268 jmp_buf jmpbuf;
269#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -0500270 png_debug(1, "in png_create_write_struct\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500271 if ((png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG)) == NULL)
272 {
273 return (png_structp)NULL;
274 }
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);
283 return (png_structp)NULL;
284 }
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600285#ifdef USE_FAR_KEYWORD
286 png_memcpy(png_ptr->jmpbuf,jmpbuf,sizeof(jmp_buf));
287#endif
288 png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
Guy Schalnate5a37791996-06-05 15:50:50 -0500289
Andreas Dilger47a0c421997-05-16 02:46:07 -0500290 /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
291 * we must recompile any applications that use any older library version.
292 * For versions after libpng 1.0, we will be compatible, so we need
293 * only check the first digit.
294 */
295 if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
296 (png_libpng_ver[0] == '0' && user_png_ver[2] < '9'))
Guy Schalnate5a37791996-06-05 15:50:50 -0500297 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500298 png_error(png_ptr,
299 "Incompatible libpng version in application and library");
Guy Schalnate5a37791996-06-05 15:50:50 -0500300 }
Guy Schalnat0d580581995-07-20 02:43:20 -0500301
302 /* initialize zbuf - compression buffer */
303 png_ptr->zbuf_size = PNG_ZBUF_SIZE;
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600304 png_ptr->zbuf = png_malloc(png_ptr, png_ptr->zbuf_size);
Guy Schalnate5a37791996-06-05 15:50:50 -0500305
306 png_set_write_fn(png_ptr, NULL, NULL, NULL);
307
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600308#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
309 png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
310 1, NULL, NULL);
311#endif
312
Guy Schalnate5a37791996-06-05 15:50:50 -0500313 return (png_ptr);
314}
315
316
Andreas Dilger47a0c421997-05-16 02:46:07 -0500317/* Initialize png_ptr structure, and allocate any memory needed */
Guy Schalnate5a37791996-06-05 15:50:50 -0500318void
319png_write_init(png_structp png_ptr)
320{
321 jmp_buf tmp_jmp; /* to save current jump buffer */
322
Andreas Dilger47a0c421997-05-16 02:46:07 -0500323 png_debug(1, "in png_write_init\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500324 /* save jump buffer and error functions */
325 png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
326
327 /* reset all variables to 0 */
328 png_memset(png_ptr, 0, sizeof (png_struct));
329
330 /* restore jump buffer */
331 png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
332
333 /* initialize zbuf - compression buffer */
334 png_ptr->zbuf_size = PNG_ZBUF_SIZE;
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600335 png_ptr->zbuf = png_malloc(png_ptr, png_ptr->zbuf_size);
Guy Schalnate5a37791996-06-05 15:50:50 -0500336 png_set_write_fn(png_ptr, NULL, NULL, NULL);
Andreas Dilger47a0c421997-05-16 02:46:07 -0500337
338#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
339 png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
340 1, NULL, NULL);
341#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500342}
343
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600344/* Write a few rows of image data. If the image is interlaced,
345 * either you will have to write the 7 sub images, or, if you
346 * have called png_set_interlace_handling(), you will have to
347 * "write" the image seven times.
348 */
Guy Schalnat0d580581995-07-20 02:43:20 -0500349void
Guy Schalnat6d764711995-12-19 03:22:19 -0600350png_write_rows(png_structp png_ptr, png_bytepp row,
Guy Schalnat0d580581995-07-20 02:43:20 -0500351 png_uint_32 num_rows)
352{
353 png_uint_32 i; /* row counter */
Guy Schalnat6d764711995-12-19 03:22:19 -0600354 png_bytepp rp; /* row pointer */
Guy Schalnat0d580581995-07-20 02:43:20 -0500355
Andreas Dilger47a0c421997-05-16 02:46:07 -0500356 png_debug(1, "in png_write_rows\n");
Guy Schalnat0d580581995-07-20 02:43:20 -0500357 /* loop through the rows */
358 for (i = 0, rp = row; i < num_rows; i++, rp++)
359 {
360 png_write_row(png_ptr, *rp);
361 }
362}
363
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600364/* Write the image. You only need to call this function once, even
365 * if you are writing an interlaced image.
366 */
Guy Schalnat0d580581995-07-20 02:43:20 -0500367void
Guy Schalnat6d764711995-12-19 03:22:19 -0600368png_write_image(png_structp png_ptr, png_bytepp image)
Guy Schalnat0d580581995-07-20 02:43:20 -0500369{
370 png_uint_32 i; /* row index */
371 int pass, num_pass; /* pass variables */
Guy Schalnat6d764711995-12-19 03:22:19 -0600372 png_bytepp rp; /* points to current row */
Guy Schalnat0d580581995-07-20 02:43:20 -0500373
Andreas Dilger47a0c421997-05-16 02:46:07 -0500374 png_debug(1, "in png_write_image\n");
Guy Schalnat0d580581995-07-20 02:43:20 -0500375 /* intialize interlace handling. If image is not interlaced,
376 this will set pass to 1 */
377 num_pass = png_set_interlace_handling(png_ptr);
378 /* loop through passes */
379 for (pass = 0; pass < num_pass; pass++)
380 {
381 /* loop through image */
382 for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
383 {
384 png_write_row(png_ptr, *rp);
385 }
386 }
387}
388
Guy Schalnate5a37791996-06-05 15:50:50 -0500389/* called by user to write a row of image data */
Guy Schalnat0d580581995-07-20 02:43:20 -0500390void
Guy Schalnat6d764711995-12-19 03:22:19 -0600391png_write_row(png_structp png_ptr, png_bytep row)
Guy Schalnat0d580581995-07-20 02:43:20 -0500392{
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600393 png_debug2(1, "in png_write_row (row %ld, pass %d)\n",
394 png_ptr->row_number, png_ptr->pass);
Guy Schalnat0d580581995-07-20 02:43:20 -0500395 /* initialize transformations and other stuff if first time */
Guy Schalnat6d764711995-12-19 03:22:19 -0600396 if (png_ptr->row_number == 0 && png_ptr->pass == 0)
Guy Schalnat0d580581995-07-20 02:43:20 -0500397 {
398 png_write_start_row(png_ptr);
399 }
400
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500401#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500402 /* if interlaced and not interested in row, return */
403 if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
404 {
405 switch (png_ptr->pass)
406 {
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600407 case 0:
Guy Schalnat0d580581995-07-20 02:43:20 -0500408 if (png_ptr->row_number & 7)
409 {
410 png_write_finish_row(png_ptr);
411 return;
412 }
413 break;
414 case 1:
415 if ((png_ptr->row_number & 7) || png_ptr->width < 5)
416 {
417 png_write_finish_row(png_ptr);
418 return;
419 }
420 break;
421 case 2:
422 if ((png_ptr->row_number & 7) != 4)
423 {
424 png_write_finish_row(png_ptr);
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600425 return;
Guy Schalnat0d580581995-07-20 02:43:20 -0500426 }
427 break;
428 case 3:
429 if ((png_ptr->row_number & 3) || png_ptr->width < 3)
430 {
431 png_write_finish_row(png_ptr);
432 return;
433 }
434 break;
435 case 4:
436 if ((png_ptr->row_number & 3) != 2)
437 {
438 png_write_finish_row(png_ptr);
439 return;
440 }
441 break;
442 case 5:
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600443 if ((png_ptr->row_number & 1) || png_ptr->width < 2)
Guy Schalnat0d580581995-07-20 02:43:20 -0500444 {
445 png_write_finish_row(png_ptr);
446 return;
447 }
448 break;
449 case 6:
450 if (!(png_ptr->row_number & 1))
451 {
452 png_write_finish_row(png_ptr);
453 return;
454 }
455 break;
456 }
457 }
Guy Schalnat6d764711995-12-19 03:22:19 -0600458#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500459
460 /* set up row info for transformations */
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600461 png_ptr->row_info.color_type = png_ptr->color_type;
Guy Schalnat0d580581995-07-20 02:43:20 -0500462 png_ptr->row_info.width = png_ptr->usr_width;
463 png_ptr->row_info.channels = png_ptr->usr_channels;
464 png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600465 png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
466 png_ptr->row_info.channels);
Guy Schalnat0d580581995-07-20 02:43:20 -0500467 png_ptr->row_info.rowbytes = ((png_ptr->row_info.width *
468 (png_uint_32)png_ptr->row_info.pixel_depth + 7) >> 3);
469
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600470 png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type);
471 png_debug1(3, "row_info->width = %d\n", png_ptr->row_info.width);
472 png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels);
473 png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth);
474 png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth);
475 png_debug1(3, "row_info->rowbytes = %d\n", png_ptr->row_info.rowbytes);
Andreas Dilger47a0c421997-05-16 02:46:07 -0500476
477 /* Copy user's row into buffer, leaving room for filter byte. */
478 png_memcpy(png_ptr->row_buf + 1, row, png_ptr->row_info.rowbytes);
Guy Schalnat0d580581995-07-20 02:43:20 -0500479
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500480#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500481 /* handle interlacing */
482 if (png_ptr->interlaced && png_ptr->pass < 6 &&
483 (png_ptr->transformations & PNG_INTERLACE))
484 {
485 png_do_write_interlace(&(png_ptr->row_info),
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600486 png_ptr->row_buf + 1, png_ptr->pass);
Guy Schalnat0d580581995-07-20 02:43:20 -0500487 /* this should always get caught above, but still ... */
488 if (!(png_ptr->row_info.width))
489 {
490 png_write_finish_row(png_ptr);
491 return;
492 }
493 }
Guy Schalnat6d764711995-12-19 03:22:19 -0600494#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500495
496 /* handle other transformations */
497 if (png_ptr->transformations)
498 png_do_write_transformations(png_ptr);
499
Andreas Dilger47a0c421997-05-16 02:46:07 -0500500 /* Find a filter if necessary, filter the row and write it out. */
Guy Schalnate5a37791996-06-05 15:50:50 -0500501 png_write_find_filter(png_ptr, &(png_ptr->row_info));
Guy Schalnat0d580581995-07-20 02:43:20 -0500502}
503
Guy Schalnat0f716451995-11-28 11:22:13 -0600504#if defined(PNG_WRITE_FLUSH_SUPPORTED)
505/* Set the automatic flush interval or 0 to turn flushing off */
506void
Guy Schalnat4ee97b01996-01-16 01:51:56 -0600507png_set_flush(png_structp png_ptr, int nrows)
Guy Schalnat0f716451995-11-28 11:22:13 -0600508{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500509 png_debug(1, "in png_set_flush\n");
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600510 png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
Guy Schalnat0f716451995-11-28 11:22:13 -0600511}
512
513/* flush the current output buffers now */
514void
Guy Schalnat4ee97b01996-01-16 01:51:56 -0600515png_write_flush(png_structp png_ptr)
Guy Schalnat0f716451995-11-28 11:22:13 -0600516{
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600517 int wrote_IDAT;
Guy Schalnat0f716451995-11-28 11:22:13 -0600518
Andreas Dilger47a0c421997-05-16 02:46:07 -0500519 png_debug(1, "in png_write_flush\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500520 /* We have already written out all of the data */
521 if (png_ptr->row_number >= png_ptr->num_rows)
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600522 return;
Guy Schalnat0f716451995-11-28 11:22:13 -0600523
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600524 do
525 {
526 int ret;
Guy Schalnat0f716451995-11-28 11:22:13 -0600527
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600528 /* compress the data */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600529 ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600530 wrote_IDAT = 0;
Guy Schalnat0f716451995-11-28 11:22:13 -0600531
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600532 /* check for compression errors */
533 if (ret != Z_OK)
534 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500535 if (png_ptr->zstream.msg != NULL)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600536 png_error(png_ptr, png_ptr->zstream.msg);
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600537 else
538 png_error(png_ptr, "zlib error");
539 }
Guy Schalnat0f716451995-11-28 11:22:13 -0600540
Andreas Dilger47a0c421997-05-16 02:46:07 -0500541 if (!(png_ptr->zstream.avail_out))
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600542 {
543 /* write the IDAT and reset the zlib output buffer */
544 png_write_IDAT(png_ptr, png_ptr->zbuf,
545 png_ptr->zbuf_size);
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600546 png_ptr->zstream.next_out = png_ptr->zbuf;
547 png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600548 wrote_IDAT = 1;
549 }
550 } while(wrote_IDAT == 1);
Guy Schalnat0f716451995-11-28 11:22:13 -0600551
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600552 /* If there is any data left to be output, write it into a new IDAT */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600553 if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600554 {
555 /* write the IDAT and reset the zlib output buffer */
556 png_write_IDAT(png_ptr, png_ptr->zbuf,
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600557 png_ptr->zbuf_size - png_ptr->zstream.avail_out);
558 png_ptr->zstream.next_out = png_ptr->zbuf;
559 png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600560 }
561 png_ptr->flush_rows = 0;
562 png_flush(png_ptr);
Guy Schalnat0f716451995-11-28 11:22:13 -0600563}
564#endif /* PNG_WRITE_FLUSH_SUPPORTED */
565
Guy Schalnate5a37791996-06-05 15:50:50 -0500566/* free all memory used by the write */
567void
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600568png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
Guy Schalnate5a37791996-06-05 15:50:50 -0500569{
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600570 png_structp png_ptr = NULL;
571 png_infop info_ptr = NULL;
572
Andreas Dilger47a0c421997-05-16 02:46:07 -0500573 png_debug(1, "in png_destroy_write_struct\n");
574 if (png_ptr_ptr != NULL)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600575 png_ptr = *png_ptr_ptr;
576
Andreas Dilger47a0c421997-05-16 02:46:07 -0500577 if (info_ptr_ptr != NULL)
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600578 info_ptr = *info_ptr_ptr;
579
Andreas Dilger47a0c421997-05-16 02:46:07 -0500580 if (info_ptr != NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500581 {
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600582#ifdef PNG_WRITE_tEXt_SUPPORTED
583 png_free(png_ptr, info_ptr->text);
584#endif
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600585 png_destroy_struct((png_voidp)info_ptr);
586 *info_ptr_ptr = (png_infop)NULL;
Guy Schalnate5a37791996-06-05 15:50:50 -0500587 }
Guy Schalnat6d764711995-12-19 03:22:19 -0600588
Andreas Dilger47a0c421997-05-16 02:46:07 -0500589 if (png_ptr != NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500590 {
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600591 png_write_destroy(png_ptr);
592 png_destroy_struct((png_voidp)png_ptr);
593 *png_ptr_ptr = (png_structp)NULL;
Guy Schalnate5a37791996-06-05 15:50:50 -0500594 }
595}
596
597
Andreas Dilger47a0c421997-05-16 02:46:07 -0500598/* Free any memory used in png_ptr struct (old method) */
Guy Schalnat0d580581995-07-20 02:43:20 -0500599void
Guy Schalnat6d764711995-12-19 03:22:19 -0600600png_write_destroy(png_structp png_ptr)
Guy Schalnat0d580581995-07-20 02:43:20 -0500601{
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600602 jmp_buf tmp_jmp; /* save jump buffer */
Guy Schalnate5a37791996-06-05 15:50:50 -0500603 png_error_ptr error_fn;
604 png_error_ptr warning_fn;
605 png_voidp error_ptr;
Guy Schalnat0d580581995-07-20 02:43:20 -0500606
Andreas Dilger47a0c421997-05-16 02:46:07 -0500607 png_debug(1, "in png_write_destroy\n");
Guy Schalnat0d580581995-07-20 02:43:20 -0500608 /* free any memory zlib uses */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600609 deflateEnd(&png_ptr->zstream);
Guy Schalnate5a37791996-06-05 15:50:50 -0500610
Guy Schalnat0d580581995-07-20 02:43:20 -0500611 /* free our memory. png_free checks NULL for us. */
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600612 png_free(png_ptr, png_ptr->zbuf);
613 png_free(png_ptr, png_ptr->row_buf);
614 png_free(png_ptr, png_ptr->prev_row);
615 png_free(png_ptr, png_ptr->sub_row);
616 png_free(png_ptr, png_ptr->up_row);
617 png_free(png_ptr, png_ptr->avg_row);
618 png_free(png_ptr, png_ptr->paeth_row);
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600619#if defined(PNG_TIME_RFC1152_SUPPORTED)
620 png_free(png_ptr, png_ptr->time_buffer);
621#endif /* PNG_TIME_RFC1152_SUPPORTED */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500622#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
623 png_free(png_ptr, png_ptr->prev_filters);
624 png_free(png_ptr, png_ptr->filter_weights);
625 png_free(png_ptr, png_ptr->inv_filter_weights);
626 png_free(png_ptr, png_ptr->filter_costs);
627 png_free(png_ptr, png_ptr->inv_filter_costs);
628#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
Guy Schalnate5a37791996-06-05 15:50:50 -0500629
Guy Schalnat0d580581995-07-20 02:43:20 -0500630 /* reset structure */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500631 png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
Guy Schalnate5a37791996-06-05 15:50:50 -0500632
633 error_fn = png_ptr->error_fn;
634 warning_fn = png_ptr->warning_fn;
635 error_ptr = png_ptr->error_ptr;
636
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500637 png_memset(png_ptr, 0, sizeof (png_struct));
Guy Schalnate5a37791996-06-05 15:50:50 -0500638
639 png_ptr->error_fn = error_fn;
640 png_ptr->warning_fn = warning_fn;
641 png_ptr->error_ptr = error_ptr;
642
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500643 png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
644}
Guy Schalnate5a37791996-06-05 15:50:50 -0500645
Andreas Dilger47a0c421997-05-16 02:46:07 -0500646/* Allow the application to select one or more row filters to use. */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500647void
Guy Schalnate5a37791996-06-05 15:50:50 -0500648png_set_filter(png_structp png_ptr, int method, int filters)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500649{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500650 png_debug(1, "in png_set_filter\n");
651 /* We allow 'method' only for future expansion of the base filter method. */
652 if (method == PNG_FILTER_TYPE_BASE)
Guy Schalnate5a37791996-06-05 15:50:50 -0500653 {
654 switch (filters & (PNG_ALL_FILTERS | 0x07))
655 {
656 case 5:
657 case 6:
Andreas Dilger47a0c421997-05-16 02:46:07 -0500658 case 7: png_warning(png_ptr, "Unknown row filter for method 0");
659 case PNG_FILTER_VALUE_NONE: png_ptr->do_filter=PNG_FILTER_NONE; break;
660 case PNG_FILTER_VALUE_SUB: png_ptr->do_filter=PNG_FILTER_SUB; break;
661 case PNG_FILTER_VALUE_UP: png_ptr->do_filter=PNG_FILTER_UP; break;
662 case PNG_FILTER_VALUE_AVG: png_ptr->do_filter=PNG_FILTER_AVG; break;
663 case PNG_FILTER_VALUE_PAETH: png_ptr->do_filter=PNG_FILTER_PAETH;break;
Guy Schalnate5a37791996-06-05 15:50:50 -0500664 default: png_ptr->do_filter = (png_byte)filters; break;
665 }
666
Andreas Dilger47a0c421997-05-16 02:46:07 -0500667 /* If we have allocated the row_buf, this means we have already started
668 * with the image and we should have allocated all of the filter buffers
669 * that have been selected. If prev_row isn't already allocated, then
670 * it is too late to start using the filters that need it, since we
671 * will be missing the data in the previous row. If an application
672 * wants to start and stop using particular filters during compression,
673 * it should start out with all of the filters, and then add and
674 * remove them after the start of compression.
Guy Schalnate5a37791996-06-05 15:50:50 -0500675 */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500676 if (png_ptr->row_buf != NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500677 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500678 if (png_ptr->do_filter & PNG_FILTER_SUB && png_ptr->sub_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500679 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500680 png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
Guy Schalnate5a37791996-06-05 15:50:50 -0500681 png_ptr->rowbytes + 1);
Andreas Dilger47a0c421997-05-16 02:46:07 -0500682 png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
Guy Schalnate5a37791996-06-05 15:50:50 -0500683 }
684
Andreas Dilger47a0c421997-05-16 02:46:07 -0500685 if (png_ptr->do_filter & PNG_FILTER_UP && png_ptr->up_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500686 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500687 if (png_ptr->prev_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500688 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500689 png_warning(png_ptr, "Can't add Up filter after starting");
Guy Schalnate5a37791996-06-05 15:50:50 -0500690 png_ptr->do_filter &= ~PNG_FILTER_UP;
691 }
692 else
693 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500694 png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
Guy Schalnate5a37791996-06-05 15:50:50 -0500695 png_ptr->rowbytes + 1);
Andreas Dilger47a0c421997-05-16 02:46:07 -0500696 png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
Guy Schalnate5a37791996-06-05 15:50:50 -0500697 }
698 }
699
Andreas Dilger47a0c421997-05-16 02:46:07 -0500700 if (png_ptr->do_filter & PNG_FILTER_AVG && png_ptr->avg_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500701 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500702 if (png_ptr->prev_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500703 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500704 png_warning(png_ptr, "Can't add Average filter after starting");
Guy Schalnate5a37791996-06-05 15:50:50 -0500705 png_ptr->do_filter &= ~PNG_FILTER_AVG;
706 }
707 else
708 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500709 png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
Guy Schalnate5a37791996-06-05 15:50:50 -0500710 png_ptr->rowbytes + 1);
Andreas Dilger47a0c421997-05-16 02:46:07 -0500711 png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
Guy Schalnate5a37791996-06-05 15:50:50 -0500712 }
713 }
714
Andreas Dilger47a0c421997-05-16 02:46:07 -0500715 if (png_ptr->do_filter & PNG_FILTER_PAETH &&
716 png_ptr->paeth_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500717 {
Andreas Dilger47a0c421997-05-16 02:46:07 -0500718 if (png_ptr->prev_row == NULL)
Guy Schalnate5a37791996-06-05 15:50:50 -0500719 {
720 png_warning(png_ptr, "Can't add Paeth filter after starting");
721 png_ptr->do_filter &= ~PNG_FILTER_PAETH;
722 }
723 else
724 {
Andreas Dilger02ad0ef1997-01-17 01:34:35 -0600725 png_ptr->paeth_row = (png_bytep )png_malloc(png_ptr,
Guy Schalnate5a37791996-06-05 15:50:50 -0500726 png_ptr->rowbytes + 1);
Andreas Dilger47a0c421997-05-16 02:46:07 -0500727 png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
Guy Schalnate5a37791996-06-05 15:50:50 -0500728 }
729 }
730
731 if (png_ptr->do_filter == PNG_NO_FILTERS)
732 png_ptr->do_filter = PNG_FILTER_NONE;
733 }
734 }
735 else
Andreas Dilger47a0c421997-05-16 02:46:07 -0500736 png_error(png_ptr, "Unknown custom filter method");
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500737}
738
Andreas Dilger47a0c421997-05-16 02:46:07 -0500739/* This allows us to influence the way in which libpng chooses the "best"
740 * filter for the current scanline. While the "minimum-sum-of-absolute-
741 * differences metric is relatively fast and effective, there is some
742 * question as to whether it can be improved upon by trying to keep the
743 * filtered data going to zlib more consistent, hopefully resulting in
Glenn Randers-Pehrsonb6ce43d1998-01-01 07:13:13 -0600744 * better compression.
745 */
Andreas Dilger47a0c421997-05-16 02:46:07 -0500746#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* GRR 970116 */
747void
748png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
749 int num_weights, png_doublep filter_weights,
750 png_doublep filter_costs)
751{
Glenn Randers-Pehrson70e3f541998-01-03 22:40:55 -0600752#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
Andreas Dilger47a0c421997-05-16 02:46:07 -0500753 int i;
Glenn Randers-Pehrson70e3f541998-01-03 22:40:55 -0600754#endif
Andreas Dilger47a0c421997-05-16 02:46:07 -0500755
756 png_debug(1, "in png_set_filter_heuristics\n");
757 if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST)
758 {
759 png_warning(png_ptr, "Unknown filter heuristic method");
760 return;
761 }
762
763 if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT)
764 {
765 heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
766 }
767
768 if (num_weights < 0 || filter_weights == NULL ||
769 heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
770 {
771 num_weights = 0;
772 }
773
774 png_ptr->num_prev_filters = num_weights;
775 png_ptr->heuristic_method = heuristic_method;
776
777 if (num_weights > 0)
778 {
779 if (png_ptr->prev_filters == NULL)
780 {
781 png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
782 sizeof(png_byte) * num_weights);
783
784 /* To make sure that the weighting starts out fairly */
785 for (i = 0; i < num_weights; i++)
786 {
787 png_ptr->prev_filters[i] = 255;
788 }
789 }
790
791 if (png_ptr->filter_weights == NULL)
792 {
793 png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
794 sizeof(png_uint_16) * num_weights);
795
796 png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
797 sizeof(png_uint_16) * num_weights);
798
799 for (i = 0; i < num_weights; i++)
800 {
801 png_ptr->inv_filter_weights[i] =
802 png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
803 }
804 }
805
806 for (i = 0; i < num_weights; i++)
807 {
808 if (filter_weights[i] < 0.0)
809 {
810 png_ptr->inv_filter_weights[i] =
811 png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
812 }
813 else
814 {
815 png_ptr->inv_filter_weights[i] =
816 (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5);
817 png_ptr->filter_weights[i] =
818 (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5);
819 }
820 }
821 }
822
823 /* If, in the future, there are other filter methods, this would
824 * need to be based on png_ptr->filter.
825 */
826 if (png_ptr->filter_costs == NULL)
827 {
828 png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
829 sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST);
830
831 png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
832 sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST);
833
834 for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
835 {
836 png_ptr->inv_filter_costs[i] =
837 png_ptr->filter_costs[i] = PNG_COST_FACTOR;
838 }
839 }
840
841 /* Here is where we set the relative costs of the different filters. We
842 * should take the desired compression level into account when setting
843 * the costs, so that Paeth, for instance, has a high relative cost at low
844 * compression levels, while it has a lower relative cost at higher
845 * compression settings. The filter types are in order of increasing
846 * relative cost, so it would be possible to do this with an algorithm.
847 */
848 for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
849 {
850 if (filter_costs == NULL || filter_costs[i] < 0.0)
851 {
852 png_ptr->inv_filter_costs[i] =
853 png_ptr->filter_costs[i] = PNG_COST_FACTOR;
854 }
855 else if (filter_costs[i] >= 1.0)
856 {
857 png_ptr->inv_filter_costs[i] =
858 (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5);
859 png_ptr->filter_costs[i] =
860 (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5);
861 }
862 }
863}
864#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
865
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500866void
Guy Schalnat6d764711995-12-19 03:22:19 -0600867png_set_compression_level(png_structp png_ptr, int level)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500868{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500869 png_debug(1, "in png_set_compression_level\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500870 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500871 png_ptr->zlib_level = level;
872}
873
874void
Guy Schalnat6d764711995-12-19 03:22:19 -0600875png_set_compression_mem_level(png_structp png_ptr, int mem_level)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500876{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500877 png_debug(1, "in png_set_compression_mem_level\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500878 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
Guy Schalnatb2e01bd1996-01-26 01:38:47 -0600879 png_ptr->zlib_mem_level = mem_level;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500880}
881
882void
Guy Schalnat6d764711995-12-19 03:22:19 -0600883png_set_compression_strategy(png_structp png_ptr, int strategy)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500884{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500885 png_debug(1, "in png_set_compression_strategy\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500886 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500887 png_ptr->zlib_strategy = strategy;
888}
889
890void
Guy Schalnat6d764711995-12-19 03:22:19 -0600891png_set_compression_window_bits(png_structp png_ptr, int window_bits)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500892{
Guy Schalnate5a37791996-06-05 15:50:50 -0500893 if (window_bits > 15)
894 png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
895 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500896 png_ptr->zlib_window_bits = window_bits;
897}
898
899void
Guy Schalnat6d764711995-12-19 03:22:19 -0600900png_set_compression_method(png_structp png_ptr, int method)
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500901{
Andreas Dilger47a0c421997-05-16 02:46:07 -0500902 png_debug(1, "in png_set_compression_method\n");
Guy Schalnate5a37791996-06-05 15:50:50 -0500903 if (method != 8)
904 png_warning(png_ptr, "Only compression method 8 is supported by PNG");
905 png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500906 png_ptr->zlib_method = method;
Guy Schalnat0d580581995-07-20 02:43:20 -0500907}
908