blob: 2e2e3718d420b430df735b199b7835ac651f6606 [file] [log] [blame]
Guy Schalnat0d580581995-07-20 02:43:20 -05001
2/* pngwrite.c - general routines to write a png file
3
Guy Schalnat51f0eb41995-09-26 05:22:39 -05004 libpng 1.0 beta 2 - version 0.81
Guy Schalnat0d580581995-07-20 02:43:20 -05005 For conditions of distribution and use, see copyright notice in png.h
6 Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
Guy Schalnat51f0eb41995-09-26 05:22:39 -05007 August 24, 1995
Guy Schalnat0d580581995-07-20 02:43:20 -05008 */
9
10/* get internal access to png.h */
11#define PNG_INTERNAL
12#include "png.h"
13
14/* Writes all the png information. This is the suggested way to use
15 the library. If you have a new chunk to add, make a function to
16 write it, and put it in the correct location here. If you want
17 the chunk written after the image data, put it in png_write_end().
18 I strongly encurage you to supply a PNG_INFO_ flag, and check
19 info->valid before writing the chunk, as that will keep the code
20 from breaking if you want to just write a plain png file.
21 If you have long comments, I suggest writing them in png_write_end(),
22 and compressing them. */
23void
24png_write_info(png_struct *png_ptr, png_info *info)
25{
26 png_write_sig(png_ptr); /* write PNG signature */
27 /* write IHDR information. */
28 png_write_IHDR(png_ptr, info->width, info->height, info->bit_depth,
29 info->color_type, info->compression_type, info->filter_type,
30 info->interlace_type);
31 /* the rest of these check to see if the valid field has the appropriate
32 flag set, and if it does, writes the chunk. */
Guy Schalnat51f0eb41995-09-26 05:22:39 -050033#if defined(PNG_WRITE_gAMA_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050034 if (info->valid & PNG_INFO_gAMA)
35 png_write_gAMA(png_ptr, info->gamma);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050036#endif
37#if defined(PNG_WRITE_sBIT_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050038 if (info->valid & PNG_INFO_sBIT)
39 png_write_sBIT(png_ptr, &(info->sig_bit), info->color_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050040#endif
41#if defined(PNG_WRITE_cHRM_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050042 if (info->valid & PNG_INFO_cHRM)
43 png_write_cHRM(png_ptr,
44 info->x_white, info->y_white,
45 info->x_red, info->y_red,
46 info->x_green, info->y_green,
47 info->x_blue, info->y_blue);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050048#endif
Guy Schalnat0d580581995-07-20 02:43:20 -050049 if (info->valid & PNG_INFO_PLTE)
50 png_write_PLTE(png_ptr, info->palette, info->num_palette);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050051#if defined(PNG_WRITE_tRNS_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050052 if (info->valid & PNG_INFO_tRNS)
53 png_write_tRNS(png_ptr, info->trans, &(info->trans_values),
54 info->num_trans, info->color_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050055#endif
56#if defined(PNG_WRITE_bKGD_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050057 if (info->valid & PNG_INFO_bKGD)
58 png_write_bKGD(png_ptr, &(info->background), info->color_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050059#endif
60#if defined(PNG_WRITE_hIST_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050061 if (info->valid & PNG_INFO_hIST)
62 png_write_hIST(png_ptr, info->hist, info->num_palette);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050063#endif
64#if defined(PNG_WRITE_pHYs_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050065 if (info->valid & PNG_INFO_pHYs)
66 png_write_pHYs(png_ptr, info->x_pixels_per_unit,
67 info->y_pixels_per_unit, info->phys_unit_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050068#endif
69#if defined(PNG_WRITE_oFFs_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050070 if (info->valid & PNG_INFO_oFFs)
71 png_write_oFFs(png_ptr, info->x_offset, info->y_offset,
72 info->offset_unit_type);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050073#endif
74#if defined(PNG_WRITE_tIME_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050075 if (info->valid & PNG_INFO_tIME)
76 png_write_tIME(png_ptr, &(info->mod_time));
77 /* Check to see if we need to write text chunks */
Guy Schalnat51f0eb41995-09-26 05:22:39 -050078#endif
79#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050080 if (info->num_text)
81 {
82 int i; /* local counter */
83
84 /* loop through the text chunks */
85 for (i = 0; i < info->num_text; i++)
86 {
87 /* if chunk is compressed */
88 if (info->text[i].compression >= 0)
89 {
Guy Schalnat51f0eb41995-09-26 05:22:39 -050090#if defined(PNG_WRITE_zTXt_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -050091 /* write compressed chunk */
92 png_write_zTXt(png_ptr, info->text[i].key,
93 info->text[i].text, info->text[i].text_length,
94 info->text[i].compression);
Guy Schalnat51f0eb41995-09-26 05:22:39 -050095#endif
Guy Schalnat0d580581995-07-20 02:43:20 -050096 }
97 else
98 {
Guy Schalnat51f0eb41995-09-26 05:22:39 -050099#if defined(PNG_WRITE_tEXt_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500100 /* write uncompressed chunk */
101 png_write_tEXt(png_ptr, info->text[i].key,
102 info->text[i].text, info->text[i].text_length);
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500103#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500104 }
105 }
106 }
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500107#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500108}
109
110/* writes the end of the png file. If you don't want to write comments or
111 time information, you can pass NULL for info. If you already wrote these
112 in png_write_info(), do not write them again here. If you have long
113 comments, I suggest writing them here, and compressing them. */
114void
115png_write_end(png_struct *png_ptr, png_info *info)
116{
117 /* see if user wants us to write information chunks */
118 if (info)
119 {
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500120#if defined(PNG_WRITE_tIME_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500121 /* check to see if user has supplied a time chunk */
122 if (info->valid & PNG_INFO_tIME)
123 png_write_tIME(png_ptr, &(info->mod_time));
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500124#endif
125#if defined(PNG_WRITE_tEXt_SUPPORTED) || defined(PNG_WRITE_zTXt_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500126 /* check to see if we need to write comment chunks */
127 if (info->num_text)
128 {
129 int i; /* local index variable */
130
131 /* loop through comment chunks */
132 for (i = 0; i < info->num_text; i++)
133 {
134 /* check to see if comment is to be compressed */
135 if (info->text[i].compression >= 0)
136 {
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500137#if defined(PNG_WRITE_zTXt_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500138 /* write compressed chunk */
139 png_write_zTXt(png_ptr, info->text[i].key,
140 info->text[i].text, info->text[i].text_length,
141 info->text[i].compression);
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500142#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500143 }
144 else
145 {
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500146#if defined(PNG_WRITE_tEXt_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500147 /* write uncompressed chunk */
148 png_write_tEXt(png_ptr, info->text[i].key,
149 info->text[i].text, info->text[i].text_length);
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500150#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500151 }
152 }
153 }
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500154#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500155 }
156 /* write end of png file */
157 png_write_IEND(png_ptr);
158}
159
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500160#if defined(PNG_WRITE_tIME_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500161void
162png_convert_from_struct_tm(png_time *ptime, struct tm *ttime)
163{
164 ptime->year = 1900 + ttime->tm_year;
165 ptime->month = ttime->tm_mon + 1;
166 ptime->day = ttime->tm_mday;
167 ptime->hour = ttime->tm_hour;
168 ptime->minute = ttime->tm_min;
169 ptime->second = ttime->tm_sec;
170}
171
172void
173png_convert_from_time_t(png_time *ptime, time_t ttime)
174{
175 struct tm *tbuf;
176
177 tbuf = gmtime(&ttime);
178 png_convert_from_struct_tm(ptime, tbuf);
179}
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500180#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500181
182/* initialize png structure, and allocate any memory needed */
183void
184png_write_init(png_struct *png_ptr)
185{
186 jmp_buf tmp_jmp; /* to save current jump buffer */
187
188 /* save jump buffer */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500189 png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
Guy Schalnat0d580581995-07-20 02:43:20 -0500190 /* reset all variables to 0 */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500191 png_memset(png_ptr, 0, sizeof (png_struct));
Guy Schalnat0d580581995-07-20 02:43:20 -0500192 /* restore jump buffer */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500193 png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
Guy Schalnat0d580581995-07-20 02:43:20 -0500194
195 /* initialize zbuf - compression buffer */
196 png_ptr->zbuf_size = PNG_ZBUF_SIZE;
197 png_ptr->zbuf = png_large_malloc(png_ptr, png_ptr->zbuf_size);
Guy Schalnat0d580581995-07-20 02:43:20 -0500198}
199
200/* write a few rows of image data. If the image is interlaced,
201 either you will have to write the 7 sub images, or, if you
202 have called png_set_interlace_handling(), you will have to
203 "write" the image seven times */
204void
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500205png_write_rows(png_struct *png_ptr, png_bytef **row,
Guy Schalnat0d580581995-07-20 02:43:20 -0500206 png_uint_32 num_rows)
207{
208 png_uint_32 i; /* row counter */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500209 png_bytef **rp; /* row pointer */
Guy Schalnat0d580581995-07-20 02:43:20 -0500210
211 /* loop through the rows */
212 for (i = 0, rp = row; i < num_rows; i++, rp++)
213 {
214 png_write_row(png_ptr, *rp);
215 }
216}
217
218/* write the image. You only need to call this function once, even
219 if you are writing an interlaced image. */
220void
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500221png_write_image(png_struct *png_ptr, png_bytef **image)
Guy Schalnat0d580581995-07-20 02:43:20 -0500222{
223 png_uint_32 i; /* row index */
224 int pass, num_pass; /* pass variables */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500225 png_bytef **rp; /* points to current row */
Guy Schalnat0d580581995-07-20 02:43:20 -0500226
227 /* intialize interlace handling. If image is not interlaced,
228 this will set pass to 1 */
229 num_pass = png_set_interlace_handling(png_ptr);
230 /* loop through passes */
231 for (pass = 0; pass < num_pass; pass++)
232 {
233 /* loop through image */
234 for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
235 {
236 png_write_row(png_ptr, *rp);
237 }
238 }
239}
240
241/* write a row of image data */
242void
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500243png_write_row(png_struct *png_ptr, png_bytef *row)
Guy Schalnat0d580581995-07-20 02:43:20 -0500244{
245 /* initialize transformations and other stuff if first time */
246 if (png_ptr->row_number == 0 && png_ptr->pass == 0)
247 {
248 png_write_start_row(png_ptr);
249 }
250
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500251#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500252 /* if interlaced and not interested in row, return */
253 if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
254 {
255 switch (png_ptr->pass)
256 {
257 case 0:
258 if (png_ptr->row_number & 7)
259 {
260 png_write_finish_row(png_ptr);
261 return;
262 }
263 break;
264 case 1:
265 if ((png_ptr->row_number & 7) || png_ptr->width < 5)
266 {
267 png_write_finish_row(png_ptr);
268 return;
269 }
270 break;
271 case 2:
272 if ((png_ptr->row_number & 7) != 4)
273 {
274 png_write_finish_row(png_ptr);
275 return;
276 }
277 break;
278 case 3:
279 if ((png_ptr->row_number & 3) || png_ptr->width < 3)
280 {
281 png_write_finish_row(png_ptr);
282 return;
283 }
284 break;
285 case 4:
286 if ((png_ptr->row_number & 3) != 2)
287 {
288 png_write_finish_row(png_ptr);
289 return;
290 }
291 break;
292 case 5:
293 if ((png_ptr->row_number & 1) || png_ptr->width < 2)
294 {
295 png_write_finish_row(png_ptr);
296 return;
297 }
298 break;
299 case 6:
300 if (!(png_ptr->row_number & 1))
301 {
302 png_write_finish_row(png_ptr);
303 return;
304 }
305 break;
306 }
307 }
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500308#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500309
310 /* set up row info for transformations */
311 png_ptr->row_info.color_type = png_ptr->color_type;
312 png_ptr->row_info.width = png_ptr->usr_width;
313 png_ptr->row_info.channels = png_ptr->usr_channels;
314 png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
315 png_ptr->row_info.pixel_depth = png_ptr->row_info.bit_depth *
316 png_ptr->row_info.channels;
317 png_ptr->row_info.rowbytes = ((png_ptr->row_info.width *
318 (png_uint_32)png_ptr->row_info.pixel_depth + 7) >> 3);
319
320 /* copy users row into buffer, leaving room for filter byte */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500321 png_memcpy(png_ptr->row_buf + 1, row, (png_size_t)png_ptr->row_info.rowbytes);
Guy Schalnat0d580581995-07-20 02:43:20 -0500322
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500323#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
Guy Schalnat0d580581995-07-20 02:43:20 -0500324 /* handle interlacing */
325 if (png_ptr->interlaced && png_ptr->pass < 6 &&
326 (png_ptr->transformations & PNG_INTERLACE))
327 {
328 png_do_write_interlace(&(png_ptr->row_info),
329 png_ptr->row_buf + 1, png_ptr->pass);
330 /* this should always get caught above, but still ... */
331 if (!(png_ptr->row_info.width))
332 {
333 png_write_finish_row(png_ptr);
334 return;
335 }
336 }
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500337#endif
Guy Schalnat0d580581995-07-20 02:43:20 -0500338
339 /* handle other transformations */
340 if (png_ptr->transformations)
341 png_do_write_transformations(png_ptr);
342
343 /* filter rows that have been proved to help */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500344 if (png_ptr->do_filter)
Guy Schalnat0d580581995-07-20 02:43:20 -0500345 {
346 /* save row to previous row */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500347 png_memcpy(png_ptr->save_row, png_ptr->row_buf,
Guy Schalnat0d580581995-07-20 02:43:20 -0500348 (png_size_t)png_ptr->row_info.rowbytes + 1);
349
350 /* filter row */
351 png_write_filter_row(&(png_ptr->row_info), png_ptr->row_buf,
352 png_ptr->prev_row);
353
354 /* trade saved pointer and prev pointer so next row references are correctly */
355 { /* scope limiter */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500356 png_bytef *tptr;
Guy Schalnat0d580581995-07-20 02:43:20 -0500357
358 tptr = png_ptr->prev_row;
359 png_ptr->prev_row = png_ptr->save_row;
360 png_ptr->save_row = tptr;
361 }
362 }
363 else
364 /* set filter row to "none" */
365 png_ptr->row_buf[0] = 0;
366
367 /* set up the zlib input buffer */
368 png_ptr->zstream->next_in = png_ptr->row_buf;
369 png_ptr->zstream->avail_in = (uInt)png_ptr->row_info.rowbytes + 1;
Guy Schalnat0d580581995-07-20 02:43:20 -0500370 /* repeat until we have compressed all the data */
371 do
372 {
373 int ret; /* return of zlib */
374
375 /* compress the data */
376 ret = deflate(png_ptr->zstream, Z_NO_FLUSH);
377 /* check for compression errors */
378 if (ret != Z_OK)
379 {
380 if (png_ptr->zstream->msg)
381 png_error(png_ptr, png_ptr->zstream->msg);
382 else
383 png_error(png_ptr, "zlib error");
384 }
385
386 /* see if it is time to write another IDAT */
387 if (!png_ptr->zstream->avail_out)
388 {
389 /* write the IDAT and reset the zlib output buffer */
390 png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
391 png_ptr->zstream->next_out = png_ptr->zbuf;
392 png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
393 }
394 /* repeat until all data has been compressed */
395 } while (png_ptr->zstream->avail_in);
396
397 /* finish row - updates counters and flushes zlib if last row */
398 png_write_finish_row(png_ptr);
399}
400
401/* free any memory used in png struct */
402void
403png_write_destroy(png_struct *png_ptr)
404{
405 jmp_buf tmp_jmp; /* save jump buffer */
406
407 /* free any memory zlib uses */
408 deflateEnd(png_ptr->zstream);
409 /* free our memory. png_free checks NULL for us. */
410 png_large_free(png_ptr, png_ptr->zbuf);
411 png_large_free(png_ptr, png_ptr->row_buf);
412 png_large_free(png_ptr, png_ptr->prev_row);
413 png_large_free(png_ptr, png_ptr->save_row);
414 /* reset structure */
Guy Schalnat51f0eb41995-09-26 05:22:39 -0500415 png_memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
416 png_memset(png_ptr, 0, sizeof (png_struct));
417 png_memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
418}
419void
420png_set_filtering(png_struct *png_ptr, int filter)
421{
422 png_ptr->do_custom_filter = 1;
423 png_ptr->do_filter = filter;
424}
425
426void
427png_set_compression_level(png_struct *png_ptr, int level)
428{
429 png_ptr->zlib_custom_level = 1;
430 png_ptr->zlib_level = level;
431}
432
433void
434png_set_compression_mem_level(png_struct *png_ptr, int mem_level)
435{
436 png_ptr->zlib_custom_mem_level = 1;
437 png_ptr->zlib_mem_level = mem_level;
438}
439
440void
441png_set_compression_strategy(png_struct *png_ptr, int strategy)
442{
443 png_ptr->zlib_custom_strategy = 1;
444 png_ptr->zlib_strategy = strategy;
445}
446
447void
448png_set_compression_window_bits(png_struct *png_ptr, int window_bits)
449{
450 png_ptr->zlib_custom_window_bits = 1;
451 png_ptr->zlib_window_bits = window_bits;
452}
453
454void
455png_set_compression_method(png_struct *png_ptr, int method)
456{
457 png_ptr->zlib_custom_method = 1;
458 png_ptr->zlib_method = method;
Guy Schalnat0d580581995-07-20 02:43:20 -0500459}
460