pngwrite.c 66.5 KB
Newer Older
1

A
Andreas Dilger 已提交
2
/* pngwrite.c - general routines to write a PNG file
3
 *
4
 * Last changed in libpng 1.5.7 [(PENDING RELEASE)]
5
 * Copyright (c) 1998-2011 Glenn Randers-Pehrson
6 7
 * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
 * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
8
 *
9
 * This code is released under the libpng license.
10
 * For conditions of distribution and use, see the disclaimer
11
 * and license in png.h
12
 */
G
Guy Schalnat 已提交
13

14
#include "pngpriv.h"
15 16 17
#if defined PNG_SIMPLIFIED_WRITE_SUPPORTED && defined PNG_STDIO_SUPPORTED
#  include <errno.h>
#endif
G
Guy Schalnat 已提交
18

19 20
#ifdef PNG_WRITE_SUPPORTED

A
Andreas Dilger 已提交
21 22 23
/* Writes all the PNG information.  This is the suggested way to use the
 * library.  If you have a new chunk to add, make a function to write it,
 * and put it in the correct location here.  If you want the chunk written
24
 * after the image data, put it in png_write_end().  I strongly encourage
A
Andreas Dilger 已提交
25 26 27 28 29
 * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
 * the chunk, as that will keep the code from breaking if you want to just
 * write a plain PNG file.  If you have long comments, I suggest writing
 * them in png_write_end(), and compressing them.
 */
30
void PNGAPI
31
png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr)
G
Guy Schalnat 已提交
32
{
33
   png_debug(1, "in png_write_info_before_PLTE");
34

35 36
   if (png_ptr == NULL || info_ptr == NULL)
      return;
37

38 39
   if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
   {
40 41
   /* Write PNG signature */
   png_write_sig(png_ptr);
42

43
#ifdef PNG_MNG_FEATURES_SUPPORTED
44
   if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \
45
       (png_ptr->mng_features_permitted))
46
   {
47
      png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
48
      png_ptr->mng_features_permitted = 0;
49 50
   }
#endif
51

52
   /* Write IHDR information. */
A
Andreas Dilger 已提交
53
   png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
54 55
       info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
       info_ptr->filter_type,
56
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
57
       info_ptr->interlace_type);
58
#else
59
       0);
60
#endif
61 62 63
   /* The rest of these check to see if the valid field has the appropriate
    * flag set, and if it does, writes the chunk.
    */
64
#ifdef PNG_WRITE_gAMA_SUPPORTED
A
Andreas Dilger 已提交
65
   if (info_ptr->valid & PNG_INFO_gAMA)
G
[devel]  
Glenn Randers-Pehrson 已提交
66
      png_write_gAMA_fixed(png_ptr, info_ptr->gamma);
G
Guy Schalnat 已提交
67
#endif
68
#ifdef PNG_WRITE_sRGB_SUPPORTED
69
   if (info_ptr->valid & PNG_INFO_sRGB)
70
      png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent);
71
#endif
72

73
#ifdef PNG_WRITE_iCCP_SUPPORTED
74
   if (info_ptr->valid & PNG_INFO_iCCP)
75
      png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE,
G
[devel]  
Glenn Randers-Pehrson 已提交
76
          (png_charp)info_ptr->iccp_profile, (int)info_ptr->iccp_proflen);
77
#endif
78
#ifdef PNG_WRITE_sBIT_SUPPORTED
A
Andreas Dilger 已提交
79 80
   if (info_ptr->valid & PNG_INFO_sBIT)
      png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
G
Guy Schalnat 已提交
81
#endif
82
#ifdef PNG_WRITE_cHRM_SUPPORTED
A
Andreas Dilger 已提交
83
   if (info_ptr->valid & PNG_INFO_cHRM)
G
[devel]  
Glenn Randers-Pehrson 已提交
84
      png_write_cHRM_fixed(png_ptr,
85 86 87 88
          info_ptr->x_white, info_ptr->y_white,
          info_ptr->x_red, info_ptr->y_red,
          info_ptr->x_green, info_ptr->y_green,
          info_ptr->x_blue, info_ptr->y_blue);
89
#endif
90

91
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
92 93
   if (info_ptr->unknown_chunks_num)
   {
94
      png_unknown_chunk *up;
95

96
      png_debug(5, "writing extra chunks");
97

98 99 100 101
      for (up = info_ptr->unknown_chunks;
           up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
           up++)
      {
102
         int keep = png_handle_as_unknown(png_ptr, up->name);
103

104
         if (keep != PNG_HANDLE_CHUNK_NEVER &&
105 106
             up->location &&
             !(up->location & PNG_HAVE_PLTE) &&
107
             !(up->location & PNG_HAVE_IDAT) &&
108
             !(up->location & PNG_AFTER_IDAT) &&
109 110
             ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
             (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
111
         {
112 113
            if (up->size == 0)
               png_warning(png_ptr, "Writing zero-length unknown chunk");
114

115
            png_write_chunk(png_ptr, up->name, up->data, up->size);
116
         }
117
      }
118
   }
G
Guy Schalnat 已提交
119
#endif
120 121 122 123
      png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
   }
}

124
void PNGAPI
125 126
png_write_info(png_structp png_ptr, png_infop info_ptr)
{
127
#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
128 129 130
   int i;
#endif

131
   png_debug(1, "in png_write_info");
132

133 134 135
   if (png_ptr == NULL || info_ptr == NULL)
      return;

136 137
   png_write_info_before_PLTE(png_ptr, info_ptr);

A
Andreas Dilger 已提交
138
   if (info_ptr->valid & PNG_INFO_PLTE)
A
Andreas Dilger 已提交
139
      png_write_PLTE(png_ptr, info_ptr->palette,
140
          (png_uint_32)info_ptr->num_palette);
141

A
Andreas Dilger 已提交
142
   else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
143
      png_error(png_ptr, "Valid palette required for paletted images");
144

145
#ifdef PNG_WRITE_tRNS_SUPPORTED
A
Andreas Dilger 已提交
146
   if (info_ptr->valid & PNG_INFO_tRNS)
147
   {
148
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
149
      /* Invert the alpha channel (in tRNS) */
150
      if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&
151
          info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
152 153
      {
         int j;
154
         for (j = 0; j<(int)info_ptr->num_trans; j++)
G
[devel]  
Glenn Randers-Pehrson 已提交
155
            info_ptr->trans_alpha[j] =
156
               (png_byte)(255 - info_ptr->trans_alpha[j]);
157
      }
158
#endif
159
      png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color),
160
          info_ptr->num_trans, info_ptr->color_type);
161
   }
G
Guy Schalnat 已提交
162
#endif
163
#ifdef PNG_WRITE_bKGD_SUPPORTED
A
Andreas Dilger 已提交
164 165
   if (info_ptr->valid & PNG_INFO_bKGD)
      png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
G
Guy Schalnat 已提交
166
#endif
167

168
#ifdef PNG_WRITE_hIST_SUPPORTED
A
Andreas Dilger 已提交
169 170
   if (info_ptr->valid & PNG_INFO_hIST)
      png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
G
Guy Schalnat 已提交
171
#endif
172

173
#ifdef PNG_WRITE_oFFs_SUPPORTED
A
Andreas Dilger 已提交
174 175
   if (info_ptr->valid & PNG_INFO_oFFs)
      png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
176
          info_ptr->offset_unit_type);
G
Guy Schalnat 已提交
177
#endif
178

179
#ifdef PNG_WRITE_pCAL_SUPPORTED
A
Andreas Dilger 已提交
180 181
   if (info_ptr->valid & PNG_INFO_pCAL)
      png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
182 183
          info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
          info_ptr->pcal_units, info_ptr->pcal_params);
A
Andreas Dilger 已提交
184
#endif
185

186
#ifdef PNG_WRITE_sCAL_SUPPORTED
G
[devel]  
Glenn Randers-Pehrson 已提交
187
   if (info_ptr->valid & PNG_INFO_sCAL)
188
      png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
189
          info_ptr->scal_s_width, info_ptr->scal_s_height);
190 191
#endif /* sCAL */

192
#ifdef PNG_WRITE_pHYs_SUPPORTED
A
Andreas Dilger 已提交
193 194
   if (info_ptr->valid & PNG_INFO_pHYs)
      png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
195
          info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
196 197
#endif /* pHYs */

198
#ifdef PNG_WRITE_tIME_SUPPORTED
A
Andreas Dilger 已提交
199
   if (info_ptr->valid & PNG_INFO_tIME)
G
Guy Schalnat 已提交
200
   {
A
Andreas Dilger 已提交
201
      png_write_tIME(png_ptr, &(info_ptr->mod_time));
202
      png_ptr->mode |= PNG_WROTE_tIME;
G
Guy Schalnat 已提交
203
   }
204 205
#endif /* tIME */

206
#ifdef PNG_WRITE_sPLT_SUPPORTED
207
   if (info_ptr->valid & PNG_INFO_sPLT)
208 209
      for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
         png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
210 211
#endif /* sPLT */

212
#ifdef PNG_WRITE_TEXT_SUPPORTED
G
Guy Schalnat 已提交
213
   /* Check to see if we need to write text chunks */
A
Andreas Dilger 已提交
214
   for (i = 0; i < info_ptr->num_text; i++)
G
Guy Schalnat 已提交
215
   {
216
      png_debug2(2, "Writing header text chunk %d, type %d", i,
217
          info_ptr->text[i].compression);
218
      /* An internationalized chunk? */
219
      if (info_ptr->text[i].compression > 0)
220
      {
221
#ifdef PNG_WRITE_iTXt_SUPPORTED
222 223 224 225 226 227 228
         /* Write international chunk */
         png_write_iTXt(png_ptr,
             info_ptr->text[i].compression,
             info_ptr->text[i].key,
             info_ptr->text[i].lang,
             info_ptr->text[i].lang_key,
             info_ptr->text[i].text);
229
#else
230
          png_warning(png_ptr, "Unable to write international text");
231 232 233 234
#endif
          /* Mark this chunk as written */
          info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
      }
235

A
Andreas Dilger 已提交
236
      /* If we want a compressed text chunk */
237
      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
G
Guy Schalnat 已提交
238
      {
239
#ifdef PNG_WRITE_zTXt_SUPPORTED
240
         /* Write compressed chunk */
A
Andreas Dilger 已提交
241
         png_write_zTXt(png_ptr, info_ptr->text[i].key,
242 243
             info_ptr->text[i].text, 0,
             info_ptr->text[i].compression);
G
Guy Schalnat 已提交
244
#else
245
         png_warning(png_ptr, "Unable to write compressed text");
G
Guy Schalnat 已提交
246
#endif
A
Andreas Dilger 已提交
247 248 249
         /* Mark this chunk as written */
         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
      }
250

A
Andreas Dilger 已提交
251 252
      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
      {
253
#ifdef PNG_WRITE_tEXt_SUPPORTED
254
         /* Write uncompressed chunk */
A
Andreas Dilger 已提交
255
         png_write_tEXt(png_ptr, info_ptr->text[i].key,
256 257
             info_ptr->text[i].text,
             0);
258 259
         /* Mark this chunk as written */
         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
G
Guy Schalnat 已提交
260
#else
261
         /* Can't get here */
262
         png_warning(png_ptr, "Unable to write uncompressed text");
G
Guy Schalnat 已提交
263
#endif
G
Guy Schalnat 已提交
264 265
      }
   }
266 267
#endif /* tEXt */

268
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
269 270
   if (info_ptr->unknown_chunks_num)
   {
271
      png_unknown_chunk *up;
272

273
      png_debug(5, "writing extra chunks");
274

275 276 277 278
      for (up = info_ptr->unknown_chunks;
           up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
           up++)
      {
279
         int keep = png_handle_as_unknown(png_ptr, up->name);
280
         if (keep != PNG_HANDLE_CHUNK_NEVER &&
281 282
             up->location &&
             (up->location & PNG_HAVE_PLTE) &&
283
             !(up->location & PNG_HAVE_IDAT) &&
284
             !(up->location & PNG_AFTER_IDAT) &&
285 286
             ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
             (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
287 288 289
         {
            png_write_chunk(png_ptr, up->name, up->data, up->size);
         }
290
      }
291 292
   }
#endif
G
Guy Schalnat 已提交
293 294
}

A
Andreas Dilger 已提交
295
/* Writes the end of the PNG file.  If you don't want to write comments or
296 297 298 299
 * time information, you can pass NULL for info.  If you already wrote these
 * in png_write_info(), do not write them again here.  If you have long
 * comments, I suggest writing them here, and compressing them.
 */
300
void PNGAPI
A
Andreas Dilger 已提交
301
png_write_end(png_structp png_ptr, png_infop info_ptr)
G
Guy Schalnat 已提交
302
{
303
   png_debug(1, "in png_write_end");
304

305 306
   if (png_ptr == NULL)
      return;
307

G
Guy Schalnat 已提交
308 309 310
   if (!(png_ptr->mode & PNG_HAVE_IDAT))
      png_error(png_ptr, "No IDATs written into file");

311
   /* See if user wants us to write information chunks */
A
Andreas Dilger 已提交
312
   if (info_ptr != NULL)
G
Guy Schalnat 已提交
313
   {
314
#ifdef PNG_WRITE_TEXT_SUPPORTED
A
Andreas Dilger 已提交
315
      int i; /* local index variable */
316
#endif
317
#ifdef PNG_WRITE_tIME_SUPPORTED
318
      /* Check to see if user has supplied a time chunk */
319
      if ((info_ptr->valid & PNG_INFO_tIME) &&
320
          !(png_ptr->mode & PNG_WROTE_tIME))
A
Andreas Dilger 已提交
321
         png_write_tIME(png_ptr, &(info_ptr->mod_time));
322

G
Guy Schalnat 已提交
323
#endif
324
#ifdef PNG_WRITE_TEXT_SUPPORTED
325
      /* Loop through comment chunks */
A
Andreas Dilger 已提交
326
      for (i = 0; i < info_ptr->num_text; i++)
G
Guy Schalnat 已提交
327
      {
328
         png_debug2(2, "Writing trailer text chunk %d, type %d", i,
A
Andreas Dilger 已提交
329
            info_ptr->text[i].compression);
330
         /* An internationalized chunk? */
331 332
         if (info_ptr->text[i].compression > 0)
         {
333
#ifdef PNG_WRITE_iTXt_SUPPORTED
334
            /* Write international chunk */
335
            png_write_iTXt(png_ptr,
336 337 338 339 340
                info_ptr->text[i].compression,
                info_ptr->text[i].key,
                info_ptr->text[i].lang,
                info_ptr->text[i].lang_key,
                info_ptr->text[i].text);
341
#else
342
            png_warning(png_ptr, "Unable to write international text");
343
#endif
344 345
            /* Mark this chunk as written */
            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
346
         }
347

348
         else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
G
Guy Schalnat 已提交
349
         {
350
#ifdef PNG_WRITE_zTXt_SUPPORTED
351
            /* Write compressed chunk */
A
Andreas Dilger 已提交
352
            png_write_zTXt(png_ptr, info_ptr->text[i].key,
353 354
                info_ptr->text[i].text, 0,
                info_ptr->text[i].compression);
A
Andreas Dilger 已提交
355
#else
356
            png_warning(png_ptr, "Unable to write compressed text");
G
Guy Schalnat 已提交
357
#endif
A
Andreas Dilger 已提交
358 359 360
            /* Mark this chunk as written */
            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
         }
361

A
Andreas Dilger 已提交
362 363
         else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
         {
364
#ifdef PNG_WRITE_tEXt_SUPPORTED
365
            /* Write uncompressed chunk */
A
Andreas Dilger 已提交
366
            png_write_tEXt(png_ptr, info_ptr->text[i].key,
367
                info_ptr->text[i].text, 0);
A
Andreas Dilger 已提交
368
#else
369
            png_warning(png_ptr, "Unable to write uncompressed text");
G
Guy Schalnat 已提交
370
#endif
A
Andreas Dilger 已提交
371 372 373

            /* Mark this chunk as written */
            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
G
Guy Schalnat 已提交
374 375
         }
      }
376
#endif
377
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
378 379
   if (info_ptr->unknown_chunks_num)
   {
380
      png_unknown_chunk *up;
381

382
      png_debug(5, "writing extra chunks");
383

384 385 386 387
      for (up = info_ptr->unknown_chunks;
           up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
           up++)
      {
388
         int keep = png_handle_as_unknown(png_ptr, up->name);
389
         if (keep != PNG_HANDLE_CHUNK_NEVER &&
390 391
             up->location &&
             (up->location & PNG_AFTER_IDAT) &&
392 393
             ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
             (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
394
         {
395
            png_write_chunk(png_ptr, up->name, up->data, up->size);
396
         }
397
      }
398
   }
G
Guy Schalnat 已提交
399
#endif
G
Guy Schalnat 已提交
400
   }
G
Guy Schalnat 已提交
401 402 403

   png_ptr->mode |= PNG_AFTER_IDAT;

404
   /* Write end of PNG file */
G
Guy Schalnat 已提交
405
   png_write_IEND(png_ptr);
406 407 408
   /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03,
    * and restored again in libpng-1.2.30, may cause some applications that
    * do not set png_ptr->output_flush_fn to crash.  If your application
409 410
    * experiences a problem, please try building libpng with
    * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to
411 412
    * png-mng-implement at lists.sf.net .
    */
413
#ifdef PNG_WRITE_FLUSH_SUPPORTED
414
#  ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
415
   png_flush(png_ptr);
416
#  endif
417
#endif
G
Guy Schalnat 已提交
418 419
}

420
#ifdef PNG_CONVERT_tIME_SUPPORTED
421
/* "tm" structure is not supported on WindowsCE */
422
void PNGAPI
423
png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm FAR * ttime)
G
Guy Schalnat 已提交
424
{
425
   png_debug(1, "in png_convert_from_struct_tm");
426

G
Guy Schalnat 已提交
427 428 429 430 431 432
   ptime->year = (png_uint_16)(1900 + ttime->tm_year);
   ptime->month = (png_byte)(ttime->tm_mon + 1);
   ptime->day = (png_byte)ttime->tm_mday;
   ptime->hour = (png_byte)ttime->tm_hour;
   ptime->minute = (png_byte)ttime->tm_min;
   ptime->second = (png_byte)ttime->tm_sec;
G
Guy Schalnat 已提交
433 434
}

435
void PNGAPI
G
Guy Schalnat 已提交
436
png_convert_from_time_t(png_timep ptime, time_t ttime)
G
Guy Schalnat 已提交
437 438 439
{
   struct tm *tbuf;

440
   png_debug(1, "in png_convert_from_time_t");
441

G
Guy Schalnat 已提交
442 443 444
   tbuf = gmtime(&ttime);
   png_convert_from_struct_tm(ptime, tbuf);
}
G
Guy Schalnat 已提交
445
#endif
446

A
Andreas Dilger 已提交
447
/* Initialize png_ptr structure, and allocate any memory needed */
448 449 450
PNG_FUNCTION(png_structp,PNGAPI
png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
    png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)
G
Guy Schalnat 已提交
451
{
452 453
#ifdef PNG_USER_MEM_SUPPORTED
   return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
454
       warn_fn, NULL, NULL, NULL));
455 456 457
}

/* Alternate initialize png_ptr structure, and allocate any memory needed */
458 459
static void png_reset_filter_heuristics(png_structp png_ptr); /* forward decl */

460 461
PNG_FUNCTION(png_structp,PNGAPI
png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
462
    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
463
    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
464 465
{
#endif /* PNG_USER_MEM_SUPPORTED */
466
   volatile int png_cleanup_needed = 0;
467
#ifdef PNG_SETJMP_SUPPORTED
468
   volatile
469
#endif
470
   png_structp png_ptr;
471
#ifdef PNG_SETJMP_SUPPORTED
A
Andreas Dilger 已提交
472
#ifdef USE_FAR_KEYWORD
473
   jmp_buf tmp_jmpbuf;
474
#endif
A
Andreas Dilger 已提交
475
#endif
476

477
   png_debug(1, "in png_create_write_struct");
478

479
#ifdef PNG_USER_MEM_SUPPORTED
480
   png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
481
       (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr);
482
#else
483
   png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
484
#endif /* PNG_USER_MEM_SUPPORTED */
485
   if (png_ptr == NULL)
486
      return (NULL);
487

488
   /* Added at libpng-1.2.6 */
489
#ifdef PNG_SET_USER_LIMITS_SUPPORTED
490 491
   png_ptr->user_width_max = PNG_USER_WIDTH_MAX;
   png_ptr->user_height_max = PNG_USER_HEIGHT_MAX;
492 493
#endif

494 495
#ifdef PNG_SETJMP_SUPPORTED
/* Applications that neglect to set up their own setjmp() and then
496 497 498
 * encounter a png_error() will longjmp here.  Since the jmpbuf is
 * then meaningless we abort instead of returning.
 */
499
#ifdef USE_FAR_KEYWORD
500
   if (setjmp(tmp_jmpbuf))
501
#else
502 503 504
   if (setjmp(png_jmpbuf(png_ptr))) /* sets longjmp to match setjmp */
#endif
#ifdef USE_FAR_KEYWORD
505
   png_memcpy(png_jmpbuf(png_ptr), tmp_jmpbuf, png_sizeof(jmp_buf));
506
#endif
507
      PNG_ABORT();
508
#endif
509

510 511 512
#ifdef PNG_USER_MEM_SUPPORTED
   png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
#endif /* PNG_USER_MEM_SUPPORTED */
A
Andreas Dilger 已提交
513
   png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
G
Guy Schalnat 已提交
514

515 516
   if (!png_user_version_check(png_ptr, user_png_ver))
      png_cleanup_needed = 1;
517

518
   /* Initialize zbuf - compression buffer */
G
Guy Schalnat 已提交
519
   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
520

521
   if (!png_cleanup_needed)
522 523
   {
      png_ptr->zbuf = (png_bytep)png_malloc_warn(png_ptr,
524
          png_ptr->zbuf_size);
525 526 527
      if (png_ptr->zbuf == NULL)
         png_cleanup_needed = 1;
   }
528

529
   if (png_cleanup_needed)
530 531 532 533
   {
       /* Clean up PNG structure and deallocate any memory. */
       png_free(png_ptr, png_ptr->zbuf);
       png_ptr->zbuf = NULL;
534
#ifdef PNG_USER_MEM_SUPPORTED
535
       png_destroy_struct_2((png_voidp)png_ptr,
536
           (png_free_ptr)free_fn, (png_voidp)mem_ptr);
537
#else
538
       png_destroy_struct((png_voidp)png_ptr);
539
#endif
540 541
       return (NULL);
   }
G
Guy Schalnat 已提交
542

543
   png_set_write_fn(png_ptr, NULL, NULL, NULL);
G
Guy Schalnat 已提交
544

545
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
546
   png_reset_filter_heuristics(png_ptr);
547 548
#endif

549
   return (png_ptr);
G
Guy Schalnat 已提交
550 551
}

552

553 554 555 556 557
/* Write a few rows of image data.  If the image is interlaced,
 * either you will have to write the 7 sub images, or, if you
 * have called png_set_interlace_handling(), you will have to
 * "write" the image seven times.
 */
558
void PNGAPI
G
Guy Schalnat 已提交
559
png_write_rows(png_structp png_ptr, png_bytepp row,
560
    png_uint_32 num_rows)
G
Guy Schalnat 已提交
561 562
{
   png_uint_32 i; /* row counter */
G
Guy Schalnat 已提交
563
   png_bytepp rp; /* row pointer */
G
Guy Schalnat 已提交
564

565
   png_debug(1, "in png_write_rows");
566 567 568 569

   if (png_ptr == NULL)
      return;

570
   /* Loop through the rows */
G
Guy Schalnat 已提交
571 572 573 574 575 576
   for (i = 0, rp = row; i < num_rows; i++, rp++)
   {
      png_write_row(png_ptr, *rp);
   }
}

577 578 579
/* Write the image.  You only need to call this function once, even
 * if you are writing an interlaced image.
 */
580
void PNGAPI
G
Guy Schalnat 已提交
581
png_write_image(png_structp png_ptr, png_bytepp image)
G
Guy Schalnat 已提交
582 583 584
{
   png_uint_32 i; /* row index */
   int pass, num_pass; /* pass variables */
G
Guy Schalnat 已提交
585
   png_bytepp rp; /* points to current row */
G
Guy Schalnat 已提交
586

587 588 589
   if (png_ptr == NULL)
      return;

590
   png_debug(1, "in png_write_image");
591

592
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
593
   /* Initialize interlace handling.  If image is not interlaced,
594 595
    * this will set pass to 1
    */
G
Guy Schalnat 已提交
596
   num_pass = png_set_interlace_handling(png_ptr);
597 598 599
#else
   num_pass = 1;
#endif
600
   /* Loop through passes */
G
Guy Schalnat 已提交
601 602
   for (pass = 0; pass < num_pass; pass++)
   {
603
      /* Loop through image */
G
Guy Schalnat 已提交
604 605 606 607 608 609 610
      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
      {
         png_write_row(png_ptr, *rp);
      }
   }
}

611
/* Called by user to write a row of image data */
612
void PNGAPI
613
png_write_row(png_structp png_ptr, png_const_bytep row)
G
Guy Schalnat 已提交
614
{
615 616 617
   /* 1.5.6: moved from png_struct to be a local structure: */
   png_row_info row_info;

618 619
   if (png_ptr == NULL)
      return;
620

621
   png_debug2(1, "in png_write_row (row %u, pass %d)",
622
      png_ptr->row_number, png_ptr->pass);
623

624
   /* Initialize transformations and other stuff if first time */
G
Guy Schalnat 已提交
625
   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
G
Guy Schalnat 已提交
626
   {
627
      /* Make sure we wrote the header info */
628 629
      if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
         png_error(png_ptr,
630
             "png_write_info was never called before png_write_row");
631

632
      /* Check for transforms that have been set but were defined out */
633
#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
634 635
      if (png_ptr->transformations & PNG_INVERT_MONO)
         png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined");
636
#endif
637

638
#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
639 640
      if (png_ptr->transformations & PNG_FILLER)
         png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined");
641
#endif
642 643
#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
    defined(PNG_READ_PACKSWAP_SUPPORTED)
644
      if (png_ptr->transformations & PNG_PACKSWAP)
645 646
         png_warning(png_ptr,
             "PNG_WRITE_PACKSWAP_SUPPORTED is not defined");
647
#endif
648

649
#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
650 651
      if (png_ptr->transformations & PNG_PACK)
         png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");
652
#endif
653

654
#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
655 656
      if (png_ptr->transformations & PNG_SHIFT)
         png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");
657
#endif
658

659
#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
660 661
      if (png_ptr->transformations & PNG_BGR)
         png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");
662
#endif
663

664
#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
665 666
      if (png_ptr->transformations & PNG_SWAP_BYTES)
         png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");
667 668
#endif

G
Guy Schalnat 已提交
669 670 671
      png_write_start_row(png_ptr);
   }

672
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
673
   /* If interlaced and not interested in row, return */
G
Guy Schalnat 已提交
674 675 676 677
   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
   {
      switch (png_ptr->pass)
      {
G
Guy Schalnat 已提交
678
         case 0:
679
            if (png_ptr->row_number & 0x07)
G
Guy Schalnat 已提交
680 681 682 683 684
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
685

G
Guy Schalnat 已提交
686
         case 1:
687
            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
G
Guy Schalnat 已提交
688 689 690 691 692
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
693

G
Guy Schalnat 已提交
694
         case 2:
695
            if ((png_ptr->row_number & 0x07) != 4)
G
Guy Schalnat 已提交
696 697
            {
               png_write_finish_row(png_ptr);
G
Guy Schalnat 已提交
698
               return;
G
Guy Schalnat 已提交
699 700
            }
            break;
701

G
Guy Schalnat 已提交
702
         case 3:
703
            if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)
G
Guy Schalnat 已提交
704 705 706 707 708
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
709

G
Guy Schalnat 已提交
710
         case 4:
711
            if ((png_ptr->row_number & 0x03) != 2)
G
Guy Schalnat 已提交
712 713 714 715 716
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
717

G
Guy Schalnat 已提交
718
         case 5:
719
            if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)
G
Guy Schalnat 已提交
720 721 722 723 724
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
725

G
Guy Schalnat 已提交
726
         case 6:
727
            if (!(png_ptr->row_number & 0x01))
G
Guy Schalnat 已提交
728 729 730 731 732
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
733 734 735

         default: /* error: ignore it */
            break;
G
Guy Schalnat 已提交
736 737
      }
   }
G
Guy Schalnat 已提交
738
#endif
G
Guy Schalnat 已提交
739

740
   /* Set up row info for transformations */
741 742 743 744 745 746 747 748 749 750 751 752 753
   row_info.color_type = png_ptr->color_type;
   row_info.width = png_ptr->usr_width;
   row_info.channels = png_ptr->usr_channels;
   row_info.bit_depth = png_ptr->usr_bit_depth;
   row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels);
   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);

   png_debug1(3, "row_info->color_type = %d", row_info.color_type);
   png_debug1(3, "row_info->width = %u", row_info.width);
   png_debug1(3, "row_info->channels = %d", row_info.channels);
   png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth);
   png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth);
   png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes);
A
Andreas Dilger 已提交
754 755

   /* Copy user's row into buffer, leaving room for filter byte. */
756
   png_memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);
G
Guy Schalnat 已提交
757

758
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
759
   /* Handle interlacing */
G
Guy Schalnat 已提交
760
   if (png_ptr->interlaced && png_ptr->pass < 6 &&
761
       (png_ptr->transformations & PNG_INTERLACE))
G
Guy Schalnat 已提交
762
   {
763
      png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass);
764
      /* This should always get caught above, but still ... */
765
      if (!(row_info.width))
G
Guy Schalnat 已提交
766 767 768 769 770
      {
         png_write_finish_row(png_ptr);
         return;
      }
   }
G
Guy Schalnat 已提交
771
#endif
G
Guy Schalnat 已提交
772

773
#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
774
   /* Handle other transformations */
G
Guy Schalnat 已提交
775
   if (png_ptr->transformations)
776
      png_do_write_transformations(png_ptr, &row_info);
777
#endif
G
Guy Schalnat 已提交
778

779 780 781 782 783 784 785
   /* At this point the row_info pixel depth must match the 'transformed' depth,
    * which is also the output depth.
    */
   if (row_info.pixel_depth != png_ptr->pixel_depth ||
      row_info.pixel_depth != png_ptr->transformed_pixel_depth)
      png_error(png_ptr, "internal write transform logic error");

786
#ifdef PNG_MNG_FEATURES_SUPPORTED
787 788 789 790 791 792 793 794 795
   /* Write filter_method 64 (intrapixel differencing) only if
    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
    * 2. Libpng did not write a PNG signature (this filter_method is only
    *    used in PNG datastreams that are embedded in MNG datastreams) and
    * 3. The application called png_permit_mng_features with a mask that
    *    included PNG_FLAG_MNG_FILTER_64 and
    * 4. The filter_method is 64 and
    * 5. The color_type is RGB or RGBA
    */
796
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
797
       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
798 799
   {
      /* Intrapixel differencing */
800
      png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);
801 802 803
   }
#endif

A
Andreas Dilger 已提交
804
   /* Find a filter if necessary, filter the row and write it out. */
805
   png_write_find_filter(png_ptr, &row_info);
806 807 808

   if (png_ptr->write_row_fn != NULL)
      (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
G
Guy Schalnat 已提交
809 810
}

811
#ifdef PNG_WRITE_FLUSH_SUPPORTED
G
Guy Schalnat 已提交
812
/* Set the automatic flush interval or 0 to turn flushing off */
813
void PNGAPI
G
Guy Schalnat 已提交
814
png_set_flush(png_structp png_ptr, int nrows)
G
Guy Schalnat 已提交
815
{
816
   png_debug(1, "in png_set_flush");
817

818 819
   if (png_ptr == NULL)
      return;
820

G
Guy Schalnat 已提交
821
   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
G
Guy Schalnat 已提交
822 823
}

824
/* Flush the current output buffers now */
825
void PNGAPI
G
Guy Schalnat 已提交
826
png_write_flush(png_structp png_ptr)
G
Guy Schalnat 已提交
827
{
G
Guy Schalnat 已提交
828 829
   int wrote_IDAT;

830
   png_debug(1, "in png_write_flush");
831

832 833
   if (png_ptr == NULL)
      return;
834

G
Guy Schalnat 已提交
835 836
   /* We have already written out all of the data */
   if (png_ptr->row_number >= png_ptr->num_rows)
837
      return;
G
Guy Schalnat 已提交
838 839 840 841 842

   do
   {
      int ret;

843
      /* Compress the data */
A
Andreas Dilger 已提交
844
      ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
G
Guy Schalnat 已提交
845 846
      wrote_IDAT = 0;

847
      /* Check for compression errors */
G
Guy Schalnat 已提交
848 849
      if (ret != Z_OK)
      {
A
Andreas Dilger 已提交
850
         if (png_ptr->zstream.msg != NULL)
A
Andreas Dilger 已提交
851
            png_error(png_ptr, png_ptr->zstream.msg);
852

G
Guy Schalnat 已提交
853 854 855 856
         else
            png_error(png_ptr, "zlib error");
      }

A
Andreas Dilger 已提交
857
      if (!(png_ptr->zstream.avail_out))
G
Guy Schalnat 已提交
858
      {
859
         /* Write the IDAT and reset the zlib output buffer */
860
         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
G
Guy Schalnat 已提交
861 862
         wrote_IDAT = 1;
      }
863
   } while (wrote_IDAT == 1);
G
Guy Schalnat 已提交
864 865

   /* If there is any data left to be output, write it into a new IDAT */
A
Andreas Dilger 已提交
866
   if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
G
Guy Schalnat 已提交
867
   {
868
      /* Write the IDAT and reset the zlib output buffer */
G
Guy Schalnat 已提交
869
      png_write_IDAT(png_ptr, png_ptr->zbuf,
870
          png_ptr->zbuf_size - png_ptr->zstream.avail_out);
G
Guy Schalnat 已提交
871 872 873
   }
   png_ptr->flush_rows = 0;
   png_flush(png_ptr);
G
Guy Schalnat 已提交
874
}
G
Guy Schalnat 已提交
875
#endif /* PNG_WRITE_FLUSH_SUPPORTED */
G
Guy Schalnat 已提交
876

877
/* Free all memory used by the write */
878
void PNGAPI
A
Andreas Dilger 已提交
879
png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
G
Guy Schalnat 已提交
880
{
A
Andreas Dilger 已提交
881 882
   png_structp png_ptr = NULL;
   png_infop info_ptr = NULL;
883 884
#ifdef PNG_USER_MEM_SUPPORTED
   png_free_ptr free_fn = NULL;
885
   png_voidp mem_ptr = NULL;
886
#endif
A
Andreas Dilger 已提交
887

888
   png_debug(1, "in png_destroy_write_struct");
889

A
Andreas Dilger 已提交
890
   if (png_ptr_ptr != NULL)
A
Andreas Dilger 已提交
891 892
      png_ptr = *png_ptr_ptr;

893 894 895 896 897 898 899 900
#ifdef PNG_USER_MEM_SUPPORTED
   if (png_ptr != NULL)
   {
      free_fn = png_ptr->free_fn;
      mem_ptr = png_ptr->mem_ptr;
   }
#endif

A
Andreas Dilger 已提交
901
   if (info_ptr_ptr != NULL)
A
Andreas Dilger 已提交
902 903
      info_ptr = *info_ptr_ptr;

A
Andreas Dilger 已提交
904
   if (info_ptr != NULL)
G
Guy Schalnat 已提交
905
   {
906 907
      if (png_ptr != NULL)
      {
908
         png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
909

910
#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
911 912 913 914 915
         if (png_ptr->num_chunk_list)
         {
            png_free(png_ptr, png_ptr->chunk_list);
            png_ptr->num_chunk_list = 0;
         }
916
#endif
917
      }
918

919
#ifdef PNG_USER_MEM_SUPPORTED
920
      png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn,
921
          (png_voidp)mem_ptr);
922
#else
A
Andreas Dilger 已提交
923
      png_destroy_struct((png_voidp)info_ptr);
924
#endif
925
      *info_ptr_ptr = NULL;
G
Guy Schalnat 已提交
926 927
   }

A
Andreas Dilger 已提交
928
   if (png_ptr != NULL)
G
Guy Schalnat 已提交
929
   {
A
Andreas Dilger 已提交
930
      png_write_destroy(png_ptr);
931
#ifdef PNG_USER_MEM_SUPPORTED
932
      png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn,
933
          (png_voidp)mem_ptr);
934
#else
A
Andreas Dilger 已提交
935
      png_destroy_struct((png_voidp)png_ptr);
936
#endif
937
      *png_ptr_ptr = NULL;
G
Guy Schalnat 已提交
938 939 940
   }
}

G
Guy Schalnat 已提交
941

A
Andreas Dilger 已提交
942
/* Free any memory used in png_ptr struct (old method) */
943
void /* PRIVATE */
G
Guy Schalnat 已提交
944
png_write_destroy(png_structp png_ptr)
G
Guy Schalnat 已提交
945
{
946
#ifdef PNG_SETJMP_SUPPORTED
947
   jmp_buf tmp_jmp; /* Save jump buffer */
948
#endif
G
Guy Schalnat 已提交
949
   png_error_ptr error_fn;
950
#ifdef PNG_WARNINGS_SUPPORTED
G
Guy Schalnat 已提交
951
   png_error_ptr warning_fn;
952
#endif
G
Guy Schalnat 已提交
953
   png_voidp error_ptr;
954 955 956
#ifdef PNG_USER_MEM_SUPPORTED
   png_free_ptr free_fn;
#endif
G
Guy Schalnat 已提交
957

958
   png_debug(1, "in png_write_destroy");
959 960

   /* Free any memory zlib uses */
961 962
   if (png_ptr->zlib_state != PNG_ZLIB_UNINITIALIZED)
      deflateEnd(&png_ptr->zstream);
G
Guy Schalnat 已提交
963

964
   /* Free our memory.  png_free checks NULL for us. */
A
Andreas Dilger 已提交
965 966
   png_free(png_ptr, png_ptr->zbuf);
   png_free(png_ptr, png_ptr->row_buf);
967
#ifdef PNG_WRITE_FILTER_SUPPORTED
A
Andreas Dilger 已提交
968 969 970 971 972
   png_free(png_ptr, png_ptr->prev_row);
   png_free(png_ptr, png_ptr->sub_row);
   png_free(png_ptr, png_ptr->up_row);
   png_free(png_ptr, png_ptr->avg_row);
   png_free(png_ptr, png_ptr->paeth_row);
973
#endif
974

975
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
976 977
   /* Use this to save a little code space, it doesn't free the filter_costs */
   png_reset_filter_heuristics(png_ptr);
A
Andreas Dilger 已提交
978 979
   png_free(png_ptr, png_ptr->filter_costs);
   png_free(png_ptr, png_ptr->inv_filter_costs);
980
#endif
G
Guy Schalnat 已提交
981

982
#ifdef PNG_SETJMP_SUPPORTED
983
   /* Reset structure */
984
   png_memcpy(tmp_jmp, png_ptr->longjmp_buffer, png_sizeof(jmp_buf));
985
#endif
G
Guy Schalnat 已提交
986 987

   error_fn = png_ptr->error_fn;
988
#ifdef PNG_WARNINGS_SUPPORTED
G
Guy Schalnat 已提交
989
   warning_fn = png_ptr->warning_fn;
990
#endif
G
Guy Schalnat 已提交
991
   error_ptr = png_ptr->error_ptr;
992 993 994
#ifdef PNG_USER_MEM_SUPPORTED
   free_fn = png_ptr->free_fn;
#endif
G
Guy Schalnat 已提交
995

996
   png_memset(png_ptr, 0, png_sizeof(png_struct));
G
Guy Schalnat 已提交
997 998

   png_ptr->error_fn = error_fn;
999
#ifdef PNG_WARNINGS_SUPPORTED
G
Guy Schalnat 已提交
1000
   png_ptr->warning_fn = warning_fn;
1001
#endif
G
Guy Schalnat 已提交
1002
   png_ptr->error_ptr = error_ptr;
1003 1004 1005
#ifdef PNG_USER_MEM_SUPPORTED
   png_ptr->free_fn = free_fn;
#endif
G
Guy Schalnat 已提交
1006

1007
#ifdef PNG_SETJMP_SUPPORTED
1008
   png_memcpy(png_ptr->longjmp_buffer, tmp_jmp, png_sizeof(jmp_buf));
1009
#endif
G
Guy Schalnat 已提交
1010
}
G
Guy Schalnat 已提交
1011

A
Andreas Dilger 已提交
1012
/* Allow the application to select one or more row filters to use. */
1013
void PNGAPI
G
Guy Schalnat 已提交
1014
png_set_filter(png_structp png_ptr, int method, int filters)
G
Guy Schalnat 已提交
1015
{
1016
   png_debug(1, "in png_set_filter");
1017

1018 1019
   if (png_ptr == NULL)
      return;
1020

1021
#ifdef PNG_MNG_FEATURES_SUPPORTED
1022
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
1023 1024
       (method == PNG_INTRAPIXEL_DIFFERENCING))
      method = PNG_FILTER_TYPE_BASE;
1025

1026
#endif
A
Andreas Dilger 已提交
1027
   if (method == PNG_FILTER_TYPE_BASE)
G
Guy Schalnat 已提交
1028 1029 1030
   {
      switch (filters & (PNG_ALL_FILTERS | 0x07))
      {
1031
#ifdef PNG_WRITE_FILTER_SUPPORTED
G
Guy Schalnat 已提交
1032 1033
         case 5:
         case 6:
A
Andreas Dilger 已提交
1034
         case 7: png_warning(png_ptr, "Unknown row filter for method 0");
1035
#endif /* PNG_WRITE_FILTER_SUPPORTED */
1036
         case PNG_FILTER_VALUE_NONE:
1037
            png_ptr->do_filter = PNG_FILTER_NONE; break;
1038

1039
#ifdef PNG_WRITE_FILTER_SUPPORTED
1040
         case PNG_FILTER_VALUE_SUB:
1041
            png_ptr->do_filter = PNG_FILTER_SUB; break;
1042

1043
         case PNG_FILTER_VALUE_UP:
1044
            png_ptr->do_filter = PNG_FILTER_UP; break;
1045

1046
         case PNG_FILTER_VALUE_AVG:
1047
            png_ptr->do_filter = PNG_FILTER_AVG; break;
1048

1049
         case PNG_FILTER_VALUE_PAETH:
1050
            png_ptr->do_filter = PNG_FILTER_PAETH; break;
1051

1052 1053
         default:
            png_ptr->do_filter = (png_byte)filters; break;
1054
#else
1055 1056
         default:
            png_warning(png_ptr, "Unknown row filter for method 0");
1057
#endif /* PNG_WRITE_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
1058 1059
      }

A
Andreas Dilger 已提交
1060 1061 1062 1063 1064 1065 1066 1067
      /* If we have allocated the row_buf, this means we have already started
       * with the image and we should have allocated all of the filter buffers
       * that have been selected.  If prev_row isn't already allocated, then
       * it is too late to start using the filters that need it, since we
       * will be missing the data in the previous row.  If an application
       * wants to start and stop using particular filters during compression,
       * it should start out with all of the filters, and then add and
       * remove them after the start of compression.
G
Guy Schalnat 已提交
1068
       */
A
Andreas Dilger 已提交
1069
      if (png_ptr->row_buf != NULL)
G
Guy Schalnat 已提交
1070
      {
1071
#ifdef PNG_WRITE_FILTER_SUPPORTED
1072
         if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)
G
Guy Schalnat 已提交
1073
         {
A
Andreas Dilger 已提交
1074
            png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
1075
                (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
1076
            png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
G
Guy Schalnat 已提交
1077 1078
         }

1079
         if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)
G
Guy Schalnat 已提交
1080
         {
A
Andreas Dilger 已提交
1081
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
1082
            {
A
Andreas Dilger 已提交
1083
               png_warning(png_ptr, "Can't add Up filter after starting");
1084 1085
               png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
                   ~PNG_FILTER_UP);
G
Guy Schalnat 已提交
1086
            }
1087

G
Guy Schalnat 已提交
1088 1089
            else
            {
A
Andreas Dilger 已提交
1090
               png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
1091
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
1092
               png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
G
Guy Schalnat 已提交
1093 1094 1095
            }
         }

1096
         if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)
G
Guy Schalnat 已提交
1097
         {
A
Andreas Dilger 已提交
1098
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
1099
            {
A
Andreas Dilger 已提交
1100
               png_warning(png_ptr, "Can't add Average filter after starting");
1101 1102
               png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
                   ~PNG_FILTER_AVG);
G
Guy Schalnat 已提交
1103
            }
1104

G
Guy Schalnat 已提交
1105 1106
            else
            {
A
Andreas Dilger 已提交
1107
               png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
1108
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
1109
               png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
G
Guy Schalnat 已提交
1110 1111 1112
            }
         }

1113
         if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&
A
Andreas Dilger 已提交
1114
             png_ptr->paeth_row == NULL)
G
Guy Schalnat 已提交
1115
         {
A
Andreas Dilger 已提交
1116
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
1117 1118
            {
               png_warning(png_ptr, "Can't add Paeth filter after starting");
1119
               png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
G
Guy Schalnat 已提交
1120
            }
1121

G
Guy Schalnat 已提交
1122 1123
            else
            {
1124
               png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
1125
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
1126
               png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
G
Guy Schalnat 已提交
1127 1128 1129 1130
            }
         }

         if (png_ptr->do_filter == PNG_NO_FILTERS)
1131
#endif /* PNG_WRITE_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
1132 1133 1134 1135
            png_ptr->do_filter = PNG_FILTER_NONE;
      }
   }
   else
A
Andreas Dilger 已提交
1136 1137 1138 1139 1140 1141 1142 1143
      png_error(png_ptr, "Unknown custom filter method");
}

/* This allows us to influence the way in which libpng chooses the "best"
 * filter for the current scanline.  While the "minimum-sum-of-absolute-
 * differences metric is relatively fast and effective, there is some
 * question as to whether it can be improved upon by trying to keep the
 * filtered data going to zlib more consistent, hopefully resulting in
1144 1145
 * better compression.
 */
1146
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED      /* GRR 970116 */
1147
/* Convenience reset API. */
1148 1149
static void
png_reset_filter_heuristics(png_structp png_ptr)
A
Andreas Dilger 已提交
1150
{
1151 1152 1153 1154 1155 1156 1157 1158
   /* Clear out any old values in the 'weights' - this must be done because if
    * the app calls set_filter_heuristics multiple times with different
    * 'num_weights' values we would otherwise potentially have wrong sized
    * arrays.
    */
   png_ptr->num_prev_filters = 0;
   png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
   if (png_ptr->prev_filters != NULL)
A
Andreas Dilger 已提交
1159
   {
1160 1161 1162
      png_bytep old = png_ptr->prev_filters;
      png_ptr->prev_filters = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1163
   }
1164
   if (png_ptr->filter_weights != NULL)
A
Andreas Dilger 已提交
1165
   {
1166 1167 1168
      png_uint_16p old = png_ptr->filter_weights;
      png_ptr->filter_weights = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1169 1170
   }

1171
   if (png_ptr->inv_filter_weights != NULL)
A
Andreas Dilger 已提交
1172
   {
1173 1174 1175
      png_uint_16p old = png_ptr->inv_filter_weights;
      png_ptr->inv_filter_weights = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1176 1177
   }

1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
   /* Leave the filter_costs - this array is fixed size. */
}

static int
png_init_filter_heuristics(png_structp png_ptr, int heuristic_method,
   int num_weights)
{
   if (png_ptr == NULL)
      return 0;

   /* Clear out the arrays */
   png_reset_filter_heuristics(png_ptr);
A
Andreas Dilger 已提交
1190

1191 1192 1193 1194 1195
   /* Check arguments; the 'reset' function makes the correct settings for the
    * unweighted case, but we must handle the weight case by initializing the
    * arrays for the caller.
    */
   if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
A
Andreas Dilger 已提交
1196
   {
1197 1198 1199
      int i;

      if (num_weights > 0)
A
Andreas Dilger 已提交
1200 1201
      {
         png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
1202
             (png_uint_32)(png_sizeof(png_byte) * num_weights));
A
Andreas Dilger 已提交
1203

1204 1205
         /* To make sure that the weighting starts out fairly */
         for (i = 0; i < num_weights; i++)
A
Andreas Dilger 已提交
1206
         {
1207
            png_ptr->prev_filters[i] = 255;
A
Andreas Dilger 已提交
1208 1209
         }

1210 1211
         png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
A
Andreas Dilger 已提交
1212

1213 1214
         png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
1215

1216 1217 1218 1219 1220
         for (i = 0; i < num_weights; i++)
         {
            png_ptr->inv_filter_weights[i] =
            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
         }
1221

1222 1223
         /* Safe to set this now */
         png_ptr->num_prev_filters = (png_byte)num_weights;
A
Andreas Dilger 已提交
1224 1225
      }

1226 1227 1228 1229
      /* If, in the future, there are other filter methods, this would
       * need to be based on png_ptr->filter.
       */
      if (png_ptr->filter_costs == NULL)
A
Andreas Dilger 已提交
1230
      {
1231 1232
         png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
1233

1234 1235
         png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
1236
      }
1237

1238 1239
      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
      {
1240 1241
         png_ptr->inv_filter_costs[i] =
         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
A
Andreas Dilger 已提交
1242
      }
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258

      /* All the arrays are inited, safe to set this: */
      png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_WEIGHTED;

      /* Return the 'ok' code. */
      return 1;
   }
   else if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT ||
      heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
   {
      return 1;
   }
   else
   {
      png_warning(png_ptr, "Unknown filter heuristic method");
      return 0;
A
Andreas Dilger 已提交
1259
   }
1260
}
A
Andreas Dilger 已提交
1261

1262 1263 1264 1265
/* Provide floating and fixed point APIs */
#ifdef PNG_FLOATING_POINT_SUPPORTED
void PNGAPI
png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
1266 1267
    int num_weights, png_const_doublep filter_weights,
    png_const_doublep filter_costs)
1268 1269 1270 1271 1272
{
   png_debug(1, "in png_set_filter_heuristics");

   /* The internal API allocates all the arrays and ensures that the elements of
    * those arrays are set to the default value.
A
Andreas Dilger 已提交
1273
    */
1274 1275
   if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
      return;
A
Andreas Dilger 已提交
1276

1277 1278 1279 1280 1281 1282
   /* If using the weighted method copy in the weights. */
   if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
   {
      int i;
      for (i = 0; i < num_weights; i++)
      {
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
         if (filter_weights[i] <= 0.0)
         {
            png_ptr->inv_filter_weights[i] =
            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
         }

         else
         {
            png_ptr->inv_filter_weights[i] =
                (png_uint_16)(PNG_WEIGHT_FACTOR*filter_weights[i]+.5);

            png_ptr->filter_weights[i] =
                (png_uint_16)(PNG_WEIGHT_FACTOR/filter_weights[i]+.5);
         }
1297
      }
A
Andreas Dilger 已提交
1298

1299 1300 1301 1302 1303 1304 1305 1306
      /* Here is where we set the relative costs of the different filters.  We
       * should take the desired compression level into account when setting
       * the costs, so that Paeth, for instance, has a high relative cost at low
       * compression levels, while it has a lower relative cost at higher
       * compression settings.  The filter types are in order of increasing
       * relative cost, so it would be possible to do this with an algorithm.
       */
      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) if (filter_costs[i] >= 1.0)
A
Andreas Dilger 已提交
1307
      {
1308 1309
         png_ptr->inv_filter_costs[i] =
             (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5);
1310

1311 1312
         png_ptr->filter_costs[i] =
             (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5);
A
Andreas Dilger 已提交
1313 1314
      }
   }
1315 1316
}
#endif /* FLOATING_POINT */
A
Andreas Dilger 已提交
1317

1318 1319 1320
#ifdef PNG_FIXED_POINT_SUPPORTED
void PNGAPI
png_set_filter_heuristics_fixed(png_structp png_ptr, int heuristic_method,
1321 1322
    int num_weights, png_const_fixed_point_p filter_weights,
    png_const_fixed_point_p filter_costs)
1323 1324 1325 1326 1327
{
   png_debug(1, "in png_set_filter_heuristics_fixed");

   /* The internal API allocates all the arrays and ensures that the elements of
    * those arrays are set to the default value.
A
Andreas Dilger 已提交
1328
    */
1329 1330 1331 1332 1333
   if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
      return;

   /* If using the weighted method copy in the weights. */
   if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
A
Andreas Dilger 已提交
1334
   {
1335 1336
      int i;
      for (i = 0; i < num_weights; i++)
A
Andreas Dilger 已提交
1337
      {
1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351
         if (filter_weights[i] <= 0)
         {
            png_ptr->inv_filter_weights[i] =
            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
         }

         else
         {
            png_ptr->inv_filter_weights[i] = (png_uint_16)
               ((PNG_WEIGHT_FACTOR*filter_weights[i]+PNG_FP_HALF)/PNG_FP_1);

            png_ptr->filter_weights[i] = (png_uint_16)((PNG_WEIGHT_FACTOR*
               PNG_FP_1+(filter_weights[i]/2))/filter_weights[i]);
         }
A
Andreas Dilger 已提交
1352
      }
1353

1354 1355 1356 1357 1358 1359 1360 1361 1362
      /* Here is where we set the relative costs of the different filters.  We
       * should take the desired compression level into account when setting
       * the costs, so that Paeth, for instance, has a high relative cost at low
       * compression levels, while it has a lower relative cost at higher
       * compression settings.  The filter types are in order of increasing
       * relative cost, so it would be possible to do this with an algorithm.
       */
      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
         if (filter_costs[i] >= PNG_FP_1)
A
Andreas Dilger 已提交
1363
      {
1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
         png_uint_32 tmp;

         /* Use a 32 bit unsigned temporary here because otherwise the
          * intermediate value will be a 32 bit *signed* integer (ANSI rules)
          * and this will get the wrong answer on division.
          */
         tmp = PNG_COST_FACTOR*PNG_FP_1 + (filter_costs[i]/2);
         tmp /= filter_costs[i];

         png_ptr->inv_filter_costs[i] = (png_uint_16)tmp;

         tmp = PNG_COST_FACTOR * filter_costs[i] + PNG_FP_HALF;
         tmp /= PNG_FP_1;
1377

1378
         png_ptr->filter_costs[i] = (png_uint_16)tmp;
A
Andreas Dilger 已提交
1379 1380
      }
   }
G
Guy Schalnat 已提交
1381
}
1382
#endif /* FIXED_POINT */
A
Andreas Dilger 已提交
1383
#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
1384

1385
void PNGAPI
G
Guy Schalnat 已提交
1386
png_set_compression_level(png_structp png_ptr, int level)
G
Guy Schalnat 已提交
1387
{
1388
   png_debug(1, "in png_set_compression_level");
1389

1390 1391
   if (png_ptr == NULL)
      return;
1392

G
Guy Schalnat 已提交
1393
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
G
Guy Schalnat 已提交
1394 1395 1396
   png_ptr->zlib_level = level;
}

1397
void PNGAPI
G
Guy Schalnat 已提交
1398
png_set_compression_mem_level(png_structp png_ptr, int mem_level)
G
Guy Schalnat 已提交
1399
{
1400
   png_debug(1, "in png_set_compression_mem_level");
1401

1402 1403
   if (png_ptr == NULL)
      return;
1404

G
Guy Schalnat 已提交
1405
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
G
Guy Schalnat 已提交
1406
   png_ptr->zlib_mem_level = mem_level;
G
Guy Schalnat 已提交
1407 1408
}

1409
void PNGAPI
G
Guy Schalnat 已提交
1410
png_set_compression_strategy(png_structp png_ptr, int strategy)
G
Guy Schalnat 已提交
1411
{
1412
   png_debug(1, "in png_set_compression_strategy");
1413

1414 1415
   if (png_ptr == NULL)
      return;
1416

G
Guy Schalnat 已提交
1417
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
G
Guy Schalnat 已提交
1418 1419 1420
   png_ptr->zlib_strategy = strategy;
}

1421 1422 1423
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
 * smaller value of window_bits if it can do so safely.
 */
1424
void PNGAPI
G
Guy Schalnat 已提交
1425
png_set_compression_window_bits(png_structp png_ptr, int window_bits)
G
Guy Schalnat 已提交
1426
{
1427 1428
   if (png_ptr == NULL)
      return;
1429

G
Guy Schalnat 已提交
1430 1431
   if (window_bits > 15)
      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
1432

1433 1434
   else if (window_bits < 8)
      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
1435

1436
#ifndef WBITS_8_OK
1437
   /* Avoid libpng bug with 256-byte windows */
1438
   if (window_bits == 8)
1439 1440 1441 1442
      {
        png_warning(png_ptr, "Compression window is being reset to 512");
        window_bits = 9;
      }
1443

1444
#endif
G
Guy Schalnat 已提交
1445
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
G
Guy Schalnat 已提交
1446 1447 1448
   png_ptr->zlib_window_bits = window_bits;
}

1449
void PNGAPI
G
Guy Schalnat 已提交
1450
png_set_compression_method(png_structp png_ptr, int method)
G
Guy Schalnat 已提交
1451
{
1452
   png_debug(1, "in png_set_compression_method");
1453

1454 1455
   if (png_ptr == NULL)
      return;
1456

G
Guy Schalnat 已提交
1457 1458
   if (method != 8)
      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
1459

G
Guy Schalnat 已提交
1460
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
G
Guy Schalnat 已提交
1461
   png_ptr->zlib_method = method;
G
Guy Schalnat 已提交
1462 1463
}

1464
/* The following were added to libpng-1.5.4 */
1465
#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501
void PNGAPI
png_set_text_compression_level(png_structp png_ptr, int level)
{
   png_debug(1, "in png_set_text_compression_level");

   if (png_ptr == NULL)
      return;

   png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_LEVEL;
   png_ptr->zlib_text_level = level;
}

void PNGAPI
png_set_text_compression_mem_level(png_structp png_ptr, int mem_level)
{
   png_debug(1, "in png_set_text_compression_mem_level");

   if (png_ptr == NULL)
      return;

   png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_MEM_LEVEL;
   png_ptr->zlib_text_mem_level = mem_level;
}

void PNGAPI
png_set_text_compression_strategy(png_structp png_ptr, int strategy)
{
   png_debug(1, "in png_set_text_compression_strategy");

   if (png_ptr == NULL)
      return;

   png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_STRATEGY;
   png_ptr->zlib_text_strategy = strategy;
}

1502 1503 1504
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
 * smaller value of window_bits if it can do so safely.
 */
1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543
void PNGAPI
png_set_text_compression_window_bits(png_structp png_ptr, int window_bits)
{
   if (png_ptr == NULL)
      return;

   if (window_bits > 15)
      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");

   else if (window_bits < 8)
      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");

#ifndef WBITS_8_OK
   /* Avoid libpng bug with 256-byte windows */
   if (window_bits == 8)
      {
        png_warning(png_ptr, "Text compression window is being reset to 512");
        window_bits = 9;
      }

#endif
   png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_WINDOW_BITS;
   png_ptr->zlib_text_window_bits = window_bits;
}

void PNGAPI
png_set_text_compression_method(png_structp png_ptr, int method)
{
   png_debug(1, "in png_set_text_compression_method");

   if (png_ptr == NULL)
      return;

   if (method != 8)
      png_warning(png_ptr, "Only compression method 8 is supported by PNG");

   png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_METHOD;
   png_ptr->zlib_text_method = method;
}
1544
#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
1545
/* end of API added to libpng-1.5.4 */
1546

1547
void PNGAPI
1548 1549
png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
{
1550 1551
   if (png_ptr == NULL)
      return;
1552

1553 1554 1555
   png_ptr->write_row_fn = write_row_fn;
}

1556
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
1557
void PNGAPI
1558
png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
1559
    write_user_transform_fn)
1560
{
1561
   png_debug(1, "in png_set_write_user_transform_fn");
1562

1563 1564
   if (png_ptr == NULL)
      return;
1565

1566 1567 1568 1569
   png_ptr->transformations |= PNG_USER_TRANSFORM;
   png_ptr->write_user_transform_fn = write_user_transform_fn;
}
#endif
1570 1571


1572
#ifdef PNG_INFO_IMAGE_SUPPORTED
1573 1574
void PNGAPI
png_write_png(png_structp png_ptr, png_infop info_ptr,
1575
    int transforms, voidp params)
1576
{
1577 1578
   if (png_ptr == NULL || info_ptr == NULL)
      return;
1579 1580 1581 1582 1583 1584

   /* Write the file header information. */
   png_write_info(png_ptr, info_ptr);

   /* ------ these transformations don't touch the info structure ------- */

1585
#ifdef PNG_WRITE_INVERT_SUPPORTED
1586
   /* Invert monochrome pixels */
1587
   if (transforms & PNG_TRANSFORM_INVERT_MONO)
1588
      png_set_invert_mono(png_ptr);
1589 1590
#endif

1591
#ifdef PNG_WRITE_SHIFT_SUPPORTED
1592 1593 1594 1595
   /* Shift the pixels up to a legal bit depth and fill in
    * as appropriate to correctly scale the image.
    */
   if ((transforms & PNG_TRANSFORM_SHIFT)
1596
       && (info_ptr->valid & PNG_INFO_sBIT))
1597
      png_set_shift(png_ptr, &info_ptr->sig_bit);
1598 1599
#endif

1600
#ifdef PNG_WRITE_PACK_SUPPORTED
1601
   /* Pack pixels into bytes */
1602 1603 1604 1605
   if (transforms & PNG_TRANSFORM_PACKING)
       png_set_packing(png_ptr);
#endif

1606
#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
1607
   /* Swap location of alpha bytes from ARGB to RGBA */
1608
   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
1609
      png_set_swap_alpha(png_ptr);
1610 1611
#endif

1612
#ifdef PNG_WRITE_FILLER_SUPPORTED
1613
   /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */
1614
   if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)
1615
      png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
1616

1617
   else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)
1618
      png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
1619 1620
#endif

1621
#ifdef PNG_WRITE_BGR_SUPPORTED
1622
   /* Flip BGR pixels to RGB */
1623
   if (transforms & PNG_TRANSFORM_BGR)
1624
      png_set_bgr(png_ptr);
1625 1626
#endif

1627
#ifdef PNG_WRITE_SWAP_SUPPORTED
1628
   /* Swap bytes of 16-bit files to most significant byte first */
1629
   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
1630
      png_set_swap(png_ptr);
1631 1632
#endif

1633
#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
1634
   /* Swap bits of 1, 2, 4 bit packed pixel formats */
1635
   if (transforms & PNG_TRANSFORM_PACKSWAP)
1636
      png_set_packswap(png_ptr);
1637 1638
#endif

1639
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
1640 1641 1642 1643 1644
   /* Invert the alpha channel from opacity to transparency */
   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
      png_set_invert_alpha(png_ptr);
#endif

1645 1646
   /* ----------------------- end of transformations ------------------- */

1647
   /* Write the bits */
1648 1649 1650 1651 1652
   if (info_ptr->valid & PNG_INFO_IDAT)
       png_write_image(png_ptr, info_ptr->row_pointers);

   /* It is REQUIRED to call this to finish writing the rest of the file */
   png_write_end(png_ptr, info_ptr);
1653

1654 1655
   PNG_UNUSED(transforms)   /* Quiet compiler warnings */
   PNG_UNUSED(params)
1656 1657
}
#endif
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674


#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
#ifdef PNG_STDIO_SUPPORTED /* currently required for png_image_write_* */
/* Initialize the write structure - general purpose utility. */
static int
png_image_write_init(png_imagep image)
{
   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image,
          png_safe_error, png_safe_warning);

   if (png_ptr != NULL)
   {
      png_infop info_ptr = png_create_info_struct(png_ptr);

      if (info_ptr != NULL)
      {
1675 1676
         png_controlp control = png_voidcast(png_controlp,
            png_malloc_warn(png_ptr, sizeof *control));
1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713

         if (control != NULL)
         {
            memset(control, 0, sizeof *control);

            control->png_ptr = png_ptr;
            control->info_ptr = info_ptr;
            control->for_write = 1;

            image->opaque = control;
            return 1;
         }

         /* Error clean up */
         png_destroy_info_struct(png_ptr, &info_ptr);
      }

      png_destroy_write_struct(&png_ptr, NULL);
   }

   return png_image_error(image, "png_image_read: out of memory");
}

/* Arguments to png_image_write_main: */
typedef struct
{
   /* Arguments: */
   png_imagep      image;
   png_const_voidp buffer;
   png_int_32      row_stride;
   int             convert_to_8bit;
   /* Local variables: */
   png_const_voidp first_row;
   ptrdiff_t       row_bytes;
   png_voidp       local_row;
} png_image_write_control;

1714 1715
/* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to
 * do any necessary byte swapping.  The component order is defined by the
1716 1717 1718 1719 1720
 * png_image format value.
 */
static int
png_write_image_16bit(png_voidp argument)
{
1721 1722
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1723 1724 1725
   png_imagep image = display->image;
   png_structp png_ptr = image->opaque->png_ptr;

1726 1727 1728
   png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,
      display->first_row);
   png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row);
1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751
   png_uint_16p row_end;
   int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;
   int aindex = 0;
   png_uint_32 y = image->height;

   if (image->format & PNG_FORMAT_FLAG_ALPHA)
   {
      if (image->format & PNG_FORMAT_FLAG_AFIRST)
      {
         aindex = -1;
         ++input_row; /* To point to the first component */
         ++output_row;
      }

      else
         aindex = channels;
   }

   else
      png_error(png_ptr, "png_write_image: internal call error");

   /* Work out the output row end and count over this, note that the increment
    * above to 'row' means that row_end can actually be beyond the end of the
1752
    * row; this is correct.
1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769
    */
   row_end = output_row + image->width * (channels+1);

   while (y-- > 0)
   {
      png_const_uint_16p in_ptr = input_row;
      png_uint_16p out_ptr = output_row;

      while (out_ptr < row_end)
      {
         png_uint_16 alpha = in_ptr[aindex];
         png_uint_32 reciprocal = 0;
         int c;

         out_ptr[aindex] = alpha;

         /* Calculate a reciprocal.  The correct calculation is simply
1770
          * component/alpha*65535 << 15. (I.e. 15 bits of precision); this
1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810
          * allows correct rounding by adding .5 before the shift.  'reciprocal'
          * is only initialized when required.
          */
         if (alpha > 0 && alpha < 65535)
            reciprocal = ((0xffff<<15)+(alpha>>1))/alpha;

         c = channels;
         do /* always at least one channel */
         {
            png_uint_16 component = *in_ptr++;

            /* The following gives 65535 for an alpha of 0, which is fine,
             * otherwise if 0/0 is represented as some other value there is more
             * likely to be a discontinuity which will probably damage
             * compression when moving from a fully transparent area to a
             * nearly transparent one.  (The assumption here is that opaque
             * areas tend not to be 0 intensity.)
             */
            if (component >= alpha)
               component = 65535;

            /* component<alpha, so component/alpha is less than one and
             * component*reciprocal is less than 2^31.
             */
            else if (component > 0 && alpha < 65535)
            {
               png_uint_32 calc = component * reciprocal;
               calc += 16384; /* round to nearest */
               component = (png_uint_16)(calc >> 15);
            }

            *out_ptr++ = component;
         }
         while (--c > 0);

         /* Skip to next component (skip the intervening alpha channel) */
         ++in_ptr;
         ++out_ptr;
      }

1811
      png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));
1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824
      input_row += display->row_bytes/(sizeof (png_uint_16));
   }

   return 1;
}

/* Given 16-bit input (1 to 4 channels) write 8-bit output.  If an alpha channel
 * is present it must be removed from the components, the components are then
 * written in sRGB encoding.  No components are added or removed.
 */
static int
png_write_image_8bit(png_voidp argument)
{
1825 1826
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1827 1828 1829
   png_imagep image = display->image;
   png_structp png_ptr = image->opaque->png_ptr;

1830 1831 1832
   png_const_uint_16p input_row = png_voidcast(png_const_uint_16p,
      display->first_row);
   png_bytep output_row = png_voidcast(png_bytep, display->local_row);
1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885
   png_uint_32 y = image->height;
   int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;

   if (image->format & PNG_FORMAT_FLAG_ALPHA)
   {
      png_bytep row_end;
      int aindex;

      if (image->format & PNG_FORMAT_FLAG_AFIRST)
      {
         aindex = -1;
         ++input_row; /* To point to the first component */
         ++output_row;
      }

      else
         aindex = channels;

      /* Use row_end in place of a loop counter: */
      row_end = output_row + image->width * (channels+1);

      while (y-- > 0)
      {
         png_const_uint_16p in_ptr = input_row;
         png_bytep out_ptr = output_row;

         if (aindex != 0) while (out_ptr < row_end) /* Alpha channel case */
         {
            png_uint_16 alpha = in_ptr[aindex];
            png_uint_32 reciprocal = 0;
            int c;

            /* Scale and write the alpha channel.  See pngrtran.c
             * png_do_scale_16_to_8 for a discussion of this calculation.  The
             * code here has machine native values, so use:
             *
             *    (V * 255 + 32895) >> 16
             */
            out_ptr[aindex] = (png_byte)((alpha * 255 + 32895) >> 16);

            /* Calculate a reciprocal.  As above the calculation can be done to
             * 15 bits of accuracy, however the output needs to be scaled in the
             * range 0..255*65535, so include that scaling here.
             */
            if (alpha > 0 && alpha < 65535)
               reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha;

            c = channels;
            do /* always at least one channel */
            {
               /* Need 32 bit accuracy in the sRGB tables */
               png_uint_32 component = *in_ptr++;

1886
               /* The following gives 1.0 for an alpha of 0, which is fine,
1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898
                * otherwise if 0/0 is represented as some other value there is
                * more likely to be a discontinuity which will probably damage
                * compression when moving from a fully transparent area to a
                * nearly transparent one.  (The assumption here is that opaque
                * areas tend not to be 0 intensity.)
                */
               if (component >= alpha)
                  *out_ptr++ = 255;

               /* component<alpha, so component/alpha is less than one and
                * component*reciprocal is less than 2^31.
                */
1899
               else if (component > 0)
1900
               {
1901 1902 1903 1904 1905 1906 1907 1908 1909
                  if (alpha < 65535)
                  {
                     component *= reciprocal;
                     component += 64; /* round to nearest */
                     component >>= 7;
                  }

                  else
                     component *= 255;
1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923

                  /* Convert the component to sRGB. */
                  *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component);
               }

               else
                  *out_ptr++ = 0;
            }
            while (--c > 0);

            /* Skip to next component (skip the intervening alpha channel) */
            ++in_ptr;
            ++out_ptr;
         } /* while out_ptr < row_end */
1924

1925 1926
         png_write_row(png_ptr, png_voidcast(png_const_bytep,
            display->local_row));
1927
         input_row += display->row_bytes/(sizeof (png_uint_16));
1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961
      } /* while y */
   }

   else
   {
      /* No alpha channel, so the row_end really is the end of the row and it
       * is sufficient to loop over the components one by one.
       */
      png_bytep row_end = output_row + image->width * channels;

      while (y-- > 0)
      {
         png_const_uint_16p in_ptr = input_row;
         png_bytep out_ptr = output_row;

         while (out_ptr < row_end)
         {
            png_uint_32 component = *in_ptr++;

            component *= 255;
            *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component);
         }

         png_write_row(png_ptr, output_row);
         input_row += display->row_bytes/(sizeof (png_uint_16));
      }
   }

   return 1;
}

static int
png_image_write_main(png_voidp argument)
{
1962 1963
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1964 1965 1966 1967 1968 1969 1970 1971 1972
   png_imagep image = display->image;
   png_structp png_ptr = image->opaque->png_ptr;
   png_infop info_ptr = image->opaque->info_ptr;
   png_uint_32 format = image->format;

   int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0; /* input */
   int alpha = (format & PNG_FORMAT_FLAG_ALPHA) != 0;
   int write_16bit = linear && !display->convert_to_8bit;

1973 1974 1975 1976
   /* Default the 'row_stride' parameter if required. */
   if (display->row_stride == 0)
      display->row_stride = PNG_IMAGE_ROW_STRIDE(*image);

1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992
   /* Set the required transforms then write the rows in the correct order. */
   png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
      write_16bit ? 16 : 8,
      ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) +
      ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0),
      PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

   /* Counter-intuitively the data transformations must be called *after*
    * png_write_info, not before as in the read code, but the 'set' functions
    * must still be called before.  Just set the color space information, never
    * write an interlaced image.
    */
   if (write_16bit)
   {
      /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */
      png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR);
1993 1994 1995 1996 1997 1998 1999 2000 2001

      if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))
         png_set_cHRM_fixed(png_ptr, info_ptr,
            /* color      x       y */
            /* white */ 31270, 32900,
            /* red   */ 64000, 33000,
            /* green */ 30000, 60000,
            /* blue  */ 15000,  6000
         );
2002 2003
   }

2004
   else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))
2005 2006
      png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);

2007 2008 2009 2010 2011 2012
   /* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit
    * space must still be gamma encoded.
    */
   else
      png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE);

2013 2014 2015 2016 2017
   /* Write the file header. */
   png_write_info(png_ptr, info_ptr);

   /* Now set up the data transformations (*after* the header is written),
    * remove the handled transformations from the 'format' flags for checking.
2018 2019
    *
    * First check for a little endian system if writing 16 bit files.
2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031
    */
   if (write_16bit)
   {
      PNG_CONST png_uint_16 le = 0x0001;

      if (*(png_const_bytep)&le)
         png_set_swap(png_ptr);
   }

#  ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
      if (format & PNG_FORMAT_FLAG_BGR)
      {
2032 2033
         if (format & PNG_FORMAT_FLAG_COLOR)
            png_set_bgr(png_ptr);
2034 2035 2036 2037 2038 2039 2040
         format &= ~PNG_FORMAT_FLAG_BGR;
      }
#  endif

#  ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
      if (format & PNG_FORMAT_FLAG_AFIRST)
      {
2041 2042
         if (format & PNG_FORMAT_FLAG_ALPHA)
            png_set_swap_alpha(png_ptr);
2043 2044 2045 2046
         format &= ~PNG_FORMAT_FLAG_AFIRST;
      }
#  endif

2047
   /* That should have handled all (both) the transforms. */
2048
   if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |
2049
         PNG_FORMAT_FLAG_ALPHA)) != 0)
2050 2051 2052
      png_error(png_ptr, "png_write_image: unsupported transformation");

   {
2053
      png_const_bytep row = png_voidcast(png_const_bytep, display->buffer);
2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071
      ptrdiff_t row_bytes = display->row_stride;

      if (linear)
         row_bytes *= sizeof (png_uint_16);

      if (row_bytes < 0)
         row += (image->height-1) * (-row_bytes);

      display->first_row = row;
      display->row_bytes = row_bytes;
   }

   /* Check for the cases that currently require a pre-transform on the row
    * before it is written.  This only applies when the input is 16-bit and
    * either there is an alpha channel or it is converted to 8-bit.
    */
   if ((linear && alpha) || display->convert_to_8bit)
   {
2072 2073
      png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
         png_get_rowbytes(png_ptr, info_ptr)));
2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094
      int result;

      display->local_row = row;
      if (write_16bit)
         result = png_safe_execute(image, png_write_image_16bit, display);
      else
         result = png_safe_execute(image, png_write_image_8bit, display);
      display->local_row = NULL;

      png_free(png_ptr, row);

      /* Skip the 'write_end' on error: */
      if (!result)
         return 0;
   }

   /* Otherwise this is the case where the input is in a format currently
    * supported by the rest of the libpng write code; call it directly.
    */
   else
   {
2095
      png_const_bytep row = png_voidcast(png_const_bytep, display->first_row);
2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110
      ptrdiff_t row_bytes = display->row_bytes;
      png_uint_32 y = image->height;

      while (y-- > 0)
      {
         png_write_row(png_ptr, row);
         row += row_bytes;
      }
   }

   png_write_end(png_ptr, info_ptr);
   return 1;
}

int PNGAPI
2111
png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154
   const void *buffer, png_int_32 row_stride)
{
   /* Write the image to the given (FILE*). */
   if (image != NULL)
   {
      if (file != NULL)
      {
         if (png_image_write_init(image))
         {
            png_image_write_control display;
            int result;

            /* This is slightly evil, but png_init_io doesn't do anything other
             * than this and we haven't changed the standard IO functions so
             * this saves a 'safe' function.
             */
            image->opaque->png_ptr->io_ptr = file;

            memset(&display, 0, sizeof display);
            display.image = image;
            display.buffer = buffer;
            display.row_stride = row_stride;
            display.convert_to_8bit = convert_to_8bit;

            result = png_safe_execute(image, png_image_write_main, &display);
            png_image_free(image);
            return result;
         }

         else
            return 0;
      }

      else
         return png_image_error(image,
            "png_image_write_to_stdio: invalid argument");
   }

   else
      return 0;
}

int PNGAPI
2155
png_image_write_to_file(png_imagep image, const char *file_name,
2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166
   int convert_to_8bit, const void *buffer, png_int_32 row_stride)
{
   /* Write the image to the named file. */
   if (image != NULL)
   {
      if (file_name != NULL)
      {
         FILE *fp = fopen(file_name, "wb");

         if (fp != NULL)
         {
2167 2168
            if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,
               row_stride))
2169
            {
2170
               int error; /* from fflush/fclose */
2171

2172 2173 2174 2175 2176
               /* Make sure the file is flushed correctly. */
               if (fflush(fp) == 0 && ferror(fp) == 0)
               {
                  if (fclose(fp) == 0)
                     return 1;
2177

2178 2179
                  error = errno; /* from fclose */
               }
2180

2181
               else
2182
               {
2183 2184
                  error = errno; /* from fflush or ferror */
                  (void)fclose(fp);
2185 2186
               }

2187 2188 2189 2190 2191
               (void)remove(file_name);
               /* The image has already been cleaned up; this is just used to
                * set the error (because the original write succeeded).
                */
               return png_image_error(image, strerror(error));
2192 2193 2194 2195 2196 2197
            }

            else
            {
               /* Clean up: just the opened file. */
               (void)fclose(fp);
2198
               (void)remove(file_name);
2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216
               return 0;
            }
         }

         else
            return png_image_error(image, strerror(errno));
      }

      else
         return png_image_error(image,
            "png_image_write_to_file: invalid argument");
   }

   else
      return 0;
}
#endif /* PNG_STDIO_SUPPORTED */
#endif /* SIMPLIFIED_WRITE */
2217
#endif /* PNG_WRITE_SUPPORTED */