pngwrite.c 69.2 KB
Newer Older
1

A
Andreas Dilger 已提交
2
/* pngwrite.c - general routines to write a PNG file
3
 *
4
 * Last changed in libpng 1.6.0 [(PENDING RELEASE)]
5
 * Copyright (c) 1998-2012 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_structrp png_ptr, png_inforp 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
png_write_info(png_structrp png_ptr, png_inforp info_ptr)
126
{
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
301
png_write_end(png_structrp png_ptr, png_inforp 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 * 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
#ifndef PNG_USER_MEM_SUPPORTED
453
   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
454 455 456 457
      error_fn, warn_fn, NULL, NULL, NULL);
#else
   return png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
       warn_fn, NULL, NULL, NULL);
458 459 460
}

/* Alternate initialize png_ptr structure, and allocate any memory needed */
461 462
PNG_FUNCTION(png_structp,PNGAPI
png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
463
    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
464
    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
465
{
466
   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
467
      error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
468
#endif /* PNG_USER_MEM_SUPPORTED */
469

470
   if (png_ptr != NULL)
471
   {
472 473 474 475 476
      /* TODO: delay this, it can be done in png_init_io() (if the app doesn't
       * do it itself) avoiding setting the default function if it is not
       * required.
       */
      png_set_write_fn(png_ptr, NULL, NULL, NULL);
477
   }
478

479
   return png_ptr;
G
Guy Schalnat 已提交
480 481
}

482

483 484 485 486 487
/* 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.
 */
488
void PNGAPI
489
png_write_rows(png_structrp png_ptr, png_bytepp row,
490
    png_uint_32 num_rows)
G
Guy Schalnat 已提交
491 492
{
   png_uint_32 i; /* row counter */
G
Guy Schalnat 已提交
493
   png_bytepp rp; /* row pointer */
G
Guy Schalnat 已提交
494

495
   png_debug(1, "in png_write_rows");
496 497 498 499

   if (png_ptr == NULL)
      return;

500
   /* Loop through the rows */
G
Guy Schalnat 已提交
501 502 503 504 505 506
   for (i = 0, rp = row; i < num_rows; i++, rp++)
   {
      png_write_row(png_ptr, *rp);
   }
}

507 508 509
/* Write the image.  You only need to call this function once, even
 * if you are writing an interlaced image.
 */
510
void PNGAPI
511
png_write_image(png_structrp png_ptr, png_bytepp image)
G
Guy Schalnat 已提交
512 513 514
{
   png_uint_32 i; /* row index */
   int pass, num_pass; /* pass variables */
G
Guy Schalnat 已提交
515
   png_bytepp rp; /* points to current row */
G
Guy Schalnat 已提交
516

517 518 519
   if (png_ptr == NULL)
      return;

520
   png_debug(1, "in png_write_image");
521

522
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
523
   /* Initialize interlace handling.  If image is not interlaced,
524 525
    * this will set pass to 1
    */
G
Guy Schalnat 已提交
526
   num_pass = png_set_interlace_handling(png_ptr);
527 528 529
#else
   num_pass = 1;
#endif
530
   /* Loop through passes */
G
Guy Schalnat 已提交
531 532
   for (pass = 0; pass < num_pass; pass++)
   {
533
      /* Loop through image */
G
Guy Schalnat 已提交
534 535 536 537 538 539 540
      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
      {
         png_write_row(png_ptr, *rp);
      }
   }
}

541
/* Called by user to write a row of image data */
542
void PNGAPI
543
png_write_row(png_structrp png_ptr, png_const_bytep row)
G
Guy Schalnat 已提交
544
{
545 546 547
   /* 1.5.6: moved from png_struct to be a local structure: */
   png_row_info row_info;

548 549
   if (png_ptr == NULL)
      return;
550

551
   png_debug2(1, "in png_write_row (row %u, pass %d)",
552
      png_ptr->row_number, png_ptr->pass);
553

554
   /* Initialize transformations and other stuff if first time */
G
Guy Schalnat 已提交
555
   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
G
Guy Schalnat 已提交
556
   {
557
      /* Make sure we wrote the header info */
558 559
      if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
         png_error(png_ptr,
560
             "png_write_info was never called before png_write_row");
561

562
      /* Check for transforms that have been set but were defined out */
563
#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
564 565
      if (png_ptr->transformations & PNG_INVERT_MONO)
         png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined");
566
#endif
567

568
#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
569 570
      if (png_ptr->transformations & PNG_FILLER)
         png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined");
571
#endif
572 573
#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
    defined(PNG_READ_PACKSWAP_SUPPORTED)
574
      if (png_ptr->transformations & PNG_PACKSWAP)
575 576
         png_warning(png_ptr,
             "PNG_WRITE_PACKSWAP_SUPPORTED is not defined");
577
#endif
578

579
#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
580 581
      if (png_ptr->transformations & PNG_PACK)
         png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");
582
#endif
583

584
#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
585 586
      if (png_ptr->transformations & PNG_SHIFT)
         png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");
587
#endif
588

589
#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
590 591
      if (png_ptr->transformations & PNG_BGR)
         png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");
592
#endif
593

594
#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
595 596
      if (png_ptr->transformations & PNG_SWAP_BYTES)
         png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");
597 598
#endif

G
Guy Schalnat 已提交
599 600 601
      png_write_start_row(png_ptr);
   }

602
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
603
   /* If interlaced and not interested in row, return */
G
Guy Schalnat 已提交
604 605 606 607
   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
   {
      switch (png_ptr->pass)
      {
G
Guy Schalnat 已提交
608
         case 0:
609
            if (png_ptr->row_number & 0x07)
G
Guy Schalnat 已提交
610 611 612 613 614
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
615

G
Guy Schalnat 已提交
616
         case 1:
617
            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
G
Guy Schalnat 已提交
618 619 620 621 622
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
623

G
Guy Schalnat 已提交
624
         case 2:
625
            if ((png_ptr->row_number & 0x07) != 4)
G
Guy Schalnat 已提交
626 627
            {
               png_write_finish_row(png_ptr);
G
Guy Schalnat 已提交
628
               return;
G
Guy Schalnat 已提交
629 630
            }
            break;
631

G
Guy Schalnat 已提交
632
         case 3:
633
            if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)
G
Guy Schalnat 已提交
634 635 636 637 638
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
639

G
Guy Schalnat 已提交
640
         case 4:
641
            if ((png_ptr->row_number & 0x03) != 2)
G
Guy Schalnat 已提交
642 643 644 645 646
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
647

G
Guy Schalnat 已提交
648
         case 5:
649
            if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)
G
Guy Schalnat 已提交
650 651 652 653 654
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
655

G
Guy Schalnat 已提交
656
         case 6:
657
            if (!(png_ptr->row_number & 0x01))
G
Guy Schalnat 已提交
658 659 660 661 662
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
663 664 665

         default: /* error: ignore it */
            break;
G
Guy Schalnat 已提交
666 667
      }
   }
G
Guy Schalnat 已提交
668
#endif
G
Guy Schalnat 已提交
669

670
   /* Set up row info for transformations */
671 672 673 674 675 676 677 678 679 680 681 682 683
   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 已提交
684 685

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

688
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
689
   /* Handle interlacing */
G
Guy Schalnat 已提交
690
   if (png_ptr->interlaced && png_ptr->pass < 6 &&
691
       (png_ptr->transformations & PNG_INTERLACE))
G
Guy Schalnat 已提交
692
   {
693
      png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass);
694
      /* This should always get caught above, but still ... */
695
      if (!(row_info.width))
G
Guy Schalnat 已提交
696 697 698 699 700
      {
         png_write_finish_row(png_ptr);
         return;
      }
   }
G
Guy Schalnat 已提交
701
#endif
G
Guy Schalnat 已提交
702

703
#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
704
   /* Handle other transformations */
G
Guy Schalnat 已提交
705
   if (png_ptr->transformations)
706
      png_do_write_transformations(png_ptr, &row_info);
707
#endif
G
Guy Schalnat 已提交
708

709 710 711 712 713 714 715
   /* 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");

716
#ifdef PNG_MNG_FEATURES_SUPPORTED
717 718 719 720 721 722 723 724 725
   /* 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
    */
726
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
727
       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
728 729
   {
      /* Intrapixel differencing */
730
      png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);
731 732 733
   }
#endif

A
Andreas Dilger 已提交
734
   /* Find a filter if necessary, filter the row and write it out. */
735
   png_write_find_filter(png_ptr, &row_info);
736 737 738

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

741
#ifdef PNG_WRITE_FLUSH_SUPPORTED
G
Guy Schalnat 已提交
742
/* Set the automatic flush interval or 0 to turn flushing off */
743
void PNGAPI
744
png_set_flush(png_structrp png_ptr, int nrows)
G
Guy Schalnat 已提交
745
{
746
   png_debug(1, "in png_set_flush");
747

748 749
   if (png_ptr == NULL)
      return;
750

G
Guy Schalnat 已提交
751
   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
G
Guy Schalnat 已提交
752 753
}

754
/* Flush the current output buffers now */
755
void PNGAPI
756
png_write_flush(png_structrp png_ptr)
G
Guy Schalnat 已提交
757
{
G
Guy Schalnat 已提交
758 759
   int wrote_IDAT;

760
   png_debug(1, "in png_write_flush");
761

762 763
   if (png_ptr == NULL)
      return;
764

G
Guy Schalnat 已提交
765 766
   /* We have already written out all of the data */
   if (png_ptr->row_number >= png_ptr->num_rows)
767
      return;
G
Guy Schalnat 已提交
768 769 770 771 772

   do
   {
      int ret;

773
      /* Compress the data */
A
Andreas Dilger 已提交
774
      ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
G
Guy Schalnat 已提交
775 776
      wrote_IDAT = 0;

777
      /* Check for compression errors */
G
Guy Schalnat 已提交
778 779
      if (ret != Z_OK)
      {
A
Andreas Dilger 已提交
780
         if (png_ptr->zstream.msg != NULL)
A
Andreas Dilger 已提交
781
            png_error(png_ptr, png_ptr->zstream.msg);
782

G
Guy Schalnat 已提交
783 784 785 786
         else
            png_error(png_ptr, "zlib error");
      }

A
Andreas Dilger 已提交
787
      if (!(png_ptr->zstream.avail_out))
G
Guy Schalnat 已提交
788
      {
789
         /* Write the IDAT and reset the zlib output buffer */
790
         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
G
Guy Schalnat 已提交
791 792
         wrote_IDAT = 1;
      }
793
   } while (wrote_IDAT == 1);
G
Guy Schalnat 已提交
794 795

   /* If there is any data left to be output, write it into a new IDAT */
A
Andreas Dilger 已提交
796
   if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
G
Guy Schalnat 已提交
797
   {
798
      /* Write the IDAT and reset the zlib output buffer */
G
Guy Schalnat 已提交
799
      png_write_IDAT(png_ptr, png_ptr->zbuf,
800
          png_ptr->zbuf_size - png_ptr->zstream.avail_out);
G
Guy Schalnat 已提交
801 802 803
   }
   png_ptr->flush_rows = 0;
   png_flush(png_ptr);
G
Guy Schalnat 已提交
804
}
G
Guy Schalnat 已提交
805
#endif /* PNG_WRITE_FLUSH_SUPPORTED */
G
Guy Schalnat 已提交
806

807
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
808
static void png_reset_filter_heuristics(png_structrp png_ptr); /* forward decl */
809
#endif
G
Guy Schalnat 已提交
810

811 812
/* Free any memory used in png_ptr struct without freeing the struct itself. */
static void
813
png_write_destroy(png_structrp png_ptr)
G
Guy Schalnat 已提交
814
{
815
   png_debug(1, "in png_write_destroy");
816 817

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

821
   /* Free our memory.  png_free checks NULL for us. */
A
Andreas Dilger 已提交
822 823
   png_free(png_ptr, png_ptr->zbuf);
   png_free(png_ptr, png_ptr->row_buf);
824
#ifdef PNG_WRITE_FILTER_SUPPORTED
A
Andreas Dilger 已提交
825 826 827 828 829
   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);
830
#endif
831

832
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
833 834
   /* Use this to save a little code space, it doesn't free the filter_costs */
   png_reset_filter_heuristics(png_ptr);
A
Andreas Dilger 已提交
835 836
   png_free(png_ptr, png_ptr->filter_costs);
   png_free(png_ptr, png_ptr->inv_filter_costs);
837
#endif
G
Guy Schalnat 已提交
838

839 840
#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
   png_free(png_ptr, png_ptr->chunk_list);
841
#endif
G
Guy Schalnat 已提交
842

843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
   /* The error handling and memory handling information is left intact at this
    * point: the jmp_buf may still have to be freed.  See png_destroy_png_struct
    * for how this happens.
    */
}

/* Free all memory used by the write.
 * In libpng 1.6.0 this API changed quietly to no longer accept a NULL value for
 * *png_ptr_ptr.  Prior to 1.6.0 it would accept such a value and it would free
 * the passed in info_structs but it would quietly fail to free any of the data
 * inside them.  In 1.6.0 it quietly does nothing (it has to be quiet because it
 * has no png_ptr.)
 */
void PNGAPI
png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
{
   png_debug(1, "in png_destroy_write_struct");
G
Guy Schalnat 已提交
860

861 862
   if (png_ptr_ptr != NULL)
   {
863
      png_structrp png_ptr = *png_ptr_ptr;
G
Guy Schalnat 已提交
864

865 866 867
      if (png_ptr != NULL) /* added in libpng 1.6.0 */
      {
         png_destroy_info_struct(png_ptr, info_ptr_ptr);
G
Guy Schalnat 已提交
868

869 870 871 872 873
         *png_ptr_ptr = NULL;
         png_write_destroy(png_ptr);
         png_destroy_png_struct(png_ptr);
      }
   }
G
Guy Schalnat 已提交
874
}
G
Guy Schalnat 已提交
875

A
Andreas Dilger 已提交
876
/* Allow the application to select one or more row filters to use. */
877
void PNGAPI
878
png_set_filter(png_structrp png_ptr, int method, int filters)
G
Guy Schalnat 已提交
879
{
880
   png_debug(1, "in png_set_filter");
881

882 883
   if (png_ptr == NULL)
      return;
884

885
#ifdef PNG_MNG_FEATURES_SUPPORTED
886
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
887 888
       (method == PNG_INTRAPIXEL_DIFFERENCING))
      method = PNG_FILTER_TYPE_BASE;
889

890
#endif
A
Andreas Dilger 已提交
891
   if (method == PNG_FILTER_TYPE_BASE)
G
Guy Schalnat 已提交
892 893 894
   {
      switch (filters & (PNG_ALL_FILTERS | 0x07))
      {
895
#ifdef PNG_WRITE_FILTER_SUPPORTED
G
Guy Schalnat 已提交
896 897
         case 5:
         case 6:
A
Andreas Dilger 已提交
898
         case 7: png_warning(png_ptr, "Unknown row filter for method 0");
899
#endif /* PNG_WRITE_FILTER_SUPPORTED */
900
         case PNG_FILTER_VALUE_NONE:
901
            png_ptr->do_filter = PNG_FILTER_NONE; break;
902

903
#ifdef PNG_WRITE_FILTER_SUPPORTED
904
         case PNG_FILTER_VALUE_SUB:
905
            png_ptr->do_filter = PNG_FILTER_SUB; break;
906

907
         case PNG_FILTER_VALUE_UP:
908
            png_ptr->do_filter = PNG_FILTER_UP; break;
909

910
         case PNG_FILTER_VALUE_AVG:
911
            png_ptr->do_filter = PNG_FILTER_AVG; break;
912

913
         case PNG_FILTER_VALUE_PAETH:
914
            png_ptr->do_filter = PNG_FILTER_PAETH; break;
915

916 917
         default:
            png_ptr->do_filter = (png_byte)filters; break;
918
#else
919 920
         default:
            png_warning(png_ptr, "Unknown row filter for method 0");
921
#endif /* PNG_WRITE_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
922 923
      }

A
Andreas Dilger 已提交
924 925 926 927 928 929 930 931
      /* 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 已提交
932
       */
A
Andreas Dilger 已提交
933
      if (png_ptr->row_buf != NULL)
G
Guy Schalnat 已提交
934
      {
935
#ifdef PNG_WRITE_FILTER_SUPPORTED
936
         if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)
G
Guy Schalnat 已提交
937
         {
A
Andreas Dilger 已提交
938
            png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
939
                (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
940
            png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
G
Guy Schalnat 已提交
941 942
         }

943
         if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)
G
Guy Schalnat 已提交
944
         {
A
Andreas Dilger 已提交
945
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
946
            {
A
Andreas Dilger 已提交
947
               png_warning(png_ptr, "Can't add Up filter after starting");
948 949
               png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
                   ~PNG_FILTER_UP);
G
Guy Schalnat 已提交
950
            }
951

G
Guy Schalnat 已提交
952 953
            else
            {
A
Andreas Dilger 已提交
954
               png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
955
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
956
               png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
G
Guy Schalnat 已提交
957 958 959
            }
         }

960
         if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)
G
Guy Schalnat 已提交
961
         {
A
Andreas Dilger 已提交
962
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
963
            {
A
Andreas Dilger 已提交
964
               png_warning(png_ptr, "Can't add Average filter after starting");
965 966
               png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
                   ~PNG_FILTER_AVG);
G
Guy Schalnat 已提交
967
            }
968

G
Guy Schalnat 已提交
969 970
            else
            {
A
Andreas Dilger 已提交
971
               png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
972
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
973
               png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
G
Guy Schalnat 已提交
974 975 976
            }
         }

977
         if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&
A
Andreas Dilger 已提交
978
             png_ptr->paeth_row == NULL)
G
Guy Schalnat 已提交
979
         {
A
Andreas Dilger 已提交
980
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
981 982
            {
               png_warning(png_ptr, "Can't add Paeth filter after starting");
983
               png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
G
Guy Schalnat 已提交
984
            }
985

G
Guy Schalnat 已提交
986 987
            else
            {
988
               png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
989
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
990
               png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
G
Guy Schalnat 已提交
991 992 993 994
            }
         }

         if (png_ptr->do_filter == PNG_NO_FILTERS)
995
#endif /* PNG_WRITE_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
996 997 998 999
            png_ptr->do_filter = PNG_FILTER_NONE;
      }
   }
   else
A
Andreas Dilger 已提交
1000 1001 1002 1003 1004 1005 1006 1007
      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
1008 1009
 * better compression.
 */
1010
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED      /* GRR 970116 */
1011
/* Convenience reset API. */
1012
static void
1013
png_reset_filter_heuristics(png_structrp png_ptr)
A
Andreas Dilger 已提交
1014
{
1015 1016 1017 1018 1019 1020 1021 1022
   /* 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 已提交
1023
   {
1024 1025 1026
      png_bytep old = png_ptr->prev_filters;
      png_ptr->prev_filters = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1027
   }
1028
   if (png_ptr->filter_weights != NULL)
A
Andreas Dilger 已提交
1029
   {
1030 1031 1032
      png_uint_16p old = png_ptr->filter_weights;
      png_ptr->filter_weights = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1033 1034
   }

1035
   if (png_ptr->inv_filter_weights != NULL)
A
Andreas Dilger 已提交
1036
   {
1037 1038 1039
      png_uint_16p old = png_ptr->inv_filter_weights;
      png_ptr->inv_filter_weights = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1040 1041
   }

1042 1043 1044 1045
   /* Leave the filter_costs - this array is fixed size. */
}

static int
1046
png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method,
1047 1048 1049 1050 1051 1052 1053
   int num_weights)
{
   if (png_ptr == NULL)
      return 0;

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

1055 1056 1057 1058 1059
   /* 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 已提交
1060
   {
1061 1062 1063
      int i;

      if (num_weights > 0)
A
Andreas Dilger 已提交
1064 1065
      {
         png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
1066
             (png_uint_32)(png_sizeof(png_byte) * num_weights));
A
Andreas Dilger 已提交
1067

1068 1069
         /* To make sure that the weighting starts out fairly */
         for (i = 0; i < num_weights; i++)
A
Andreas Dilger 已提交
1070
         {
1071
            png_ptr->prev_filters[i] = 255;
A
Andreas Dilger 已提交
1072 1073
         }

1074 1075
         png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
A
Andreas Dilger 已提交
1076

1077 1078
         png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
1079

1080 1081 1082 1083 1084
         for (i = 0; i < num_weights; i++)
         {
            png_ptr->inv_filter_weights[i] =
            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
         }
1085

1086 1087
         /* Safe to set this now */
         png_ptr->num_prev_filters = (png_byte)num_weights;
A
Andreas Dilger 已提交
1088 1089
      }

1090 1091 1092 1093
      /* 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 已提交
1094
      {
1095 1096
         png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
1097

1098 1099
         png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
1100
      }
1101

1102 1103
      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
      {
1104 1105
         png_ptr->inv_filter_costs[i] =
         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
A
Andreas Dilger 已提交
1106
      }
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122

      /* 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 已提交
1123
   }
1124
}
A
Andreas Dilger 已提交
1125

1126 1127 1128
/* Provide floating and fixed point APIs */
#ifdef PNG_FLOATING_POINT_SUPPORTED
void PNGAPI
1129
png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method,
1130 1131
    int num_weights, png_const_doublep filter_weights,
    png_const_doublep filter_costs)
1132 1133 1134 1135 1136
{
   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 已提交
1137
    */
1138 1139
   if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
      return;
A
Andreas Dilger 已提交
1140

1141 1142 1143 1144 1145 1146
   /* 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++)
      {
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
         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);
         }
1161
      }
A
Andreas Dilger 已提交
1162

1163 1164 1165 1166 1167 1168 1169 1170
      /* 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 已提交
1171
      {
1172 1173
         png_ptr->inv_filter_costs[i] =
             (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5);
1174

1175 1176
         png_ptr->filter_costs[i] =
             (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5);
A
Andreas Dilger 已提交
1177 1178
      }
   }
1179 1180
}
#endif /* FLOATING_POINT */
A
Andreas Dilger 已提交
1181

1182 1183
#ifdef PNG_FIXED_POINT_SUPPORTED
void PNGAPI
1184
png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method,
1185 1186
    int num_weights, png_const_fixed_point_p filter_weights,
    png_const_fixed_point_p filter_costs)
1187 1188 1189 1190 1191
{
   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 已提交
1192
    */
1193 1194 1195 1196 1197
   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 已提交
1198
   {
1199 1200
      int i;
      for (i = 0; i < num_weights; i++)
A
Andreas Dilger 已提交
1201
      {
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
         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 已提交
1216
      }
1217

1218 1219 1220 1221 1222 1223 1224 1225 1226
      /* 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 已提交
1227
      {
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240
         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;
1241

1242
         png_ptr->filter_costs[i] = (png_uint_16)tmp;
A
Andreas Dilger 已提交
1243 1244
      }
   }
G
Guy Schalnat 已提交
1245
}
1246
#endif /* FIXED_POINT */
A
Andreas Dilger 已提交
1247
#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
1248

1249
void PNGAPI
1250
png_set_compression_level(png_structrp png_ptr, int level)
G
Guy Schalnat 已提交
1251
{
1252
   png_debug(1, "in png_set_compression_level");
1253

1254 1255
   if (png_ptr == NULL)
      return;
1256

G
Guy Schalnat 已提交
1257
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
G
Guy Schalnat 已提交
1258 1259 1260
   png_ptr->zlib_level = level;
}

1261
void PNGAPI
1262
png_set_compression_mem_level(png_structrp png_ptr, int mem_level)
G
Guy Schalnat 已提交
1263
{
1264
   png_debug(1, "in png_set_compression_mem_level");
1265

1266 1267
   if (png_ptr == NULL)
      return;
1268

G
Guy Schalnat 已提交
1269
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
G
Guy Schalnat 已提交
1270
   png_ptr->zlib_mem_level = mem_level;
G
Guy Schalnat 已提交
1271 1272
}

1273
void PNGAPI
1274
png_set_compression_strategy(png_structrp png_ptr, int strategy)
G
Guy Schalnat 已提交
1275
{
1276
   png_debug(1, "in png_set_compression_strategy");
1277

1278 1279
   if (png_ptr == NULL)
      return;
1280

G
Guy Schalnat 已提交
1281
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
G
Guy Schalnat 已提交
1282 1283 1284
   png_ptr->zlib_strategy = strategy;
}

1285 1286 1287
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
 * smaller value of window_bits if it can do so safely.
 */
1288
void PNGAPI
1289
png_set_compression_window_bits(png_structrp png_ptr, int window_bits)
G
Guy Schalnat 已提交
1290
{
1291 1292
   if (png_ptr == NULL)
      return;
1293

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

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

1300
#ifndef WBITS_8_OK
1301
   /* Avoid libpng bug with 256-byte windows */
1302
   if (window_bits == 8)
1303 1304 1305 1306
      {
        png_warning(png_ptr, "Compression window is being reset to 512");
        window_bits = 9;
      }
1307

1308
#endif
G
Guy Schalnat 已提交
1309
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
G
Guy Schalnat 已提交
1310 1311 1312
   png_ptr->zlib_window_bits = window_bits;
}

1313
void PNGAPI
1314
png_set_compression_method(png_structrp png_ptr, int method)
G
Guy Schalnat 已提交
1315
{
1316
   png_debug(1, "in png_set_compression_method");
1317

1318 1319
   if (png_ptr == NULL)
      return;
1320

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

G
Guy Schalnat 已提交
1324
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
G
Guy Schalnat 已提交
1325
   png_ptr->zlib_method = method;
G
Guy Schalnat 已提交
1326 1327
}

1328
/* The following were added to libpng-1.5.4 */
1329
#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
1330
void PNGAPI
1331
png_set_text_compression_level(png_structrp png_ptr, int level)
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342
{
   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
1343
png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
{
   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
1355
png_set_text_compression_strategy(png_structrp png_ptr, int strategy)
1356 1357 1358 1359 1360 1361 1362 1363 1364 1365
{
   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;
}

1366 1367 1368
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
 * smaller value of window_bits if it can do so safely.
 */
1369
void PNGAPI
1370
png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits)
1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394
{
   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
1395
png_set_text_compression_method(png_structrp png_ptr, int method)
1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
{
   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;
}
1408
#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
1409
/* end of API added to libpng-1.5.4 */
1410

1411
void PNGAPI
1412
png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)
1413
{
1414 1415
   if (png_ptr == NULL)
      return;
1416

1417 1418 1419
   png_ptr->write_row_fn = write_row_fn;
}

1420
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
1421
void PNGAPI
1422
png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
1423
    write_user_transform_fn)
1424
{
1425
   png_debug(1, "in png_set_write_user_transform_fn");
1426

1427 1428
   if (png_ptr == NULL)
      return;
1429

1430 1431 1432 1433
   png_ptr->transformations |= PNG_USER_TRANSFORM;
   png_ptr->write_user_transform_fn = write_user_transform_fn;
}
#endif
1434 1435


1436
#ifdef PNG_INFO_IMAGE_SUPPORTED
1437
void PNGAPI
1438
png_write_png(png_structrp png_ptr, png_inforp info_ptr,
1439
    int transforms, voidp params)
1440
{
1441 1442
   if (png_ptr == NULL || info_ptr == NULL)
      return;
1443 1444 1445 1446 1447 1448

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

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

1449
#ifdef PNG_WRITE_INVERT_SUPPORTED
1450
   /* Invert monochrome pixels */
1451
   if (transforms & PNG_TRANSFORM_INVERT_MONO)
1452
      png_set_invert_mono(png_ptr);
1453 1454
#endif

1455
#ifdef PNG_WRITE_SHIFT_SUPPORTED
1456 1457 1458 1459
   /* Shift the pixels up to a legal bit depth and fill in
    * as appropriate to correctly scale the image.
    */
   if ((transforms & PNG_TRANSFORM_SHIFT)
1460
       && (info_ptr->valid & PNG_INFO_sBIT))
1461
      png_set_shift(png_ptr, &info_ptr->sig_bit);
1462 1463
#endif

1464
#ifdef PNG_WRITE_PACK_SUPPORTED
1465
   /* Pack pixels into bytes */
1466 1467 1468 1469
   if (transforms & PNG_TRANSFORM_PACKING)
       png_set_packing(png_ptr);
#endif

1470
#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
1471
   /* Swap location of alpha bytes from ARGB to RGBA */
1472
   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
1473
      png_set_swap_alpha(png_ptr);
1474 1475
#endif

1476
#ifdef PNG_WRITE_FILLER_SUPPORTED
1477
   /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */
1478
   if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)
1479
      png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
1480

1481
   else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)
1482
      png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
1483 1484
#endif

1485
#ifdef PNG_WRITE_BGR_SUPPORTED
1486
   /* Flip BGR pixels to RGB */
1487
   if (transforms & PNG_TRANSFORM_BGR)
1488
      png_set_bgr(png_ptr);
1489 1490
#endif

1491
#ifdef PNG_WRITE_SWAP_SUPPORTED
1492
   /* Swap bytes of 16-bit files to most significant byte first */
1493
   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
1494
      png_set_swap(png_ptr);
1495 1496
#endif

1497
#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
1498
   /* Swap bits of 1, 2, 4 bit packed pixel formats */
1499
   if (transforms & PNG_TRANSFORM_PACKSWAP)
1500
      png_set_packswap(png_ptr);
1501 1502
#endif

1503
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
1504 1505 1506 1507 1508
   /* Invert the alpha channel from opacity to transparency */
   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
      png_set_invert_alpha(png_ptr);
#endif

1509 1510
   /* ----------------------- end of transformations ------------------- */

1511
   /* Write the bits */
1512 1513 1514 1515 1516
   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);
1517

1518 1519
   PNG_UNUSED(transforms)   /* Quiet compiler warnings */
   PNG_UNUSED(params)
1520 1521
}
#endif
1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538


#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)
      {
1539 1540
         png_controlp control = png_voidcast(png_controlp,
            png_malloc_warn(png_ptr, sizeof *control));
1541 1542 1543

         if (control != NULL)
         {
1544
            png_memset(control, 0, sizeof *control);
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570

            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;
1571
   png_const_voidp colormap;
1572 1573 1574 1575 1576 1577 1578
   int             convert_to_8bit;
   /* Local variables: */
   png_const_voidp first_row;
   ptrdiff_t       row_bytes;
   png_voidp       local_row;
} png_image_write_control;

1579 1580
/* 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
1581 1582 1583 1584 1585
 * png_image format value.
 */
static int
png_write_image_16bit(png_voidp argument)
{
1586 1587
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1588
   png_imagep image = display->image;
1589
   png_structrp png_ptr = image->opaque->png_ptr;
1590

1591 1592 1593
   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);
1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616
   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
1617
    * row; this is correct.
1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634
    */
   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
1635
          * component/alpha*65535 << 15. (I.e. 15 bits of precision); this
1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675
          * 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;
      }

1676
      png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));
1677 1678 1679 1680 1681 1682 1683 1684 1685
      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.
1686 1687 1688 1689
 *
 * Calculate an alpha reciprocal to reverse pre-multiplication.  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.
1690
 */
1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737
#define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha)

static png_byte
png_unpremultiply(png_uint_32 component, png_uint_32 alpha,
   png_uint_32 reciprocal/*from the above macro*/)
{
   /* The following gives 1.0 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.)
    *
    * There is a rounding problem here; if alpha is less than 128 it will end up
    * as 0 when scaled to 8 bits.  To avoid introducing spurious colors into the
    * output change for this too.
    */
   if (component >= alpha || alpha < 128)
      return 255;

   /* component<alpha, so component/alpha is less than one and
    * component*reciprocal is less than 2^31.
    */
   else if (component > 0)
   {
      /* The test is that alpha/257 (rounded) is less than 255, the first value
       * that becomes 255 is 65407.
       * NOTE: this must agree with the PNG_DIV257 macro (which must, therefore,
       * be exact!)  [Could also test reciprocal != 0]
       */
      if (alpha < 65407)
      {
         component *= reciprocal;
         component += 64; /* round to nearest */
         component >>= 7;
      }

      else
         component *= 255;

      /* Convert the component to sRGB. */
      return (png_byte)PNG_sRGB_FROM_LINEAR(component);
   }

   else
      return 0;
}

1738 1739 1740
static int
png_write_image_8bit(png_voidp argument)
{
1741 1742
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1743
   png_imagep image = display->image;
1744
   png_structrp png_ptr = image->opaque->png_ptr;
1745

1746 1747 1748
   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);
1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777
   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];
1778
            png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
1779 1780 1781
            png_uint_32 reciprocal = 0;
            int c;

1782 1783
            /* Scale and write the alpha channel. */
            out_ptr[aindex] = alphabyte;
1784

1785 1786
            if (alphabyte > 0 && alphabyte < 255)
               reciprocal = UNP_RECIPROCAL(alpha);
1787 1788 1789

            c = channels;
            do /* always at least one channel */
1790
               *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal);
1791 1792 1793 1794 1795 1796
            while (--c > 0);

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

1798 1799
         png_write_row(png_ptr, png_voidcast(png_const_bytep,
            display->local_row));
1800
         input_row += display->row_bytes/(sizeof (png_uint_16));
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831
      } /* 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;
}

1832 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 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 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 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976
static void
png_image_set_PLTE(png_image_write_control *display)
{
   const png_imagep image = display->image;
   const void *cmap = display->colormap;
   const int entries = image->colormap_entries > 256 ? 256 :
      (int)image->colormap_entries;

   /* NOTE: the caller must check for cmap != NULL and entries != 0 */
   const png_uint_32 format = image->format;
   const int channels = PNG_IMAGE_SAMPLE_CHANNELS(format);

#  ifdef PNG_FORMAT_BGR_SUPPORTED
      const int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 &&
         (format & PNG_FORMAT_FLAG_ALPHA) != 0;
#  else
#     define afirst 0
#  endif

#  ifdef PNG_FORMAT_BGR_SUPPORTED
      const int bgr = (format & PNG_FORMAT_FLAG_BGR) ? 2 : 0;
#  else
#     define bgr 0
#  endif

   int i, num_trans;
   png_color palette[256];
   png_byte tRNS[256];

   memset(tRNS, 255, sizeof tRNS);
   memset(palette, 0, sizeof palette);

   for (i=num_trans=0; i<entries; ++i)
   {
      /* This gets automatically converted to sRGB with reversal of the
       * pre-multiplication if the color-map has an alpha channel.
       */
      if (format & PNG_FORMAT_FLAG_LINEAR)
      {
         png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap);
         
         entry += i * channels;

         if (channels & 1) /* no alpha */
         {
            if (channels >= 3) /* RGB */
            {
               palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
                  entry[(2 ^ bgr)]);
               palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
                  entry[1]);
               palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 *
                  entry[bgr]);
            }

            else /* Gray */
               palette[i].blue = palette[i].red = palette[i].green =
                  (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry);
         }

         else /* alpha */
         {
            png_uint_16 alpha = entry[afirst ? 0 : channels-1];
            png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
            png_uint_32 reciprocal = 0;

            /* Calculate a reciprocal, as in the png_write_image_8bit code above
             * this is designed to produce a value scaled to 255*65535 when
             * divided by 128 (i.e. asr 7).
             */
            if (alphabyte > 0 && alphabyte < 255)
               reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha;

            tRNS[i] = alphabyte;
            if (alphabyte < 255)
               num_trans = i+1;

            if (channels >= 3) /* RGB */
            {
               palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)],
                  alpha, reciprocal);
               palette[i].green = png_unpremultiply(entry[afirst + 1], alpha,
                  reciprocal);
               palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha,
                  reciprocal);
            }

            else /* gray */
               palette[i].blue = palette[i].red = palette[i].green =
                  png_unpremultiply(entry[afirst], alpha, reciprocal);
         }
      }

      else /* Color-map has sRGB values */
      {
         png_const_bytep entry = png_voidcast(png_const_bytep, cmap);
         
         entry += i * channels;

         switch (channels)
         {
            case 4:
               tRNS[i] = entry[afirst ? 0 : 3];
               if (tRNS[i] < 255)
                  num_trans = i+1;
               /* FALL THROUGH */
            case 3:
               palette[i].blue = entry[afirst + (2 ^ bgr)];
               palette[i].green = entry[afirst + 1];
               palette[i].red = entry[afirst + bgr];
               break;

            case 2:
               tRNS[i] = entry[1 ^ afirst];
               if (tRNS[i] < 255)
                  num_trans = i+1;
               /* FALL THROUGH */
            case 1:
               palette[i].blue = palette[i].red = palette[i].green =
                  entry[afirst];
               break;

            default:
               break;
         }
      }
   }

#  ifdef afirst
#     undef afirst
#  endif
#  ifdef bgr
#     undef bgr
#  endif

   png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette,
      entries);

   if (num_trans > 0)
      png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS,
         num_trans, NULL);

   image->colormap_entries = entries;
}

1977 1978 1979
static int
png_image_write_main(png_voidp argument)
{
1980 1981
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1982
   png_imagep image = display->image;
1983 1984
   png_structrp png_ptr = image->opaque->png_ptr;
   png_inforp info_ptr = image->opaque->info_ptr;
1985 1986
   png_uint_32 format = image->format;

1987 1988 1989 1990
   int colormap = (format & PNG_FORMAT_FLAG_COLORMAP) != 0;
   int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR) != 0; /* input */
   int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0;
   int write_16bit = linear && !colormap && !display->convert_to_8bit;
1991

1992 1993 1994 1995
   /* Default the 'row_stride' parameter if required. */
   if (display->row_stride == 0)
      display->row_stride = PNG_IMAGE_ROW_STRIDE(*image);

1996
   /* Set the required transforms then write the rows in the correct order. */
1997
   if (format & PNG_FORMAT_FLAG_COLORMAP)
1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014
   {
      if (display->colormap != NULL && image->colormap_entries > 0)
      {
         png_uint_32 entries = image->colormap_entries;

         png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
            entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)),
            PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
            PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

         png_image_set_PLTE(display);
      }

      else
         png_error(image->opaque->png_ptr,
            "no color-map for color-mapped image");
   }
2015 2016 2017 2018 2019 2020 2021

   else
      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);
2022 2023 2024 2025 2026 2027

   /* 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.
    */
2028

2029 2030 2031 2032
   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);
2033 2034 2035 2036 2037 2038 2039 2040 2041

      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
         );
2042 2043
   }

2044
   else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))
2045 2046
      png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);

2047 2048 2049 2050 2051 2052
   /* 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);

2053 2054 2055 2056 2057
   /* 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.
2058 2059
    *
    * First check for a little endian system if writing 16 bit files.
2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071
    */
   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)
      {
2072
         if (!colormap && (format & PNG_FORMAT_FLAG_COLOR) != 0)
2073
            png_set_bgr(png_ptr);
2074 2075 2076 2077 2078 2079 2080
         format &= ~PNG_FORMAT_FLAG_BGR;
      }
#  endif

#  ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
      if (format & PNG_FORMAT_FLAG_AFIRST)
      {
2081
         if (!colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0)
2082
            png_set_swap_alpha(png_ptr);
2083 2084 2085 2086
         format &= ~PNG_FORMAT_FLAG_AFIRST;
      }
#  endif

2087 2088 2089 2090 2091 2092
   /* If there are 16 or fewer color-map entries we wrote a lower bit depth
    * above, but the application data is still byte packed.
    */
   if (colormap && image->colormap_entries <= 16)
      png_set_packing(png_ptr);

2093
   /* That should have handled all (both) the transforms. */
2094
   if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |
2095
         PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0)
2096 2097 2098
      png_error(png_ptr, "png_write_image: unsupported transformation");

   {
2099
      png_const_bytep row = png_voidcast(png_const_bytep, display->buffer);
2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115
      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.
    */
2116
   if ((linear && alpha) || (!colormap && display->convert_to_8bit))
2117
   {
2118 2119
      png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
         png_get_rowbytes(png_ptr, info_ptr)));
2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140
      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
   {
2141
      png_const_bytep row = png_voidcast(png_const_bytep, display->first_row);
2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156
      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
2157
png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
2158
   const void *buffer, png_int_32 row_stride, const void *colormap)
2159 2160
{
   /* Write the image to the given (FILE*). */
2161
   if (image != NULL || image->version != PNG_IMAGE_VERSION)
2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175
   {
      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;

2176
            png_memset(&display, 0, sizeof display);
2177 2178 2179
            display.image = image;
            display.buffer = buffer;
            display.row_stride = row_stride;
2180
            display.colormap = colormap;
2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201
            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
2202
png_image_write_to_file(png_imagep image, const char *file_name,
2203 2204
   int convert_to_8bit, const void *buffer, png_int_32 row_stride,
   const void *colormap)
2205 2206
{
   /* Write the image to the named file. */
2207
   if (image != NULL || image->version != PNG_IMAGE_VERSION)
2208 2209 2210 2211 2212 2213 2214
   {
      if (file_name != NULL)
      {
         FILE *fp = fopen(file_name, "wb");

         if (fp != NULL)
         {
2215
            if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,
2216
               row_stride, colormap))
2217
            {
2218
               int error; /* from fflush/fclose */
2219

2220 2221 2222 2223 2224
               /* Make sure the file is flushed correctly. */
               if (fflush(fp) == 0 && ferror(fp) == 0)
               {
                  if (fclose(fp) == 0)
                     return 1;
2225

2226 2227
                  error = errno; /* from fclose */
               }
2228

2229
               else
2230
               {
2231 2232
                  error = errno; /* from fflush or ferror */
                  (void)fclose(fp);
2233 2234
               }

2235 2236 2237 2238 2239
               (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));
2240 2241 2242 2243 2244 2245
            }

            else
            {
               /* Clean up: just the opened file. */
               (void)fclose(fp);
2246
               (void)remove(file_name);
2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264
               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 */
2265
#endif /* PNG_WRITE_SUPPORTED */