blob: ac7e22f69b478c3078ba1bd8b90be21c8ca15dc4 [file] [log] [blame]
Guy Schalnat0d580581995-07-20 02:43:20 -05001
2/* pngwrite.c - general routines to write a png file
3
4 libpng 1.0 beta 1 - version 0.71
5 For conditions of distribution and use, see copyright notice in png.h
6 Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
7 June 26, 1995
8 */
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. */
33 if (info->valid & PNG_INFO_gAMA)
34 png_write_gAMA(png_ptr, info->gamma);
35 if (info->valid & PNG_INFO_sBIT)
36 png_write_sBIT(png_ptr, &(info->sig_bit), info->color_type);
37 if (info->valid & PNG_INFO_cHRM)
38 png_write_cHRM(png_ptr,
39 info->x_white, info->y_white,
40 info->x_red, info->y_red,
41 info->x_green, info->y_green,
42 info->x_blue, info->y_blue);
43 if (info->valid & PNG_INFO_PLTE)
44 png_write_PLTE(png_ptr, info->palette, info->num_palette);
45 if (info->valid & PNG_INFO_tRNS)
46 png_write_tRNS(png_ptr, info->trans, &(info->trans_values),
47 info->num_trans, info->color_type);
48 if (info->valid & PNG_INFO_bKGD)
49 png_write_bKGD(png_ptr, &(info->background), info->color_type);
50 if (info->valid & PNG_INFO_hIST)
51 png_write_hIST(png_ptr, info->hist, info->num_palette);
52 if (info->valid & PNG_INFO_pHYs)
53 png_write_pHYs(png_ptr, info->x_pixels_per_unit,
54 info->y_pixels_per_unit, info->phys_unit_type);
55 if (info->valid & PNG_INFO_oFFs)
56 png_write_oFFs(png_ptr, info->x_offset, info->y_offset,
57 info->offset_unit_type);
58 if (info->valid & PNG_INFO_tIME)
59 png_write_tIME(png_ptr, &(info->mod_time));
60 /* Check to see if we need to write text chunks */
61 if (info->num_text)
62 {
63 int i; /* local counter */
64
65 /* loop through the text chunks */
66 for (i = 0; i < info->num_text; i++)
67 {
68 /* if chunk is compressed */
69 if (info->text[i].compression >= 0)
70 {
71 /* write compressed chunk */
72 png_write_zTXt(png_ptr, info->text[i].key,
73 info->text[i].text, info->text[i].text_length,
74 info->text[i].compression);
75 }
76 else
77 {
78 /* write uncompressed chunk */
79 png_write_tEXt(png_ptr, info->text[i].key,
80 info->text[i].text, info->text[i].text_length);
81 }
82 }
83 }
84}
85
86/* writes the end of the png file. If you don't want to write comments or
87 time information, you can pass NULL for info. If you already wrote these
88 in png_write_info(), do not write them again here. If you have long
89 comments, I suggest writing them here, and compressing them. */
90void
91png_write_end(png_struct *png_ptr, png_info *info)
92{
93 /* see if user wants us to write information chunks */
94 if (info)
95 {
96 /* check to see if user has supplied a time chunk */
97 if (info->valid & PNG_INFO_tIME)
98 png_write_tIME(png_ptr, &(info->mod_time));
99 /* check to see if we need to write comment chunks */
100 if (info->num_text)
101 {
102 int i; /* local index variable */
103
104 /* loop through comment chunks */
105 for (i = 0; i < info->num_text; i++)
106 {
107 /* check to see if comment is to be compressed */
108 if (info->text[i].compression >= 0)
109 {
110 /* write compressed chunk */
111 png_write_zTXt(png_ptr, info->text[i].key,
112 info->text[i].text, info->text[i].text_length,
113 info->text[i].compression);
114 }
115 else
116 {
117 /* write uncompressed chunk */
118 png_write_tEXt(png_ptr, info->text[i].key,
119 info->text[i].text, info->text[i].text_length);
120 }
121 }
122 }
123 }
124 /* write end of png file */
125 png_write_IEND(png_ptr);
126}
127
128/* initialize the info structure */
129void
130png_info_init(png_info *info)
131{
132 /* set everything to 0 */
133 memset(info, 0, sizeof (png_info));
134}
135
136void
137png_convert_from_struct_tm(png_time *ptime, struct tm *ttime)
138{
139 ptime->year = 1900 + ttime->tm_year;
140 ptime->month = ttime->tm_mon + 1;
141 ptime->day = ttime->tm_mday;
142 ptime->hour = ttime->tm_hour;
143 ptime->minute = ttime->tm_min;
144 ptime->second = ttime->tm_sec;
145}
146
147void
148png_convert_from_time_t(png_time *ptime, time_t ttime)
149{
150 struct tm *tbuf;
151
152 tbuf = gmtime(&ttime);
153 png_convert_from_struct_tm(ptime, tbuf);
154}
155
156/* initialize png structure, and allocate any memory needed */
157void
158png_write_init(png_struct *png_ptr)
159{
160 jmp_buf tmp_jmp; /* to save current jump buffer */
161
162 /* save jump buffer */
163 memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
164 /* reset all variables to 0 */
165 memset(png_ptr, 0, sizeof (png_struct));
166 /* restore jump buffer */
167 memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
168
169 /* initialize zbuf - compression buffer */
170 png_ptr->zbuf_size = PNG_ZBUF_SIZE;
171 png_ptr->zbuf = png_large_malloc(png_ptr, png_ptr->zbuf_size);
172 /* initialize zlib */
173 png_ptr->zstream = &(png_ptr->zstream_struct);
174 png_ptr->zstream->zalloc = png_zalloc;
175 png_ptr->zstream->zfree = png_zfree;
176 png_ptr->zstream->opaque = (voidp)png_ptr;
177 deflateInit(png_ptr->zstream, Z_BEST_COMPRESSION);
178 png_ptr->zstream->next_out = png_ptr->zbuf;
179 png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
180}
181
182/* write a few rows of image data. If the image is interlaced,
183 either you will have to write the 7 sub images, or, if you
184 have called png_set_interlace_handling(), you will have to
185 "write" the image seven times */
186void
187png_write_rows(png_struct *png_ptr, png_byte **row,
188 png_uint_32 num_rows)
189{
190 png_uint_32 i; /* row counter */
191 png_byte **rp; /* row pointer */
192
193 /* loop through the rows */
194 for (i = 0, rp = row; i < num_rows; i++, rp++)
195 {
196 png_write_row(png_ptr, *rp);
197 }
198}
199
200/* write the image. You only need to call this function once, even
201 if you are writing an interlaced image. */
202void
203png_write_image(png_struct *png_ptr, png_byte **image)
204{
205 png_uint_32 i; /* row index */
206 int pass, num_pass; /* pass variables */
207 png_byte **rp; /* points to current row */
208
209 /* intialize interlace handling. If image is not interlaced,
210 this will set pass to 1 */
211 num_pass = png_set_interlace_handling(png_ptr);
212 /* loop through passes */
213 for (pass = 0; pass < num_pass; pass++)
214 {
215 /* loop through image */
216 for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
217 {
218 png_write_row(png_ptr, *rp);
219 }
220 }
221}
222
223/* write a row of image data */
224void
225png_write_row(png_struct *png_ptr, png_byte *row)
226{
227 /* initialize transformations and other stuff if first time */
228 if (png_ptr->row_number == 0 && png_ptr->pass == 0)
229 {
230 png_write_start_row(png_ptr);
231 }
232
233 /* if interlaced and not interested in row, return */
234 if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
235 {
236 switch (png_ptr->pass)
237 {
238 case 0:
239 if (png_ptr->row_number & 7)
240 {
241 png_write_finish_row(png_ptr);
242 return;
243 }
244 break;
245 case 1:
246 if ((png_ptr->row_number & 7) || png_ptr->width < 5)
247 {
248 png_write_finish_row(png_ptr);
249 return;
250 }
251 break;
252 case 2:
253 if ((png_ptr->row_number & 7) != 4)
254 {
255 png_write_finish_row(png_ptr);
256 return;
257 }
258 break;
259 case 3:
260 if ((png_ptr->row_number & 3) || png_ptr->width < 3)
261 {
262 png_write_finish_row(png_ptr);
263 return;
264 }
265 break;
266 case 4:
267 if ((png_ptr->row_number & 3) != 2)
268 {
269 png_write_finish_row(png_ptr);
270 return;
271 }
272 break;
273 case 5:
274 if ((png_ptr->row_number & 1) || png_ptr->width < 2)
275 {
276 png_write_finish_row(png_ptr);
277 return;
278 }
279 break;
280 case 6:
281 if (!(png_ptr->row_number & 1))
282 {
283 png_write_finish_row(png_ptr);
284 return;
285 }
286 break;
287 }
288 }
289
290 /* set up row info for transformations */
291 png_ptr->row_info.color_type = png_ptr->color_type;
292 png_ptr->row_info.width = png_ptr->usr_width;
293 png_ptr->row_info.channels = png_ptr->usr_channels;
294 png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
295 png_ptr->row_info.pixel_depth = png_ptr->row_info.bit_depth *
296 png_ptr->row_info.channels;
297 png_ptr->row_info.rowbytes = ((png_ptr->row_info.width *
298 (png_uint_32)png_ptr->row_info.pixel_depth + 7) >> 3);
299
300 /* copy users row into buffer, leaving room for filter byte */
301 memcpy(png_ptr->row_buf + 1, row, (png_size_t)png_ptr->row_info.rowbytes);
302
303 /* handle interlacing */
304 if (png_ptr->interlaced && png_ptr->pass < 6 &&
305 (png_ptr->transformations & PNG_INTERLACE))
306 {
307 png_do_write_interlace(&(png_ptr->row_info),
308 png_ptr->row_buf + 1, png_ptr->pass);
309 /* this should always get caught above, but still ... */
310 if (!(png_ptr->row_info.width))
311 {
312 png_write_finish_row(png_ptr);
313 return;
314 }
315 }
316
317 /* handle other transformations */
318 if (png_ptr->transformations)
319 png_do_write_transformations(png_ptr);
320
321 /* filter rows that have been proved to help */
322 if (png_ptr->bit_depth >= 8 && png_ptr->color_type != 3)
323 {
324 /* save row to previous row */
325 memcpy(png_ptr->save_row, png_ptr->row_buf,
326 (png_size_t)png_ptr->row_info.rowbytes + 1);
327
328 /* filter row */
329 png_write_filter_row(&(png_ptr->row_info), png_ptr->row_buf,
330 png_ptr->prev_row);
331
332 /* trade saved pointer and prev pointer so next row references are correctly */
333 { /* scope limiter */
334 png_byte *tptr;
335
336 tptr = png_ptr->prev_row;
337 png_ptr->prev_row = png_ptr->save_row;
338 png_ptr->save_row = tptr;
339 }
340 }
341 else
342 /* set filter row to "none" */
343 png_ptr->row_buf[0] = 0;
344
345 /* set up the zlib input buffer */
346 png_ptr->zstream->next_in = png_ptr->row_buf;
347 png_ptr->zstream->avail_in = (uInt)png_ptr->row_info.rowbytes + 1;
348
349#ifdef zlibinout
350/* temp zlib problem */
351{
352 extern FILE *fpzlibin;
353
354 fwrite(png_ptr->row_buf, 1, png_ptr->zstream->avail_in, fpzlibin);
355}
356/* end temp zlib problem */
357#endif
358
359 /* repeat until we have compressed all the data */
360 do
361 {
362 int ret; /* return of zlib */
363
364 /* compress the data */
365 ret = deflate(png_ptr->zstream, Z_NO_FLUSH);
366 /* check for compression errors */
367 if (ret != Z_OK)
368 {
369 if (png_ptr->zstream->msg)
370 png_error(png_ptr, png_ptr->zstream->msg);
371 else
372 png_error(png_ptr, "zlib error");
373 }
374
375 /* see if it is time to write another IDAT */
376 if (!png_ptr->zstream->avail_out)
377 {
378 /* write the IDAT and reset the zlib output buffer */
379 png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
380 png_ptr->zstream->next_out = png_ptr->zbuf;
381 png_ptr->zstream->avail_out = (uInt)png_ptr->zbuf_size;
382 }
383 /* repeat until all data has been compressed */
384 } while (png_ptr->zstream->avail_in);
385
386 /* finish row - updates counters and flushes zlib if last row */
387 png_write_finish_row(png_ptr);
388}
389
390/* free any memory used in png struct */
391void
392png_write_destroy(png_struct *png_ptr)
393{
394 jmp_buf tmp_jmp; /* save jump buffer */
395
396 /* free any memory zlib uses */
397 deflateEnd(png_ptr->zstream);
398 /* free our memory. png_free checks NULL for us. */
399 png_large_free(png_ptr, png_ptr->zbuf);
400 png_large_free(png_ptr, png_ptr->row_buf);
401 png_large_free(png_ptr, png_ptr->prev_row);
402 png_large_free(png_ptr, png_ptr->save_row);
403 /* reset structure */
404 memcpy(tmp_jmp, png_ptr->jmpbuf, sizeof (jmp_buf));
405 memset(png_ptr, 0, sizeof (png_struct));
406 memcpy(png_ptr->jmpbuf, tmp_jmp, sizeof (jmp_buf));
407}
408