Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | |
2 | * |
||
3 | * Last changed in libpng 1.6.2 [April 25, 2013] |
||
4 | * Copyright (c) 1998-2013 Glenn Randers-Pehrson |
||
5 | * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) |
||
6 | * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) |
||
7 | * |
||
8 | * This code is released under the libpng license. |
||
9 | * For conditions of distribution and use, see the disclaimer |
||
10 | * and license in png.h |
||
11 | */ |
||
12 | |||
13 | |||
14 | #if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) |
||
15 | # include |
||
16 | #endif |
||
17 | |||
18 | |||
19 | |||
20 | |||
21 | /* Write out all the unknown chunks for the current given location */ |
||
22 | static void |
||
23 | write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr, |
||
24 | unsigned int where) |
||
25 | { |
||
26 | if (info_ptr->unknown_chunks_num) |
||
27 | { |
||
28 | png_const_unknown_chunkp up; |
||
29 | |||
30 | |||
31 | |||
32 | |||
33 | up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; |
||
34 | ++up) |
||
35 | if (up->location & where) |
||
36 | { |
||
37 | /* If per-chunk unknown chunk handling is enabled use it, otherwise |
||
38 | * just write the chunks the application has set. |
||
39 | */ |
||
40 | #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED |
||
41 | int keep = png_handle_as_unknown(png_ptr, up->name); |
||
42 | |||
43 | |||
44 | * matter of handling an ancillary unknown chunk. In the read side |
||
45 | * the default behavior is to discard it, in the code below the default |
||
46 | * behavior is to write it. Critical chunks are, however, only |
||
47 | * written if explicitly listed or if the default is set to write all |
||
48 | * unknown chunks. |
||
49 | * |
||
50 | * The default handling is also slightly weird - it is not possible to |
||
51 | * stop the writing of all unsafe-to-copy chunks! |
||
52 | * |
||
53 | * TODO: REVIEW: this would seem to be a bug. |
||
54 | */ |
||
55 | if (keep != PNG_HANDLE_CHUNK_NEVER && |
||
56 | ((up->name[3] & 0x20) /* safe-to-copy overrides everything */ || |
||
57 | keep == PNG_HANDLE_CHUNK_ALWAYS || |
||
58 | (keep == PNG_HANDLE_CHUNK_AS_DEFAULT && |
||
59 | png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS))) |
||
60 | #endif |
||
61 | { |
||
62 | /* TODO: review, what is wrong with a zero length unknown chunk? */ |
||
63 | if (up->size == 0) |
||
64 | png_warning(png_ptr, "Writing zero-length unknown chunk"); |
||
65 | |||
66 | |||
67 | } |
||
68 | } |
||
69 | } |
||
70 | } |
||
71 | #endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */ |
||
72 | |||
73 | |||
74 | * library. If you have a new chunk to add, make a function to write it, |
||
75 | * and put it in the correct location here. If you want the chunk written |
||
76 | * after the image data, put it in png_write_end(). I strongly encourage |
||
77 | * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing |
||
78 | * the chunk, as that will keep the code from breaking if you want to just |
||
79 | * write a plain PNG file. If you have long comments, I suggest writing |
||
80 | * them in png_write_end(), and compressing them. |
||
81 | */ |
||
82 | void PNGAPI |
||
83 | png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr) |
||
84 | { |
||
85 | png_debug(1, "in png_write_info_before_PLTE"); |
||
86 | |||
87 | |||
88 | return; |
||
89 | |||
90 | |||
91 | { |
||
92 | /* Write PNG signature */ |
||
93 | png_write_sig(png_ptr); |
||
94 | |||
95 | |||
96 | if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \ |
||
97 | (png_ptr->mng_features_permitted)) |
||
98 | { |
||
99 | png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); |
||
100 | png_ptr->mng_features_permitted = 0; |
||
101 | } |
||
102 | #endif |
||
103 | |||
104 | |||
105 | png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, |
||
106 | info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, |
||
107 | info_ptr->filter_type, |
||
108 | #ifdef PNG_WRITE_INTERLACING_SUPPORTED |
||
109 | info_ptr->interlace_type |
||
110 | #else |
||
111 | 0 |
||
112 | |||
113 | ); |
||
114 | |||
115 | |||
116 | * flag set, and if it does, writes the chunk. |
||
117 | * |
||
118 | * 1.6.0: COLORSPACE support controls the writing of these chunks too, and |
||
119 | * the chunks will be written if the WRITE routine is there and information |
||
120 | * is available in the COLORSPACE. (See png_colorspace_sync_info in png.c |
||
121 | * for where the valid flags get set.) |
||
122 | * |
||
123 | * Under certain circumstances the colorspace can be invalidated without |
||
124 | * syncing the info_struct 'valid' flags; this happens if libpng detects and |
||
125 | * error and calls png_error while the color space is being set, yet the |
||
126 | * application continues writing the PNG. So check the 'invalid' flag here |
||
127 | * too. |
||
128 | */ |
||
129 | #ifdef PNG_GAMMA_SUPPORTED |
||
130 | # ifdef PNG_WRITE_gAMA_SUPPORTED |
||
131 | if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && |
||
132 | (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) && |
||
133 | (info_ptr->valid & PNG_INFO_gAMA)) |
||
134 | png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma); |
||
135 | # endif |
||
136 | #endif |
||
137 | |||
138 | |||
139 | /* Write only one of sRGB or an ICC profile. If a profile was supplied |
||
140 | * and it matches one of the known sRGB ones issue a warning. |
||
141 | */ |
||
142 | # ifdef PNG_WRITE_iCCP_SUPPORTED |
||
143 | if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && |
||
144 | (info_ptr->valid & PNG_INFO_iCCP)) |
||
145 | { |
||
146 | # ifdef PNG_WRITE_sRGB_SUPPORTED |
||
147 | if (info_ptr->valid & PNG_INFO_sRGB) |
||
148 | png_app_warning(png_ptr, |
||
149 | "profile matches sRGB but writing iCCP instead"); |
||
150 | # endif |
||
151 | |||
152 | |||
153 | info_ptr->iccp_profile); |
||
154 | } |
||
155 | # ifdef PNG_WRITE_sRGB_SUPPORTED |
||
156 | else |
||
157 | # endif |
||
158 | # endif |
||
159 | |||
160 | |||
161 | if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && |
||
162 | (info_ptr->valid & PNG_INFO_sRGB)) |
||
163 | png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent); |
||
164 | # endif /* WRITE_sRGB */ |
||
165 | #endif /* COLORSPACE */ |
||
166 | |||
167 | |||
168 | if (info_ptr->valid & PNG_INFO_sBIT) |
||
169 | png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); |
||
170 | #endif |
||
171 | |||
172 | |||
173 | # ifdef PNG_WRITE_cHRM_SUPPORTED |
||
174 | if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && |
||
175 | (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) && |
||
176 | (info_ptr->valid & PNG_INFO_cHRM)) |
||
177 | png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy); |
||
178 | # endif |
||
179 | #endif |
||
180 | |||
181 | |||
182 | write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR); |
||
183 | #endif |
||
184 | |||
185 | |||
186 | } |
||
187 | } |
||
188 | |||
189 | |||
190 | png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) |
||
191 | { |
||
192 | #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) |
||
193 | int i; |
||
194 | #endif |
||
195 | |||
196 | |||
197 | |||
198 | |||
199 | return; |
||
200 | |||
201 | |||
202 | |||
203 | |||
204 | png_write_PLTE(png_ptr, info_ptr->palette, |
||
205 | (png_uint_32)info_ptr->num_palette); |
||
206 | |||
207 | |||
208 | png_error(png_ptr, "Valid palette required for paletted images"); |
||
209 | |||
210 | |||
211 | if (info_ptr->valid & PNG_INFO_tRNS) |
||
212 | { |
||
213 | #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED |
||
214 | /* Invert the alpha channel (in tRNS) */ |
||
215 | if ((png_ptr->transformations & PNG_INVERT_ALPHA) && |
||
216 | info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) |
||
217 | { |
||
218 | int j; |
||
219 | for (j = 0; j<(int)info_ptr->num_trans; j++) |
||
220 | info_ptr->trans_alpha[j] = |
||
221 | (png_byte)(255 - info_ptr->trans_alpha[j]); |
||
222 | } |
||
223 | #endif |
||
224 | png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color), |
||
225 | info_ptr->num_trans, info_ptr->color_type); |
||
226 | } |
||
227 | #endif |
||
228 | #ifdef PNG_WRITE_bKGD_SUPPORTED |
||
229 | if (info_ptr->valid & PNG_INFO_bKGD) |
||
230 | png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); |
||
231 | #endif |
||
232 | |||
233 | |||
234 | if (info_ptr->valid & PNG_INFO_hIST) |
||
235 | png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); |
||
236 | #endif |
||
237 | |||
238 | |||
239 | if (info_ptr->valid & PNG_INFO_oFFs) |
||
240 | png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, |
||
241 | info_ptr->offset_unit_type); |
||
242 | #endif |
||
243 | |||
244 | |||
245 | if (info_ptr->valid & PNG_INFO_pCAL) |
||
246 | png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, |
||
247 | info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, |
||
248 | info_ptr->pcal_units, info_ptr->pcal_params); |
||
249 | #endif |
||
250 | |||
251 | |||
252 | if (info_ptr->valid & PNG_INFO_sCAL) |
||
253 | png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, |
||
254 | info_ptr->scal_s_width, info_ptr->scal_s_height); |
||
255 | #endif /* sCAL */ |
||
256 | |||
257 | |||
258 | if (info_ptr->valid & PNG_INFO_pHYs) |
||
259 | png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, |
||
260 | info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); |
||
261 | #endif /* pHYs */ |
||
262 | |||
263 | |||
264 | if (info_ptr->valid & PNG_INFO_tIME) |
||
265 | { |
||
266 | png_write_tIME(png_ptr, &(info_ptr->mod_time)); |
||
267 | png_ptr->mode |= PNG_WROTE_tIME; |
||
268 | } |
||
269 | #endif /* tIME */ |
||
270 | |||
271 | |||
272 | if (info_ptr->valid & PNG_INFO_sPLT) |
||
273 | for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) |
||
274 | png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); |
||
275 | #endif /* sPLT */ |
||
276 | |||
277 | |||
278 | /* Check to see if we need to write text chunks */ |
||
279 | for (i = 0; i < info_ptr->num_text; i++) |
||
280 | { |
||
281 | png_debug2(2, "Writing header text chunk %d, type %d", i, |
||
282 | info_ptr->text[i].compression); |
||
283 | /* An internationalized chunk? */ |
||
284 | if (info_ptr->text[i].compression > 0) |
||
285 | { |
||
286 | #ifdef PNG_WRITE_iTXt_SUPPORTED |
||
287 | /* Write international chunk */ |
||
288 | png_write_iTXt(png_ptr, |
||
289 | info_ptr->text[i].compression, |
||
290 | info_ptr->text[i].key, |
||
291 | info_ptr->text[i].lang, |
||
292 | info_ptr->text[i].lang_key, |
||
293 | info_ptr->text[i].text); |
||
294 | #else |
||
295 | png_warning(png_ptr, "Unable to write international text"); |
||
296 | #endif |
||
297 | /* Mark this chunk as written */ |
||
298 | info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; |
||
299 | } |
||
300 | |||
301 | |||
302 | else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) |
||
303 | { |
||
304 | #ifdef PNG_WRITE_zTXt_SUPPORTED |
||
305 | /* Write compressed chunk */ |
||
306 | png_write_zTXt(png_ptr, info_ptr->text[i].key, |
||
307 | info_ptr->text[i].text, 0, |
||
308 | info_ptr->text[i].compression); |
||
309 | #else |
||
310 | png_warning(png_ptr, "Unable to write compressed text"); |
||
311 | #endif |
||
312 | /* Mark this chunk as written */ |
||
313 | info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; |
||
314 | } |
||
315 | |||
316 | |||
317 | { |
||
318 | #ifdef PNG_WRITE_tEXt_SUPPORTED |
||
319 | /* Write uncompressed chunk */ |
||
320 | png_write_tEXt(png_ptr, info_ptr->text[i].key, |
||
321 | info_ptr->text[i].text, |
||
322 | 0); |
||
323 | /* Mark this chunk as written */ |
||
324 | info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; |
||
325 | #else |
||
326 | /* Can't get here */ |
||
327 | png_warning(png_ptr, "Unable to write uncompressed text"); |
||
328 | #endif |
||
329 | } |
||
330 | } |
||
331 | #endif /* tEXt */ |
||
332 | |||
333 | |||
334 | write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE); |
||
335 | #endif |
||
336 | } |
||
337 | |||
338 | |||
339 | * time information, you can pass NULL for info. If you already wrote these |
||
340 | * in png_write_info(), do not write them again here. If you have long |
||
341 | * comments, I suggest writing them here, and compressing them. |
||
342 | */ |
||
343 | void PNGAPI |
||
344 | png_write_end(png_structrp png_ptr, png_inforp info_ptr) |
||
345 | { |
||
346 | png_debug(1, "in png_write_end"); |
||
347 | |||
348 | |||
349 | return; |
||
350 | |||
351 | |||
352 | png_error(png_ptr, "No IDATs written into file"); |
||
353 | |||
354 | |||
355 | if (png_ptr->num_palette_max > png_ptr->num_palette) |
||
356 | png_benign_error(png_ptr, "Wrote palette index exceeding num_palette"); |
||
357 | #endif |
||
358 | |||
359 | |||
360 | if (info_ptr != NULL) |
||
361 | { |
||
362 | #ifdef PNG_WRITE_TEXT_SUPPORTED |
||
363 | int i; /* local index variable */ |
||
364 | #endif |
||
365 | #ifdef PNG_WRITE_tIME_SUPPORTED |
||
366 | /* Check to see if user has supplied a time chunk */ |
||
367 | if ((info_ptr->valid & PNG_INFO_tIME) && |
||
368 | !(png_ptr->mode & PNG_WROTE_tIME)) |
||
369 | png_write_tIME(png_ptr, &(info_ptr->mod_time)); |
||
370 | |||
371 | |||
372 | #ifdef PNG_WRITE_TEXT_SUPPORTED |
||
373 | /* Loop through comment chunks */ |
||
374 | for (i = 0; i < info_ptr->num_text; i++) |
||
375 | { |
||
376 | png_debug2(2, "Writing trailer text chunk %d, type %d", i, |
||
377 | info_ptr->text[i].compression); |
||
378 | /* An internationalized chunk? */ |
||
379 | if (info_ptr->text[i].compression > 0) |
||
380 | { |
||
381 | #ifdef PNG_WRITE_iTXt_SUPPORTED |
||
382 | /* Write international chunk */ |
||
383 | png_write_iTXt(png_ptr, |
||
384 | info_ptr->text[i].compression, |
||
385 | info_ptr->text[i].key, |
||
386 | info_ptr->text[i].lang, |
||
387 | info_ptr->text[i].lang_key, |
||
388 | info_ptr->text[i].text); |
||
389 | #else |
||
390 | png_warning(png_ptr, "Unable to write international text"); |
||
391 | #endif |
||
392 | /* Mark this chunk as written */ |
||
393 | info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; |
||
394 | } |
||
395 | |||
396 | |||
397 | { |
||
398 | #ifdef PNG_WRITE_zTXt_SUPPORTED |
||
399 | /* Write compressed chunk */ |
||
400 | png_write_zTXt(png_ptr, info_ptr->text[i].key, |
||
401 | info_ptr->text[i].text, 0, |
||
402 | info_ptr->text[i].compression); |
||
403 | #else |
||
404 | png_warning(png_ptr, "Unable to write compressed text"); |
||
405 | #endif |
||
406 | /* Mark this chunk as written */ |
||
407 | info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; |
||
408 | } |
||
409 | |||
410 | |||
411 | { |
||
412 | #ifdef PNG_WRITE_tEXt_SUPPORTED |
||
413 | /* Write uncompressed chunk */ |
||
414 | png_write_tEXt(png_ptr, info_ptr->text[i].key, |
||
415 | info_ptr->text[i].text, 0); |
||
416 | #else |
||
417 | png_warning(png_ptr, "Unable to write uncompressed text"); |
||
418 | #endif |
||
419 | |||
420 | |||
421 | info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; |
||
422 | } |
||
423 | } |
||
424 | #endif |
||
425 | #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED |
||
426 | write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT); |
||
427 | #endif |
||
428 | } |
||
429 | |||
430 | |||
431 | |||
432 | |||
433 | png_write_IEND(png_ptr); |
||
434 | /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, |
||
435 | * and restored again in libpng-1.2.30, may cause some applications that |
||
436 | * do not set png_ptr->output_flush_fn to crash. If your application |
||
437 | * experiences a problem, please try building libpng with |
||
438 | * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to |
||
439 | * png-mng-implement at lists.sf.net . |
||
440 | */ |
||
441 | #ifdef PNG_WRITE_FLUSH_SUPPORTED |
||
442 | # ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED |
||
443 | png_flush(png_ptr); |
||
444 | # endif |
||
445 | #endif |
||
446 | } |
||
447 | |||
448 | |||
449 | void PNGAPI |
||
450 | png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime) |
||
451 | { |
||
452 | png_debug(1, "in png_convert_from_struct_tm"); |
||
453 | |||
454 | |||
455 | ptime->month = (png_byte)(ttime->tm_mon + 1); |
||
456 | ptime->day = (png_byte)ttime->tm_mday; |
||
457 | ptime->hour = (png_byte)ttime->tm_hour; |
||
458 | ptime->minute = (png_byte)ttime->tm_min; |
||
459 | ptime->second = (png_byte)ttime->tm_sec; |
||
460 | } |
||
461 | |||
462 | |||
463 | png_convert_from_time_t(png_timep ptime, time_t ttime) |
||
464 | { |
||
465 | struct tm *tbuf; |
||
466 | |||
467 | |||
468 | |||
469 | |||
470 | png_convert_from_struct_tm(ptime, tbuf); |
||
471 | } |
||
472 | #endif |
||
473 | |||
474 | |||
475 | PNG_FUNCTION(png_structp,PNGAPI |
||
476 | png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr, |
||
477 | png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED) |
||
478 | { |
||
479 | #ifndef PNG_USER_MEM_SUPPORTED |
||
480 | png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr, |
||
481 | error_fn, warn_fn, NULL, NULL, NULL); |
||
482 | #else |
||
483 | return png_create_write_struct_2(user_png_ver, error_ptr, error_fn, |
||
484 | warn_fn, NULL, NULL, NULL); |
||
485 | } |
||
486 | |||
487 | |||
488 | PNG_FUNCTION(png_structp,PNGAPI |
||
489 | png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, |
||
490 | png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, |
||
491 | png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) |
||
492 | { |
||
493 | png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr, |
||
494 | error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); |
||
495 | #endif /* PNG_USER_MEM_SUPPORTED */ |
||
496 | if (png_ptr != NULL) |
||
497 | { |
||
498 | /* Set the zlib control values to defaults; they can be overridden by the |
||
499 | * application after the struct has been created. |
||
500 | */ |
||
501 | png_ptr->zbuffer_size = PNG_ZBUF_SIZE; |
||
502 | |||
503 | |||
504 | * pngwutil.c defaults it according to whether or not filters will be |
||
505 | * used, and ignores this setting. |
||
506 | */ |
||
507 | png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY; |
||
508 | png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION; |
||
509 | png_ptr->zlib_mem_level = 8; |
||
510 | png_ptr->zlib_window_bits = 15; |
||
511 | png_ptr->zlib_method = 8; |
||
512 | |||
513 | |||
514 | png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY; |
||
515 | png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION; |
||
516 | png_ptr->zlib_text_mem_level = 8; |
||
517 | png_ptr->zlib_text_window_bits = 15; |
||
518 | png_ptr->zlib_text_method = 8; |
||
519 | #endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */ |
||
520 | |||
521 | |||
522 | * but it may be appropriate for private builds that are testing |
||
523 | * extensions not conformant to the current specification, or of |
||
524 | * applications that must not fail to write at all costs! |
||
525 | */ |
||
526 | #ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED |
||
527 | png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; |
||
528 | /* In stable builds only warn if an application error can be completely |
||
529 | * handled. |
||
530 | */ |
||
531 | #endif |
||
532 | |||
533 | |||
534 | * are errors during development. |
||
535 | */ |
||
536 | #if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC |
||
537 | png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; |
||
538 | #endif |
||
539 | |||
540 | |||
541 | * do it itself) avoiding setting the default function if it is not |
||
542 | * required. |
||
543 | */ |
||
544 | png_set_write_fn(png_ptr, NULL, NULL, NULL); |
||
545 | } |
||
546 | |||
547 | |||
548 | } |
||
549 | |||
550 | |||
551 | |||
552 | * either you will have to write the 7 sub images, or, if you |
||
553 | * have called png_set_interlace_handling(), you will have to |
||
554 | * "write" the image seven times. |
||
555 | */ |
||
556 | void PNGAPI |
||
557 | png_write_rows(png_structrp png_ptr, png_bytepp row, |
||
558 | png_uint_32 num_rows) |
||
559 | { |
||
560 | png_uint_32 i; /* row counter */ |
||
561 | png_bytepp rp; /* row pointer */ |
||
562 | |||
563 | |||
564 | |||
565 | |||
566 | return; |
||
567 | |||
568 | |||
569 | for (i = 0, rp = row; i < num_rows; i++, rp++) |
||
570 | { |
||
571 | png_write_row(png_ptr, *rp); |
||
572 | } |
||
573 | } |
||
574 | |||
575 | |||
576 | * if you are writing an interlaced image. |
||
577 | */ |
||
578 | void PNGAPI |
||
579 | png_write_image(png_structrp png_ptr, png_bytepp image) |
||
580 | { |
||
581 | png_uint_32 i; /* row index */ |
||
582 | int pass, num_pass; /* pass variables */ |
||
583 | png_bytepp rp; /* points to current row */ |
||
584 | |||
585 | |||
586 | return; |
||
587 | |||
588 | |||
589 | |||
590 | |||
591 | /* Initialize interlace handling. If image is not interlaced, |
||
592 | * this will set pass to 1 |
||
593 | */ |
||
594 | num_pass = png_set_interlace_handling(png_ptr); |
||
595 | #else |
||
596 | num_pass = 1; |
||
597 | #endif |
||
598 | /* Loop through passes */ |
||
599 | for (pass = 0; pass < num_pass; pass++) |
||
600 | { |
||
601 | /* Loop through image */ |
||
602 | for (i = 0, rp = image; i < png_ptr->height; i++, rp++) |
||
603 | { |
||
604 | png_write_row(png_ptr, *rp); |
||
605 | } |
||
606 | } |
||
607 | } |
||
608 | |||
609 | |||
610 | void PNGAPI |
||
611 | png_write_row(png_structrp png_ptr, png_const_bytep row) |
||
612 | { |
||
613 | /* 1.5.6: moved from png_struct to be a local structure: */ |
||
614 | png_row_info row_info; |
||
615 | |||
616 | |||
617 | return; |
||
618 | |||
619 | |||
620 | png_ptr->row_number, png_ptr->pass); |
||
621 | |||
622 | |||
623 | if (png_ptr->row_number == 0 && png_ptr->pass == 0) |
||
624 | { |
||
625 | /* Make sure we wrote the header info */ |
||
626 | if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) |
||
627 | png_error(png_ptr, |
||
628 | "png_write_info was never called before png_write_row"); |
||
629 | |||
630 | |||
631 | #if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) |
||
632 | if (png_ptr->transformations & PNG_INVERT_MONO) |
||
633 | png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined"); |
||
634 | #endif |
||
635 | |||
636 | |||
637 | if (png_ptr->transformations & PNG_FILLER) |
||
638 | png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined"); |
||
639 | #endif |
||
640 | #if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ |
||
641 | defined(PNG_READ_PACKSWAP_SUPPORTED) |
||
642 | if (png_ptr->transformations & PNG_PACKSWAP) |
||
643 | png_warning(png_ptr, |
||
644 | "PNG_WRITE_PACKSWAP_SUPPORTED is not defined"); |
||
645 | #endif |
||
646 | |||
647 | |||
648 | if (png_ptr->transformations & PNG_PACK) |
||
649 | png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined"); |
||
650 | #endif |
||
651 | |||
652 | |||
653 | if (png_ptr->transformations & PNG_SHIFT) |
||
654 | png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined"); |
||
655 | #endif |
||
656 | |||
657 | |||
658 | if (png_ptr->transformations & PNG_BGR) |
||
659 | png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined"); |
||
660 | #endif |
||
661 | |||
662 | |||
663 | if (png_ptr->transformations & PNG_SWAP_BYTES) |
||
664 | png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined"); |
||
665 | #endif |
||
666 | |||
667 | |||
668 | } |
||
669 | |||
670 | |||
671 | /* If interlaced and not interested in row, return */ |
||
672 | if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) |
||
673 | { |
||
674 | switch (png_ptr->pass) |
||
675 | { |
||
676 | case 0: |
||
677 | if (png_ptr->row_number & 0x07) |
||
678 | { |
||
679 | png_write_finish_row(png_ptr); |
||
680 | return; |
||
681 | } |
||
682 | break; |
||
683 | |||
684 | |||
685 | if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) |
||
686 | { |
||
687 | png_write_finish_row(png_ptr); |
||
688 | return; |
||
689 | } |
||
690 | break; |
||
691 | |||
692 | |||
693 | if ((png_ptr->row_number & 0x07) != 4) |
||
694 | { |
||
695 | png_write_finish_row(png_ptr); |
||
696 | return; |
||
697 | } |
||
698 | break; |
||
699 | |||
700 | |||
701 | if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) |
||
702 | { |
||
703 | png_write_finish_row(png_ptr); |
||
704 | return; |
||
705 | } |
||
706 | break; |
||
707 | |||
708 | |||
709 | if ((png_ptr->row_number & 0x03) != 2) |
||
710 | { |
||
711 | png_write_finish_row(png_ptr); |
||
712 | return; |
||
713 | } |
||
714 | break; |
||
715 | |||
716 | |||
717 | if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) |
||
718 | { |
||
719 | png_write_finish_row(png_ptr); |
||
720 | return; |
||
721 | } |
||
722 | break; |
||
723 | |||
724 | |||
725 | if (!(png_ptr->row_number & 0x01)) |
||
726 | { |
||
727 | png_write_finish_row(png_ptr); |
||
728 | return; |
||
729 | } |
||
730 | break; |
||
731 | |||
732 | |||
733 | break; |
||
734 | } |
||
735 | } |
||
736 | #endif |
||
737 | |||
738 | |||
739 | row_info.color_type = png_ptr->color_type; |
||
740 | row_info.width = png_ptr->usr_width; |
||
741 | row_info.channels = png_ptr->usr_channels; |
||
742 | row_info.bit_depth = png_ptr->usr_bit_depth; |
||
743 | row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels); |
||
744 | row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); |
||
745 | |||
746 | |||
747 | png_debug1(3, "row_info->width = %u", row_info.width); |
||
748 | png_debug1(3, "row_info->channels = %d", row_info.channels); |
||
749 | png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth); |
||
750 | png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth); |
||
751 | png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes); |
||
752 | |||
753 | |||
754 | memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes); |
||
755 | |||
756 | |||
757 | /* Handle interlacing */ |
||
758 | if (png_ptr->interlaced && png_ptr->pass < 6 && |
||
759 | (png_ptr->transformations & PNG_INTERLACE)) |
||
760 | { |
||
761 | png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass); |
||
762 | /* This should always get caught above, but still ... */ |
||
763 | if (!(row_info.width)) |
||
764 | { |
||
765 | png_write_finish_row(png_ptr); |
||
766 | return; |
||
767 | } |
||
768 | } |
||
769 | #endif |
||
770 | |||
771 | |||
772 | /* Handle other transformations */ |
||
773 | if (png_ptr->transformations) |
||
774 | png_do_write_transformations(png_ptr, &row_info); |
||
775 | #endif |
||
776 | |||
777 | |||
778 | * which is also the output depth. |
||
779 | */ |
||
780 | if (row_info.pixel_depth != png_ptr->pixel_depth || |
||
781 | row_info.pixel_depth != png_ptr->transformed_pixel_depth) |
||
782 | png_error(png_ptr, "internal write transform logic error"); |
||
783 | |||
784 | |||
785 | /* Write filter_method 64 (intrapixel differencing) only if |
||
786 | * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and |
||
787 | * 2. Libpng did not write a PNG signature (this filter_method is only |
||
788 | * used in PNG datastreams that are embedded in MNG datastreams) and |
||
789 | * 3. The application called png_permit_mng_features with a mask that |
||
790 | * included PNG_FLAG_MNG_FILTER_64 and |
||
791 | * 4. The filter_method is 64 and |
||
792 | * 5. The color_type is RGB or RGBA |
||
793 | */ |
||
794 | if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && |
||
795 | (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) |
||
796 | { |
||
797 | /* Intrapixel differencing */ |
||
798 | png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1); |
||
799 | } |
||
800 | #endif |
||
801 | |||
802 | |||
803 | #ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED |
||
804 | /* Check for out-of-range palette index */ |
||
805 | if (row_info.color_type == PNG_COLOR_TYPE_PALETTE && |
||
806 | png_ptr->num_palette_max >= 0) |
||
807 | png_do_check_palette_indexes(png_ptr, &row_info); |
||
808 | #endif |
||
809 | |||
810 | |||
811 | png_write_find_filter(png_ptr, &row_info); |
||
812 | |||
813 | |||
814 | (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); |
||
815 | } |
||
816 | |||
817 | |||
818 | /* Set the automatic flush interval or 0 to turn flushing off */ |
||
819 | void PNGAPI |
||
820 | png_set_flush(png_structrp png_ptr, int nrows) |
||
821 | { |
||
822 | png_debug(1, "in png_set_flush"); |
||
823 | |||
824 | |||
825 | return; |
||
826 | |||
827 | |||
828 | } |
||
829 | |||
830 | |||
831 | void PNGAPI |
||
832 | png_write_flush(png_structrp png_ptr) |
||
833 | { |
||
834 | png_debug(1, "in png_write_flush"); |
||
835 | |||
836 | |||
837 | return; |
||
838 | |||
839 | |||
840 | if (png_ptr->row_number >= png_ptr->num_rows) |
||
841 | return; |
||
842 | |||
843 | |||
844 | png_ptr->flush_rows = 0; |
||
845 | png_flush(png_ptr); |
||
846 | } |
||
847 | #endif /* PNG_WRITE_FLUSH_SUPPORTED */ |
||
848 | |||
849 | |||
850 | static void png_reset_filter_heuristics(png_structrp png_ptr);/* forward decl */ |
||
851 | #endif |
||
852 | |||
853 | |||
854 | static void |
||
855 | png_write_destroy(png_structrp png_ptr) |
||
856 | { |
||
857 | png_debug(1, "in png_write_destroy"); |
||
858 | |||
859 | |||
860 | if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) |
||
861 | deflateEnd(&png_ptr->zstream); |
||
862 | |||
863 | |||
864 | png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); |
||
865 | png_free(png_ptr, png_ptr->row_buf); |
||
866 | #ifdef PNG_WRITE_FILTER_SUPPORTED |
||
867 | png_free(png_ptr, png_ptr->prev_row); |
||
868 | png_free(png_ptr, png_ptr->sub_row); |
||
869 | png_free(png_ptr, png_ptr->up_row); |
||
870 | png_free(png_ptr, png_ptr->avg_row); |
||
871 | png_free(png_ptr, png_ptr->paeth_row); |
||
872 | #endif |
||
873 | |||
874 | |||
875 | /* Use this to save a little code space, it doesn't free the filter_costs */ |
||
876 | png_reset_filter_heuristics(png_ptr); |
||
877 | png_free(png_ptr, png_ptr->filter_costs); |
||
878 | png_free(png_ptr, png_ptr->inv_filter_costs); |
||
879 | #endif |
||
880 | |||
881 | |||
882 | png_free(png_ptr, png_ptr->chunk_list); |
||
883 | #endif |
||
884 | |||
885 | |||
886 | * point: the jmp_buf may still have to be freed. See png_destroy_png_struct |
||
887 | * for how this happens. |
||
888 | */ |
||
889 | } |
||
890 | |||
891 | |||
892 | * In libpng 1.6.0 this API changed quietly to no longer accept a NULL value for |
||
893 | * *png_ptr_ptr. Prior to 1.6.0 it would accept such a value and it would free |
||
894 | * the passed in info_structs but it would quietly fail to free any of the data |
||
895 | * inside them. In 1.6.0 it quietly does nothing (it has to be quiet because it |
||
896 | * has no png_ptr.) |
||
897 | */ |
||
898 | void PNGAPI |
||
899 | png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) |
||
900 | { |
||
901 | png_debug(1, "in png_destroy_write_struct"); |
||
902 | |||
903 | |||
904 | { |
||
905 | png_structrp png_ptr = *png_ptr_ptr; |
||
906 | |||
907 | |||
908 | { |
||
909 | png_destroy_info_struct(png_ptr, info_ptr_ptr); |
||
910 | |||
911 | |||
912 | png_write_destroy(png_ptr); |
||
913 | png_destroy_png_struct(png_ptr); |
||
914 | } |
||
915 | } |
||
916 | } |
||
917 | |||
918 | |||
919 | void PNGAPI |
||
920 | png_set_filter(png_structrp png_ptr, int method, int filters) |
||
921 | { |
||
922 | png_debug(1, "in png_set_filter"); |
||
923 | |||
924 | |||
925 | return; |
||
926 | |||
927 | |||
928 | if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && |
||
929 | (method == PNG_INTRAPIXEL_DIFFERENCING)) |
||
930 | method = PNG_FILTER_TYPE_BASE; |
||
931 | |||
932 | |||
933 | if (method == PNG_FILTER_TYPE_BASE) |
||
934 | { |
||
935 | switch (filters & (PNG_ALL_FILTERS | 0x07)) |
||
936 | { |
||
937 | #ifdef PNG_WRITE_FILTER_SUPPORTED |
||
938 | case 5: |
||
939 | case 6: |
||
940 | case 7: png_app_error(png_ptr, "Unknown row filter for method 0"); |
||
941 | /* FALL THROUGH */ |
||
942 | #endif /* PNG_WRITE_FILTER_SUPPORTED */ |
||
943 | case PNG_FILTER_VALUE_NONE: |
||
944 | png_ptr->do_filter = PNG_FILTER_NONE; break; |
||
945 | |||
946 | |||
947 | case PNG_FILTER_VALUE_SUB: |
||
948 | png_ptr->do_filter = PNG_FILTER_SUB; break; |
||
949 | |||
950 | |||
951 | png_ptr->do_filter = PNG_FILTER_UP; break; |
||
952 | |||
953 | |||
954 | png_ptr->do_filter = PNG_FILTER_AVG; break; |
||
955 | |||
956 | |||
957 | png_ptr->do_filter = PNG_FILTER_PAETH; break; |
||
958 | |||
959 | |||
960 | png_ptr->do_filter = (png_byte)filters; break; |
||
961 | #else |
||
962 | default: |
||
963 | png_app_error(png_ptr, "Unknown row filter for method 0"); |
||
964 | #endif /* PNG_WRITE_FILTER_SUPPORTED */ |
||
965 | } |
||
966 | |||
967 | |||
968 | * with the image and we should have allocated all of the filter buffers |
||
969 | * that have been selected. If prev_row isn't already allocated, then |
||
970 | * it is too late to start using the filters that need it, since we |
||
971 | * will be missing the data in the previous row. If an application |
||
972 | * wants to start and stop using particular filters during compression, |
||
973 | * it should start out with all of the filters, and then add and |
||
974 | * remove them after the start of compression. |
||
975 | */ |
||
976 | if (png_ptr->row_buf != NULL) |
||
977 | { |
||
978 | #ifdef PNG_WRITE_FILTER_SUPPORTED |
||
979 | if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) |
||
980 | { |
||
981 | png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, |
||
982 | (png_ptr->rowbytes + 1)); |
||
983 | png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; |
||
984 | } |
||
985 | |||
986 | |||
987 | { |
||
988 | if (png_ptr->prev_row == NULL) |
||
989 | { |
||
990 | png_warning(png_ptr, "Can't add Up filter after starting"); |
||
991 | png_ptr->do_filter = (png_byte)(png_ptr->do_filter & |
||
992 | ~PNG_FILTER_UP); |
||
993 | } |
||
994 | |||
995 | |||
996 | { |
||
997 | png_ptr->up_row = (png_bytep)png_malloc(png_ptr, |
||
998 | (png_ptr->rowbytes + 1)); |
||
999 | png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; |
||
1000 | } |
||
1001 | } |
||
1002 | |||
1003 | |||
1004 | { |
||
1005 | if (png_ptr->prev_row == NULL) |
||
1006 | { |
||
1007 | png_warning(png_ptr, "Can't add Average filter after starting"); |
||
1008 | png_ptr->do_filter = (png_byte)(png_ptr->do_filter & |
||
1009 | ~PNG_FILTER_AVG); |
||
1010 | } |
||
1011 | |||
1012 | |||
1013 | { |
||
1014 | png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, |
||
1015 | (png_ptr->rowbytes + 1)); |
||
1016 | png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; |
||
1017 | } |
||
1018 | } |
||
1019 | |||
1020 | |||
1021 | png_ptr->paeth_row == NULL) |
||
1022 | { |
||
1023 | if (png_ptr->prev_row == NULL) |
||
1024 | { |
||
1025 | png_warning(png_ptr, "Can't add Paeth filter after starting"); |
||
1026 | png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); |
||
1027 | } |
||
1028 | |||
1029 | |||
1030 | { |
||
1031 | png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, |
||
1032 | (png_ptr->rowbytes + 1)); |
||
1033 | png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; |
||
1034 | } |
||
1035 | } |
||
1036 | |||
1037 | |||
1038 | #endif /* PNG_WRITE_FILTER_SUPPORTED */ |
||
1039 | png_ptr->do_filter = PNG_FILTER_NONE; |
||
1040 | } |
||
1041 | } |
||
1042 | else |
||
1043 | png_error(png_ptr, "Unknown custom filter method"); |
||
1044 | } |
||
1045 | |||
1046 | |||
1047 | * filter for the current scanline. While the "minimum-sum-of-absolute- |
||
1048 | * differences metric is relatively fast and effective, there is some |
||
1049 | * question as to whether it can be improved upon by trying to keep the |
||
1050 | * filtered data going to zlib more consistent, hopefully resulting in |
||
1051 | * better compression. |
||
1052 | */ |
||
1053 | #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ |
||
1054 | /* Convenience reset API. */ |
||
1055 | static void |
||
1056 | png_reset_filter_heuristics(png_structrp png_ptr) |
||
1057 | { |
||
1058 | /* Clear out any old values in the 'weights' - this must be done because if |
||
1059 | * the app calls set_filter_heuristics multiple times with different |
||
1060 | * 'num_weights' values we would otherwise potentially have wrong sized |
||
1061 | * arrays. |
||
1062 | */ |
||
1063 | png_ptr->num_prev_filters = 0; |
||
1064 | png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; |
||
1065 | if (png_ptr->prev_filters != NULL) |
||
1066 | { |
||
1067 | png_bytep old = png_ptr->prev_filters; |
||
1068 | png_ptr->prev_filters = NULL; |
||
1069 | png_free(png_ptr, old); |
||
1070 | } |
||
1071 | if (png_ptr->filter_weights != NULL) |
||
1072 | { |
||
1073 | png_uint_16p old = png_ptr->filter_weights; |
||
1074 | png_ptr->filter_weights = NULL; |
||
1075 | png_free(png_ptr, old); |
||
1076 | } |
||
1077 | |||
1078 | |||
1079 | { |
||
1080 | png_uint_16p old = png_ptr->inv_filter_weights; |
||
1081 | png_ptr->inv_filter_weights = NULL; |
||
1082 | png_free(png_ptr, old); |
||
1083 | } |
||
1084 | |||
1085 | |||
1086 | } |
||
1087 | |||
1088 | |||
1089 | png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method, |
||
1090 | int num_weights) |
||
1091 | { |
||
1092 | if (png_ptr == NULL) |
||
1093 | return 0; |
||
1094 | |||
1095 | |||
1096 | png_reset_filter_heuristics(png_ptr); |
||
1097 | |||
1098 | |||
1099 | * unweighted case, but we must handle the weight case by initializing the |
||
1100 | * arrays for the caller. |
||
1101 | */ |
||
1102 | if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) |
||
1103 | { |
||
1104 | int i; |
||
1105 | |||
1106 | |||
1107 | { |
||
1108 | png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, |
||
1109 | (png_uint_32)((sizeof (png_byte)) * num_weights)); |
||
1110 | |||
1111 | |||
1112 | for (i = 0; i < num_weights; i++) |
||
1113 | { |
||
1114 | png_ptr->prev_filters[i] = 255; |
||
1115 | } |
||
1116 | |||
1117 | |||
1118 | (png_uint_32)((sizeof (png_uint_16)) * num_weights)); |
||
1119 | |||
1120 | |||
1121 | (png_uint_32)((sizeof (png_uint_16)) * num_weights)); |
||
1122 | |||
1123 | |||
1124 | { |
||
1125 | png_ptr->inv_filter_weights[i] = |
||
1126 | png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; |
||
1127 | } |
||
1128 | |||
1129 | |||
1130 | png_ptr->num_prev_filters = (png_byte)num_weights; |
||
1131 | } |
||
1132 | |||
1133 | |||
1134 | * need to be based on png_ptr->filter. |
||
1135 | */ |
||
1136 | if (png_ptr->filter_costs == NULL) |
||
1137 | { |
||
1138 | png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, |
||
1139 | (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST)); |
||
1140 | |||
1141 | |||
1142 | (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST)); |
||
1143 | } |
||
1144 | |||
1145 | |||
1146 | { |
||
1147 | png_ptr->inv_filter_costs[i] = |
||
1148 | png_ptr->filter_costs[i] = PNG_COST_FACTOR; |
||
1149 | } |
||
1150 | |||
1151 | |||
1152 | png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_WEIGHTED; |
||
1153 | |||
1154 | |||
1155 | return 1; |
||
1156 | } |
||
1157 | else if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT || |
||
1158 | heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) |
||
1159 | { |
||
1160 | return 1; |
||
1161 | } |
||
1162 | else |
||
1163 | { |
||
1164 | png_warning(png_ptr, "Unknown filter heuristic method"); |
||
1165 | return 0; |
||
1166 | } |
||
1167 | } |
||
1168 | |||
1169 | |||
1170 | #ifdef PNG_FLOATING_POINT_SUPPORTED |
||
1171 | void PNGAPI |
||
1172 | png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method, |
||
1173 | int num_weights, png_const_doublep filter_weights, |
||
1174 | png_const_doublep filter_costs) |
||
1175 | { |
||
1176 | png_debug(1, "in png_set_filter_heuristics"); |
||
1177 | |||
1178 | |||
1179 | * those arrays are set to the default value. |
||
1180 | */ |
||
1181 | if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights)) |
||
1182 | return; |
||
1183 | |||
1184 | |||
1185 | if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) |
||
1186 | { |
||
1187 | int i; |
||
1188 | for (i = 0; i < num_weights; i++) |
||
1189 | { |
||
1190 | if (filter_weights[i] <= 0.0) |
||
1191 | { |
||
1192 | png_ptr->inv_filter_weights[i] = |
||
1193 | png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; |
||
1194 | } |
||
1195 | |||
1196 | |||
1197 | { |
||
1198 | png_ptr->inv_filter_weights[i] = |
||
1199 | (png_uint_16)(PNG_WEIGHT_FACTOR*filter_weights[i]+.5); |
||
1200 | |||
1201 | |||
1202 | (png_uint_16)(PNG_WEIGHT_FACTOR/filter_weights[i]+.5); |
||
1203 | } |
||
1204 | } |
||
1205 | |||
1206 | |||
1207 | * should take the desired compression level into account when setting |
||
1208 | * the costs, so that Paeth, for instance, has a high relative cost at low |
||
1209 | * compression levels, while it has a lower relative cost at higher |
||
1210 | * compression settings. The filter types are in order of increasing |
||
1211 | * relative cost, so it would be possible to do this with an algorithm. |
||
1212 | */ |
||
1213 | for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) if (filter_costs[i] >= 1.0) |
||
1214 | { |
||
1215 | png_ptr->inv_filter_costs[i] = |
||
1216 | (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5); |
||
1217 | |||
1218 | |||
1219 | (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5); |
||
1220 | } |
||
1221 | } |
||
1222 | } |
||
1223 | #endif /* FLOATING_POINT */ |
||
1224 | |||
1225 | |||
1226 | void PNGAPI |
||
1227 | png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method, |
||
1228 | int num_weights, png_const_fixed_point_p filter_weights, |
||
1229 | png_const_fixed_point_p filter_costs) |
||
1230 | { |
||
1231 | png_debug(1, "in png_set_filter_heuristics_fixed"); |
||
1232 | |||
1233 | |||
1234 | * those arrays are set to the default value. |
||
1235 | */ |
||
1236 | if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights)) |
||
1237 | return; |
||
1238 | |||
1239 | |||
1240 | if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) |
||
1241 | { |
||
1242 | int i; |
||
1243 | for (i = 0; i < num_weights; i++) |
||
1244 | { |
||
1245 | if (filter_weights[i] <= 0) |
||
1246 | { |
||
1247 | png_ptr->inv_filter_weights[i] = |
||
1248 | png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; |
||
1249 | } |
||
1250 | |||
1251 | |||
1252 | { |
||
1253 | png_ptr->inv_filter_weights[i] = (png_uint_16) |
||
1254 | ((PNG_WEIGHT_FACTOR*filter_weights[i]+PNG_FP_HALF)/PNG_FP_1); |
||
1255 | |||
1256 | |||
1257 | PNG_FP_1+(filter_weights[i]/2))/filter_weights[i]); |
||
1258 | } |
||
1259 | } |
||
1260 | |||
1261 | |||
1262 | * should take the desired compression level into account when setting |
||
1263 | * the costs, so that Paeth, for instance, has a high relative cost at low |
||
1264 | * compression levels, while it has a lower relative cost at higher |
||
1265 | * compression settings. The filter types are in order of increasing |
||
1266 | * relative cost, so it would be possible to do this with an algorithm. |
||
1267 | */ |
||
1268 | for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) |
||
1269 | if (filter_costs[i] >= PNG_FP_1) |
||
1270 | { |
||
1271 | png_uint_32 tmp; |
||
1272 | |||
1273 | |||
1274 | * intermediate value will be a 32 bit *signed* integer (ANSI rules) |
||
1275 | * and this will get the wrong answer on division. |
||
1276 | */ |
||
1277 | tmp = PNG_COST_FACTOR*PNG_FP_1 + (filter_costs[i]/2); |
||
1278 | tmp /= filter_costs[i]; |
||
1279 | |||
1280 | |||
1281 | |||
1282 | |||
1283 | tmp /= PNG_FP_1; |
||
1284 | |||
1285 | |||
1286 | } |
||
1287 | } |
||
1288 | } |
||
1289 | #endif /* FIXED_POINT */ |
||
1290 | #endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ |
||
1291 | |||
1292 | |||
1293 | png_set_compression_level(png_structrp png_ptr, int level) |
||
1294 | { |
||
1295 | png_debug(1, "in png_set_compression_level"); |
||
1296 | |||
1297 | |||
1298 | return; |
||
1299 | |||
1300 | |||
1301 | } |
||
1302 | |||
1303 | |||
1304 | png_set_compression_mem_level(png_structrp png_ptr, int mem_level) |
||
1305 | { |
||
1306 | png_debug(1, "in png_set_compression_mem_level"); |
||
1307 | |||
1308 | |||
1309 | return; |
||
1310 | |||
1311 | |||
1312 | } |
||
1313 | |||
1314 | |||
1315 | png_set_compression_strategy(png_structrp png_ptr, int strategy) |
||
1316 | { |
||
1317 | png_debug(1, "in png_set_compression_strategy"); |
||
1318 | |||
1319 | |||
1320 | return; |
||
1321 | |||
1322 | |||
1323 | */ |
||
1324 | png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; |
||
1325 | png_ptr->zlib_strategy = strategy; |
||
1326 | } |
||
1327 | |||
1328 | |||
1329 | * smaller value of window_bits if it can do so safely. |
||
1330 | */ |
||
1331 | void PNGAPI |
||
1332 | png_set_compression_window_bits(png_structrp png_ptr, int window_bits) |
||
1333 | { |
||
1334 | if (png_ptr == NULL) |
||
1335 | return; |
||
1336 | |||
1337 | |||
1338 | * meant that negative window bits values could be selected which would cause |
||
1339 | * libpng to write a non-standard PNG file with raw deflate or gzip |
||
1340 | * compressed IDAT or ancillary chunks. Such files can be read and there is |
||
1341 | * no warning on read, so this seems like a very bad idea. |
||
1342 | */ |
||
1343 | if (window_bits > 15) |
||
1344 | { |
||
1345 | png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); |
||
1346 | window_bits = 15; |
||
1347 | } |
||
1348 | |||
1349 | |||
1350 | { |
||
1351 | png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); |
||
1352 | window_bits = 8; |
||
1353 | } |
||
1354 | |||
1355 | |||
1356 | } |
||
1357 | |||
1358 | |||
1359 | png_set_compression_method(png_structrp png_ptr, int method) |
||
1360 | { |
||
1361 | png_debug(1, "in png_set_compression_method"); |
||
1362 | |||
1363 | |||
1364 | return; |
||
1365 | |||
1366 | |||
1367 | * deflate will fault it, so it is harmless to just warn here. |
||
1368 | */ |
||
1369 | if (method != 8) |
||
1370 | png_warning(png_ptr, "Only compression method 8 is supported by PNG"); |
||
1371 | |||
1372 | |||
1373 | } |
||
1374 | |||
1375 | |||
1376 | #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED |
||
1377 | void PNGAPI |
||
1378 | png_set_text_compression_level(png_structrp png_ptr, int level) |
||
1379 | { |
||
1380 | png_debug(1, "in png_set_text_compression_level"); |
||
1381 | |||
1382 | |||
1383 | return; |
||
1384 | |||
1385 | |||
1386 | } |
||
1387 | |||
1388 | |||
1389 | png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level) |
||
1390 | { |
||
1391 | png_debug(1, "in png_set_text_compression_mem_level"); |
||
1392 | |||
1393 | |||
1394 | return; |
||
1395 | |||
1396 | |||
1397 | } |
||
1398 | |||
1399 | |||
1400 | png_set_text_compression_strategy(png_structrp png_ptr, int strategy) |
||
1401 | { |
||
1402 | png_debug(1, "in png_set_text_compression_strategy"); |
||
1403 | |||
1404 | |||
1405 | return; |
||
1406 | |||
1407 | |||
1408 | } |
||
1409 | |||
1410 | |||
1411 | * smaller value of window_bits if it can do so safely. |
||
1412 | */ |
||
1413 | void PNGAPI |
||
1414 | png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits) |
||
1415 | { |
||
1416 | if (png_ptr == NULL) |
||
1417 | return; |
||
1418 | |||
1419 | |||
1420 | { |
||
1421 | png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); |
||
1422 | window_bits = 15; |
||
1423 | } |
||
1424 | |||
1425 | |||
1426 | { |
||
1427 | png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); |
||
1428 | window_bits = 8; |
||
1429 | } |
||
1430 | |||
1431 | |||
1432 | } |
||
1433 | |||
1434 | |||
1435 | png_set_text_compression_method(png_structrp png_ptr, int method) |
||
1436 | { |
||
1437 | png_debug(1, "in png_set_text_compression_method"); |
||
1438 | |||
1439 | |||
1440 | return; |
||
1441 | |||
1442 | |||
1443 | png_warning(png_ptr, "Only compression method 8 is supported by PNG"); |
||
1444 | |||
1445 | |||
1446 | } |
||
1447 | #endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */ |
||
1448 | /* end of API added to libpng-1.5.4 */ |
||
1449 | |||
1450 | |||
1451 | png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn) |
||
1452 | { |
||
1453 | if (png_ptr == NULL) |
||
1454 | return; |
||
1455 | |||
1456 | |||
1457 | } |
||
1458 | |||
1459 | |||
1460 | void PNGAPI |
||
1461 | png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr |
||
1462 | write_user_transform_fn) |
||
1463 | { |
||
1464 | png_debug(1, "in png_set_write_user_transform_fn"); |
||
1465 | |||
1466 | |||
1467 | return; |
||
1468 | |||
1469 | |||
1470 | png_ptr->write_user_transform_fn = write_user_transform_fn; |
||
1471 | } |
||
1472 | #endif |
||
1473 | |||
1474 | |||
1475 | |||
1476 | void PNGAPI |
||
1477 | png_write_png(png_structrp png_ptr, png_inforp info_ptr, |
||
1478 | int transforms, voidp params) |
||
1479 | { |
||
1480 | if (png_ptr == NULL || info_ptr == NULL) |
||
1481 | return; |
||
1482 | |||
1483 | |||
1484 | png_write_info(png_ptr, info_ptr); |
||
1485 | |||
1486 | |||
1487 | |||
1488 | |||
1489 | /* Invert monochrome pixels */ |
||
1490 | if (transforms & PNG_TRANSFORM_INVERT_MONO) |
||
1491 | png_set_invert_mono(png_ptr); |
||
1492 | #endif |
||
1493 | |||
1494 | |||
1495 | /* Shift the pixels up to a legal bit depth and fill in |
||
1496 | * as appropriate to correctly scale the image. |
||
1497 | */ |
||
1498 | if ((transforms & PNG_TRANSFORM_SHIFT) |
||
1499 | && (info_ptr->valid & PNG_INFO_sBIT)) |
||
1500 | png_set_shift(png_ptr, &info_ptr->sig_bit); |
||
1501 | #endif |
||
1502 | |||
1503 | |||
1504 | /* Pack pixels into bytes */ |
||
1505 | if (transforms & PNG_TRANSFORM_PACKING) |
||
1506 | png_set_packing(png_ptr); |
||
1507 | #endif |
||
1508 | |||
1509 | |||
1510 | /* Swap location of alpha bytes from ARGB to RGBA */ |
||
1511 | if (transforms & PNG_TRANSFORM_SWAP_ALPHA) |
||
1512 | png_set_swap_alpha(png_ptr); |
||
1513 | #endif |
||
1514 | |||
1515 | |||
1516 | /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */ |
||
1517 | if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) |
||
1518 | png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); |
||
1519 | |||
1520 | |||
1521 | png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); |
||
1522 | #endif |
||
1523 | |||
1524 | |||
1525 | /* Flip BGR pixels to RGB */ |
||
1526 | if (transforms & PNG_TRANSFORM_BGR) |
||
1527 | png_set_bgr(png_ptr); |
||
1528 | #endif |
||
1529 | |||
1530 | |||
1531 | /* Swap bytes of 16-bit files to most significant byte first */ |
||
1532 | if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) |
||
1533 | png_set_swap(png_ptr); |
||
1534 | #endif |
||
1535 | |||
1536 | |||
1537 | /* Swap bits of 1, 2, 4 bit packed pixel formats */ |
||
1538 | if (transforms & PNG_TRANSFORM_PACKSWAP) |
||
1539 | png_set_packswap(png_ptr); |
||
1540 | #endif |
||
1541 | |||
1542 | |||
1543 | /* Invert the alpha channel from opacity to transparency */ |
||
1544 | if (transforms & PNG_TRANSFORM_INVERT_ALPHA) |
||
1545 | png_set_invert_alpha(png_ptr); |
||
1546 | #endif |
||
1547 | |||
1548 | |||
1549 | |||
1550 | |||
1551 | if (info_ptr->valid & PNG_INFO_IDAT) |
||
1552 | png_write_image(png_ptr, info_ptr->row_pointers); |
||
1553 | |||
1554 | |||
1555 | png_write_end(png_ptr, info_ptr); |
||
1556 | |||
1557 | |||
1558 | PNG_UNUSED(params) |
||
1559 | } |
||
1560 | #endif |
||
1561 | |||
1562 | |||
1563 | |||
1564 | #ifdef PNG_STDIO_SUPPORTED /* currently required for png_image_write_* */ |
||
1565 | /* Initialize the write structure - general purpose utility. */ |
||
1566 | static int |
||
1567 | png_image_write_init(png_imagep image) |
||
1568 | { |
||
1569 | png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image, |
||
1570 | png_safe_error, png_safe_warning); |
||
1571 | |||
1572 | |||
1573 | { |
||
1574 | png_infop info_ptr = png_create_info_struct(png_ptr); |
||
1575 | |||
1576 | |||
1577 | { |
||
1578 | png_controlp control = png_voidcast(png_controlp, |
||
1579 | png_malloc_warn(png_ptr, (sizeof *control))); |
||
1580 | |||
1581 | |||
1582 | { |
||
1583 | memset(control, 0, (sizeof *control)); |
||
1584 | |||
1585 | |||
1586 | control->info_ptr = info_ptr; |
||
1587 | control->for_write = 1; |
||
1588 | |||
1589 | |||
1590 | return 1; |
||
1591 | } |
||
1592 | |||
1593 | |||
1594 | png_destroy_info_struct(png_ptr, &info_ptr); |
||
1595 | } |
||
1596 | |||
1597 | |||
1598 | } |
||
1599 | |||
1600 | |||
1601 | } |
||
1602 | |||
1603 | |||
1604 | typedef struct |
||
1605 | { |
||
1606 | /* Arguments: */ |
||
1607 | png_imagep image; |
||
1608 | png_const_voidp buffer; |
||
1609 | png_int_32 row_stride; |
||
1610 | png_const_voidp colormap; |
||
1611 | int convert_to_8bit; |
||
1612 | /* Local variables: */ |
||
1613 | png_const_voidp first_row; |
||
1614 | ptrdiff_t row_bytes; |
||
1615 | png_voidp local_row; |
||
1616 | } png_image_write_control; |
||
1617 | |||
1618 | |||
1619 | * do any necessary byte swapping. The component order is defined by the |
||
1620 | * png_image format value. |
||
1621 | */ |
||
1622 | static int |
||
1623 | png_write_image_16bit(png_voidp argument) |
||
1624 | { |
||
1625 | png_image_write_control *display = png_voidcast(png_image_write_control*, |
||
1626 | argument); |
||
1627 | png_imagep image = display->image; |
||
1628 | png_structrp png_ptr = image->opaque->png_ptr; |
||
1629 | |||
1630 | |||
1631 | display->first_row); |
||
1632 | png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row); |
||
1633 | png_uint_16p row_end; |
||
1634 | const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; |
||
1635 | int aindex = 0; |
||
1636 | png_uint_32 y = image->height; |
||
1637 | |||
1638 | |||
1639 | { |
||
1640 | if (image->format & PNG_FORMAT_FLAG_AFIRST) |
||
1641 | { |
||
1642 | aindex = -1; |
||
1643 | ++input_row; /* To point to the first component */ |
||
1644 | ++output_row; |
||
1645 | } |
||
1646 | |||
1647 | |||
1648 | aindex = channels; |
||
1649 | } |
||
1650 | |||
1651 | |||
1652 | png_error(png_ptr, "png_write_image: internal call error"); |
||
1653 | |||
1654 | |||
1655 | * above to 'row' means that row_end can actually be beyond the end of the |
||
1656 | * row; this is correct. |
||
1657 | */ |
||
1658 | row_end = output_row + image->width * (channels+1); |
||
1659 | |||
1660 | |||
1661 | { |
||
1662 | png_const_uint_16p in_ptr = input_row; |
||
1663 | png_uint_16p out_ptr = output_row; |
||
1664 | |||
1665 | |||
1666 | { |
||
1667 | const png_uint_16 alpha = in_ptr[aindex]; |
||
1668 | png_uint_32 reciprocal = 0; |
||
1669 | int c; |
||
1670 | |||
1671 | |||
1672 | |||
1673 | |||
1674 | * component/alpha*65535 << 15. (I.e. 15 bits of precision); this |
||
1675 | * allows correct rounding by adding .5 before the shift. 'reciprocal' |
||
1676 | * is only initialized when required. |
||
1677 | */ |
||
1678 | if (alpha > 0 && alpha < 65535) |
||
1679 | reciprocal = ((0xffff<<15)+(alpha>>1))/alpha; |
||
1680 | |||
1681 | |||
1682 | do /* always at least one channel */ |
||
1683 | { |
||
1684 | png_uint_16 component = *in_ptr++; |
||
1685 | |||
1686 | |||
1687 | * otherwise if 0/0 is represented as some other value there is more |
||
1688 | * likely to be a discontinuity which will probably damage |
||
1689 | * compression when moving from a fully transparent area to a |
||
1690 | * nearly transparent one. (The assumption here is that opaque |
||
1691 | * areas tend not to be 0 intensity.) |
||
1692 | */ |
||
1693 | if (component >= alpha) |
||
1694 | component = 65535; |
||
1695 | |||
1696 | |||
1697 | * component*reciprocal is less than 2^31. |
||
1698 | */ |
||
1699 | else if (component > 0 && alpha < 65535) |
||
1700 | { |
||
1701 | png_uint_32 calc = component * reciprocal; |
||
1702 | calc += 16384; /* round to nearest */ |
||
1703 | component = (png_uint_16)(calc >> 15); |
||
1704 | } |
||
1705 | |||
1706 | |||
1707 | } |
||
1708 | while (--c > 0); |
||
1709 | |||
1710 | |||
1711 | ++in_ptr; |
||
1712 | ++out_ptr; |
||
1713 | } |
||
1714 | |||
1715 | |||
1716 | input_row += display->row_bytes/(sizeof (png_uint_16)); |
||
1717 | } |
||
1718 | |||
1719 | |||
1720 | } |
||
1721 | |||
1722 | |||
1723 | * is present it must be removed from the components, the components are then |
||
1724 | * written in sRGB encoding. No components are added or removed. |
||
1725 | * |
||
1726 | * Calculate an alpha reciprocal to reverse pre-multiplication. As above the |
||
1727 | * calculation can be done to 15 bits of accuracy; however, the output needs to |
||
1728 | * be scaled in the range 0..255*65535, so include that scaling here. |
||
1729 | */ |
||
1730 | #define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha) |
||
1731 | |||
1732 | |||
1733 | png_unpremultiply(png_uint_32 component, png_uint_32 alpha, |
||
1734 | png_uint_32 reciprocal/*from the above macro*/) |
||
1735 | { |
||
1736 | /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0 |
||
1737 | * is represented as some other value there is more likely to be a |
||
1738 | * discontinuity which will probably damage compression when moving from a |
||
1739 | * fully transparent area to a nearly transparent one. (The assumption here |
||
1740 | * is that opaque areas tend not to be 0 intensity.) |
||
1741 | * |
||
1742 | * There is a rounding problem here; if alpha is less than 128 it will end up |
||
1743 | * as 0 when scaled to 8 bits. To avoid introducing spurious colors into the |
||
1744 | * output change for this too. |
||
1745 | */ |
||
1746 | if (component >= alpha || alpha < 128) |
||
1747 | return 255; |
||
1748 | |||
1749 | |||
1750 | * component*reciprocal is less than 2^31. |
||
1751 | */ |
||
1752 | else if (component > 0) |
||
1753 | { |
||
1754 | /* The test is that alpha/257 (rounded) is less than 255, the first value |
||
1755 | * that becomes 255 is 65407. |
||
1756 | * NOTE: this must agree with the PNG_DIV257 macro (which must, therefore, |
||
1757 | * be exact!) [Could also test reciprocal != 0] |
||
1758 | */ |
||
1759 | if (alpha < 65407) |
||
1760 | { |
||
1761 | component *= reciprocal; |
||
1762 | component += 64; /* round to nearest */ |
||
1763 | component >>= 7; |
||
1764 | } |
||
1765 | |||
1766 | |||
1767 | component *= 255; |
||
1768 | |||
1769 | |||
1770 | return (png_byte)PNG_sRGB_FROM_LINEAR(component); |
||
1771 | } |
||
1772 | |||
1773 | |||
1774 | return 0; |
||
1775 | } |
||
1776 | |||
1777 | |||
1778 | png_write_image_8bit(png_voidp argument) |
||
1779 | { |
||
1780 | png_image_write_control *display = png_voidcast(png_image_write_control*, |
||
1781 | argument); |
||
1782 | png_imagep image = display->image; |
||
1783 | png_structrp png_ptr = image->opaque->png_ptr; |
||
1784 | |||
1785 | |||
1786 | display->first_row); |
||
1787 | png_bytep output_row = png_voidcast(png_bytep, display->local_row); |
||
1788 | png_uint_32 y = image->height; |
||
1789 | const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; |
||
1790 | |||
1791 | |||
1792 | { |
||
1793 | png_bytep row_end; |
||
1794 | int aindex; |
||
1795 | |||
1796 | |||
1797 | { |
||
1798 | aindex = -1; |
||
1799 | ++input_row; /* To point to the first component */ |
||
1800 | ++output_row; |
||
1801 | } |
||
1802 | |||
1803 | |||
1804 | aindex = channels; |
||
1805 | |||
1806 | |||
1807 | row_end = output_row + image->width * (channels+1); |
||
1808 | |||
1809 | |||
1810 | { |
||
1811 | png_const_uint_16p in_ptr = input_row; |
||
1812 | png_bytep out_ptr = output_row; |
||
1813 | |||
1814 | |||
1815 | { |
||
1816 | png_uint_16 alpha = in_ptr[aindex]; |
||
1817 | png_byte alphabyte = (png_byte)PNG_DIV257(alpha); |
||
1818 | png_uint_32 reciprocal = 0; |
||
1819 | int c; |
||
1820 | |||
1821 | |||
1822 | out_ptr[aindex] = alphabyte; |
||
1823 | |||
1824 | |||
1825 | reciprocal = UNP_RECIPROCAL(alpha); |
||
1826 | |||
1827 | |||
1828 | do /* always at least one channel */ |
||
1829 | *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal); |
||
1830 | while (--c > 0); |
||
1831 | |||
1832 | |||
1833 | ++in_ptr; |
||
1834 | ++out_ptr; |
||
1835 | } /* while out_ptr < row_end */ |
||
1836 | |||
1837 | |||
1838 | display->local_row)); |
||
1839 | input_row += display->row_bytes/(sizeof (png_uint_16)); |
||
1840 | } /* while y */ |
||
1841 | } |
||
1842 | |||
1843 | |||
1844 | { |
||
1845 | /* No alpha channel, so the row_end really is the end of the row and it |
||
1846 | * is sufficient to loop over the components one by one. |
||
1847 | */ |
||
1848 | png_bytep row_end = output_row + image->width * channels; |
||
1849 | |||
1850 | |||
1851 | { |
||
1852 | png_const_uint_16p in_ptr = input_row; |
||
1853 | png_bytep out_ptr = output_row; |
||
1854 | |||
1855 | |||
1856 | { |
||
1857 | png_uint_32 component = *in_ptr++; |
||
1858 | |||
1859 | |||
1860 | *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component); |
||
1861 | } |
||
1862 | |||
1863 | |||
1864 | input_row += display->row_bytes/(sizeof (png_uint_16)); |
||
1865 | } |
||
1866 | } |
||
1867 | |||
1868 | |||
1869 | } |
||
1870 | |||
1871 | |||
1872 | png_image_set_PLTE(png_image_write_control *display) |
||
1873 | { |
||
1874 | const png_imagep image = display->image; |
||
1875 | const void *cmap = display->colormap; |
||
1876 | const int entries = image->colormap_entries > 256 ? 256 : |
||
1877 | (int)image->colormap_entries; |
||
1878 | |||
1879 | |||
1880 | const png_uint_32 format = image->format; |
||
1881 | const int channels = PNG_IMAGE_SAMPLE_CHANNELS(format); |
||
1882 | |||
1883 | |||
1884 | const int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 && |
||
1885 | (format & PNG_FORMAT_FLAG_ALPHA) != 0; |
||
1886 | # else |
||
1887 | # define afirst 0 |
||
1888 | # endif |
||
1889 | |||
1890 | |||
1891 | const int bgr = (format & PNG_FORMAT_FLAG_BGR) ? 2 : 0; |
||
1892 | # else |
||
1893 | # define bgr 0 |
||
1894 | # endif |
||
1895 | |||
1896 | |||
1897 | png_color palette[256]; |
||
1898 | png_byte tRNS[256]; |
||
1899 | |||
1900 | |||
1901 | memset(palette, 0, (sizeof palette)); |
||
1902 | |||
1903 | |||
1904 | { |
||
1905 | /* This gets automatically converted to sRGB with reversal of the |
||
1906 | * pre-multiplication if the color-map has an alpha channel. |
||
1907 | */ |
||
1908 | if (format & PNG_FORMAT_FLAG_LINEAR) |
||
1909 | { |
||
1910 | png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap); |
||
1911 | |||
1912 | |||
1913 | |||
1914 | |||
1915 | { |
||
1916 | if (channels >= 3) /* RGB */ |
||
1917 | { |
||
1918 | palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 * |
||
1919 | entry[(2 ^ bgr)]); |
||
1920 | palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 * |
||
1921 | entry[1]); |
||
1922 | palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 * |
||
1923 | entry[bgr]); |
||
1924 | } |
||
1925 | |||
1926 | |||
1927 | palette[i].blue = palette[i].red = palette[i].green = |
||
1928 | (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry); |
||
1929 | } |
||
1930 | |||
1931 | |||
1932 | { |
||
1933 | png_uint_16 alpha = entry[afirst ? 0 : channels-1]; |
||
1934 | png_byte alphabyte = (png_byte)PNG_DIV257(alpha); |
||
1935 | png_uint_32 reciprocal = 0; |
||
1936 | |||
1937 | |||
1938 | * this is designed to produce a value scaled to 255*65535 when |
||
1939 | * divided by 128 (i.e. asr 7). |
||
1940 | */ |
||
1941 | if (alphabyte > 0 && alphabyte < 255) |
||
1942 | reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha; |
||
1943 | |||
1944 | |||
1945 | if (alphabyte < 255) |
||
1946 | num_trans = i+1; |
||
1947 | |||
1948 | |||
1949 | { |
||
1950 | palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)], |
||
1951 | alpha, reciprocal); |
||
1952 | palette[i].green = png_unpremultiply(entry[afirst + 1], alpha, |
||
1953 | reciprocal); |
||
1954 | palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha, |
||
1955 | reciprocal); |
||
1956 | } |
||
1957 | |||
1958 | |||
1959 | palette[i].blue = palette[i].red = palette[i].green = |
||
1960 | png_unpremultiply(entry[afirst], alpha, reciprocal); |
||
1961 | } |
||
1962 | } |
||
1963 | |||
1964 | |||
1965 | { |
||
1966 | png_const_bytep entry = png_voidcast(png_const_bytep, cmap); |
||
1967 | |||
1968 | |||
1969 | |||
1970 | |||
1971 | { |
||
1972 | case 4: |
||
1973 | tRNS[i] = entry[afirst ? 0 : 3]; |
||
1974 | if (tRNS[i] < 255) |
||
1975 | num_trans = i+1; |
||
1976 | /* FALL THROUGH */ |
||
1977 | case 3: |
||
1978 | palette[i].blue = entry[afirst + (2 ^ bgr)]; |
||
1979 | palette[i].green = entry[afirst + 1]; |
||
1980 | palette[i].red = entry[afirst + bgr]; |
||
1981 | break; |
||
1982 | |||
1983 | |||
1984 | tRNS[i] = entry[1 ^ afirst]; |
||
1985 | if (tRNS[i] < 255) |
||
1986 | num_trans = i+1; |
||
1987 | /* FALL THROUGH */ |
||
1988 | case 1: |
||
1989 | palette[i].blue = palette[i].red = palette[i].green = |
||
1990 | entry[afirst]; |
||
1991 | break; |
||
1992 | |||
1993 | |||
1994 | break; |
||
1995 | } |
||
1996 | } |
||
1997 | } |
||
1998 | |||
1999 | |||
2000 | # undef afirst |
||
2001 | # endif |
||
2002 | # ifdef bgr |
||
2003 | # undef bgr |
||
2004 | # endif |
||
2005 | |||
2006 | |||
2007 | entries); |
||
2008 | |||
2009 | |||
2010 | png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS, |
||
2011 | num_trans, NULL); |
||
2012 | |||
2013 | |||
2014 | } |
||
2015 | |||
2016 | |||
2017 | png_image_write_main(png_voidp argument) |
||
2018 | { |
||
2019 | png_image_write_control *display = png_voidcast(png_image_write_control*, |
||
2020 | argument); |
||
2021 | png_imagep image = display->image; |
||
2022 | png_structrp png_ptr = image->opaque->png_ptr; |
||
2023 | png_inforp info_ptr = image->opaque->info_ptr; |
||
2024 | png_uint_32 format = image->format; |
||
2025 | |||
2026 | |||
2027 | int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR) != 0; /* input */ |
||
2028 | int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0; |
||
2029 | int write_16bit = linear && !colormap && !display->convert_to_8bit; |
||
2030 | |||
2031 | |||
2032 | /* Make sure we error out on any bad situation */ |
||
2033 | png_set_benign_errors(png_ptr, 0/*error*/); |
||
2034 | # endif |
||
2035 | |||
2036 | |||
2037 | if (display->row_stride == 0) |
||
2038 | display->row_stride = PNG_IMAGE_ROW_STRIDE(*image); |
||
2039 | |||
2040 | |||
2041 | if (format & PNG_FORMAT_FLAG_COLORMAP) |
||
2042 | { |
||
2043 | if (display->colormap != NULL && image->colormap_entries > 0) |
||
2044 | { |
||
2045 | png_uint_32 entries = image->colormap_entries; |
||
2046 | |||
2047 | |||
2048 | entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)), |
||
2049 | PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, |
||
2050 | PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); |
||
2051 | |||
2052 | |||
2053 | } |
||
2054 | |||
2055 | |||
2056 | png_error(image->opaque->png_ptr, |
||
2057 | "no color-map for color-mapped image"); |
||
2058 | } |
||
2059 | |||
2060 | |||
2061 | png_set_IHDR(png_ptr, info_ptr, image->width, image->height, |
||
2062 | write_16bit ? 16 : 8, |
||
2063 | ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) + |
||
2064 | ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0), |
||
2065 | PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); |
||
2066 | |||
2067 | |||
2068 | * png_write_info, not before as in the read code, but the 'set' functions |
||
2069 | * must still be called before. Just set the color space information, never |
||
2070 | * write an interlaced image. |
||
2071 | */ |
||
2072 | |||
2073 | |||
2074 | { |
||
2075 | /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */ |
||
2076 | png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR); |
||
2077 | |||
2078 | |||
2079 | png_set_cHRM_fixed(png_ptr, info_ptr, |
||
2080 | /* color x y */ |
||
2081 | /* white */ 31270, 32900, |
||
2082 | /* red */ 64000, 33000, |
||
2083 | /* green */ 30000, 60000, |
||
2084 | /* blue */ 15000, 6000 |
||
2085 | ); |
||
2086 | } |
||
2087 | |||
2088 | |||
2089 | png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); |
||
2090 | |||
2091 | |||
2092 | * space must still be gamma encoded. |
||
2093 | */ |
||
2094 | else |
||
2095 | png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE); |
||
2096 | |||
2097 | |||
2098 | png_write_info(png_ptr, info_ptr); |
||
2099 | |||
2100 | |||
2101 | * remove the handled transformations from the 'format' flags for checking. |
||
2102 | * |
||
2103 | * First check for a little endian system if writing 16 bit files. |
||
2104 | */ |
||
2105 | if (write_16bit) |
||
2106 | { |
||
2107 | PNG_CONST png_uint_16 le = 0x0001; |
||
2108 | |||
2109 | |||
2110 | png_set_swap(png_ptr); |
||
2111 | } |
||
2112 | |||
2113 | |||
2114 | if (format & PNG_FORMAT_FLAG_BGR) |
||
2115 | { |
||
2116 | if (!colormap && (format & PNG_FORMAT_FLAG_COLOR) != 0) |
||
2117 | png_set_bgr(png_ptr); |
||
2118 | format &= ~PNG_FORMAT_FLAG_BGR; |
||
2119 | } |
||
2120 | # endif |
||
2121 | |||
2122 | |||
2123 | if (format & PNG_FORMAT_FLAG_AFIRST) |
||
2124 | { |
||
2125 | if (!colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0) |
||
2126 | png_set_swap_alpha(png_ptr); |
||
2127 | format &= ~PNG_FORMAT_FLAG_AFIRST; |
||
2128 | } |
||
2129 | # endif |
||
2130 | |||
2131 | |||
2132 | * above, but the application data is still byte packed. |
||
2133 | */ |
||
2134 | if (colormap && image->colormap_entries <= 16) |
||
2135 | png_set_packing(png_ptr); |
||
2136 | |||
2137 | |||
2138 | if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR | |
||
2139 | PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0) |
||
2140 | png_error(png_ptr, "png_write_image: unsupported transformation"); |
||
2141 | |||
2142 | |||
2143 | png_const_bytep row = png_voidcast(png_const_bytep, display->buffer); |
||
2144 | ptrdiff_t row_bytes = display->row_stride; |
||
2145 | |||
2146 | |||
2147 | row_bytes *= (sizeof (png_uint_16)); |
||
2148 | |||
2149 | |||
2150 | row += (image->height-1) * (-row_bytes); |
||
2151 | |||
2152 | |||
2153 | display->row_bytes = row_bytes; |
||
2154 | } |
||
2155 | |||
2156 | |||
2157 | if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0) |
||
2158 | { |
||
2159 | png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS); |
||
2160 | /* NOTE: determined by experiment using pngstest, this reflects some |
||
2161 | * balance between the time to write the image once and the time to read |
||
2162 | * it about 50 times. The speed-up in pngstest was about 10-20% of the |
||
2163 | * total (user) time on a heavily loaded system. |
||
2164 | */ |
||
2165 | png_set_compression_level(png_ptr, 3); |
||
2166 | } |
||
2167 | |||
2168 | |||
2169 | * before it is written. This only applies when the input is 16-bit and |
||
2170 | * either there is an alpha channel or it is converted to 8-bit. |
||
2171 | */ |
||
2172 | if ((linear && alpha) || (!colormap && display->convert_to_8bit)) |
||
2173 | { |
||
2174 | png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr, |
||
2175 | png_get_rowbytes(png_ptr, info_ptr))); |
||
2176 | int result; |
||
2177 | |||
2178 | |||
2179 | if (write_16bit) |
||
2180 | result = png_safe_execute(image, png_write_image_16bit, display); |
||
2181 | else |
||
2182 | result = png_safe_execute(image, png_write_image_8bit, display); |
||
2183 | display->local_row = NULL; |
||
2184 | |||
2185 | |||
2186 | |||
2187 | |||
2188 | if (!result) |
||
2189 | return 0; |
||
2190 | } |
||
2191 | |||
2192 | |||
2193 | * supported by the rest of the libpng write code; call it directly. |
||
2194 | */ |
||
2195 | else |
||
2196 | { |
||
2197 | png_const_bytep row = png_voidcast(png_const_bytep, display->first_row); |
||
2198 | ptrdiff_t row_bytes = display->row_bytes; |
||
2199 | png_uint_32 y = image->height; |
||
2200 | |||
2201 | |||
2202 | { |
||
2203 | png_write_row(png_ptr, row); |
||
2204 | row += row_bytes; |
||
2205 | } |
||
2206 | } |
||
2207 | |||
2208 | |||
2209 | return 1; |
||
2210 | } |
||
2211 | |||
2212 | |||
2213 | png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, |
||
2214 | const void *buffer, png_int_32 row_stride, const void *colormap) |
||
2215 | { |
||
2216 | /* Write the image to the given (FILE*). */ |
||
2217 | if (image != NULL && image->version == PNG_IMAGE_VERSION) |
||
2218 | { |
||
2219 | if (file != NULL) |
||
2220 | { |
||
2221 | if (png_image_write_init(image)) |
||
2222 | { |
||
2223 | png_image_write_control display; |
||
2224 | int result; |
||
2225 | |||
2226 | |||
2227 | * than this and we haven't changed the standard IO functions so |
||
2228 | * this saves a 'safe' function. |
||
2229 | */ |
||
2230 | image->opaque->png_ptr->io_ptr = file; |
||
2231 | |||
2232 | |||
2233 | display.image = image; |
||
2234 | display.buffer = buffer; |
||
2235 | display.row_stride = row_stride; |
||
2236 | display.colormap = colormap; |
||
2237 | display.convert_to_8bit = convert_to_8bit; |
||
2238 | |||
2239 | |||
2240 | png_image_free(image); |
||
2241 | return result; |
||
2242 | } |
||
2243 | |||
2244 | |||
2245 | return 0; |
||
2246 | } |
||
2247 | |||
2248 | |||
2249 | return png_image_error(image, |
||
2250 | "png_image_write_to_stdio: invalid argument"); |
||
2251 | } |
||
2252 | |||
2253 | |||
2254 | return png_image_error(image, |
||
2255 | "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION"); |
||
2256 | |||
2257 | |||
2258 | return 0; |
||
2259 | } |
||
2260 | |||
2261 | |||
2262 | png_image_write_to_file(png_imagep image, const char *file_name, |
||
2263 | int convert_to_8bit, const void *buffer, png_int_32 row_stride, |
||
2264 | const void *colormap) |
||
2265 | { |
||
2266 | /* Write the image to the named file. */ |
||
2267 | if (image != NULL && image->version == PNG_IMAGE_VERSION) |
||
2268 | { |
||
2269 | if (file_name != NULL) |
||
2270 | { |
||
2271 | FILE *fp = fopen(file_name, "wb"); |
||
2272 | |||
2273 | |||
2274 | { |
||
2275 | if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer, |
||
2276 | row_stride, colormap)) |
||
2277 | { |
||
2278 | int error; /* from fflush/fclose */ |
||
2279 | |||
2280 | |||
2281 | if (fflush(fp) == 0 && ferror(fp) == 0) |
||
2282 | { |
||
2283 | if (fclose(fp) == 0) |
||
2284 | return 1; |
||
2285 | |||
2286 | |||
2287 | } |
||
2288 | |||
2289 | |||
2290 | { |
||
2291 | error = errno; /* from fflush or ferror */ |
||
2292 | (void)fclose(fp); |
||
2293 | } |
||
2294 | |||
2295 | |||
2296 | /* The image has already been cleaned up; this is just used to |
||
2297 | * set the error (because the original write succeeded). |
||
2298 | */ |
||
2299 | return png_image_error(image, strerror(error)); |
||
2300 | } |
||
2301 | |||
2302 | |||
2303 | { |
||
2304 | /* Clean up: just the opened file. */ |
||
2305 | (void)fclose(fp); |
||
2306 | (void)remove(file_name); |
||
2307 | return 0; |
||
2308 | } |
||
2309 | } |
||
2310 | |||
2311 | |||
2312 | return png_image_error(image, strerror(errno)); |
||
2313 | } |
||
2314 | |||
2315 | |||
2316 | return png_image_error(image, |
||
2317 | "png_image_write_to_file: invalid argument"); |
||
2318 | } |
||
2319 | |||
2320 | |||
2321 | return png_image_error(image, |
||
2322 | "png_image_write_to_file: incorrect PNG_IMAGE_VERSION"); |
||
2323 | |||
2324 | |||
2325 | return 0; |
||
2326 | } |
||
2327 | #endif /* PNG_STDIO_SUPPORTED */ |
||
2328 | #endif /* SIMPLIFIED_WRITE */ |
||
2329 | #endif /* PNG_WRITE_SUPPORTED */>=>>>>7)+(alpha><7)+(alpha>>>>>>>>7)+(alpha><7)+(alpha>>15)+(alpha><15)+(alpha>>><>>>=>>=>>=>>>=>>>>>>>>>>>>>>>>(int)info_ptr->> |
||
2330 |