pngwrite.c 72.3 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

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
/* Write out all the unknown chunks for the current given location */
static void
write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr,
   unsigned int where)
{
   if (info_ptr->unknown_chunks_num)
   {
      png_const_unknown_chunkp up;

      png_debug(5, "writing extra chunks");

      for (up = info_ptr->unknown_chunks;
           up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
           ++up)
         if (up->location & where)
      {
38 39 40 41
         /* If per-chunk unknown chunk handling is enabled use it, otherwise
          * just write the chunks the application has set.
          */
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
42 43 44
         int keep = png_handle_as_unknown(png_ptr, up->name);

         /* NOTE: this code is radically different from the read side in the
45
          * matter of handling an ancillary unknown chunk.  In the read side
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
          * the default behavior is to discard it, in the code below the default
          * behavior is to write it.  Critical chunks are, however, only
          * written if explicitly listed or if the default is set to write all
          * unknown chunks.
          *
          * The default handling is also slightly weird - it is not possible to
          * stop the writing of all unsafe-to-copy chunks!
          *
          * TODO: REVIEW: this would seem to be a bug.
          */
         if (keep != PNG_HANDLE_CHUNK_NEVER &&
             ((up->name[3] & 0x20) /* safe-to-copy overrides everything */ ||
              keep == PNG_HANDLE_CHUNK_ALWAYS ||
              (keep == PNG_HANDLE_CHUNK_AS_DEFAULT &&
               png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS)))
61
#endif
62 63 64 65 66 67 68 69 70 71 72 73
         {
            /* TODO: review, what is wrong with a zero length unknown chunk? */
            if (up->size == 0)
               png_warning(png_ptr, "Writing zero-length unknown chunk");

            png_write_chunk(png_ptr, up->name, up->data, up->size);
         }
      }
   }
}
#endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */

A
Andreas Dilger 已提交
74 75 76
/* 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
77
 * after the image data, put it in png_write_end().  I strongly encourage
A
Andreas Dilger 已提交
78 79 80 81 82
 * 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.
 */
83
void PNGAPI
84
png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
G
Guy Schalnat 已提交
85
{
86
   png_debug(1, "in png_write_info_before_PLTE");
87

88 89
   if (png_ptr == NULL || info_ptr == NULL)
      return;
90

91 92
   if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
   {
93 94
   /* Write PNG signature */
   png_write_sig(png_ptr);
95

96
#ifdef PNG_MNG_FEATURES_SUPPORTED
97
   if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \
98
       (png_ptr->mng_features_permitted))
99
   {
100
      png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
101
      png_ptr->mng_features_permitted = 0;
102 103
   }
#endif
104

105
   /* Write IHDR information. */
A
Andreas Dilger 已提交
106
   png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
107 108
       info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
       info_ptr->filter_type,
109
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
110
       info_ptr->interlace_type
111
#else
112
       0
113
#endif
114 115
      );

116 117
   /* The rest of these check to see if the valid field has the appropriate
    * flag set, and if it does, writes the chunk.
118 119 120 121 122 123 124 125 126 127 128
    *
    * 1.6.0: COLORSPACE support controls the writing of these chunks too, and
    * the chunks will be written if the WRITE routine is there and information
    * is available in the COLORSPACE.  (See png_colorspace_sync_info in png.c
    * for where the valid flags get set.)
    *
    * Under certain circumstances the colorspace can be invalidated without
    * syncing the info_struct 'valid' flags; this happens if libpng detects and
    * error and calls png_error while the color space is being set, yet the
    * application continues writing the PNG.  So check the 'invalid' flag here
    * too.
129
    */
130 131 132 133 134 135 136
#ifdef PNG_GAMMA_SUPPORTED
#  ifdef PNG_WRITE_gAMA_SUPPORTED
      if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&
         (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) &&
         (info_ptr->valid & PNG_INFO_gAMA))
         png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma);
#  endif
137
#endif
138

139
#ifdef PNG_COLORSPACE_SUPPORTED
140 141
   /* Write only one of sRGB or an ICC profile.  If a profile was supplied
    * and it matches one of the known sRGB ones issue a warning.
142 143 144 145
    */
#  ifdef PNG_WRITE_iCCP_SUPPORTED
      if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&
         (info_ptr->valid & PNG_INFO_iCCP))
146 147 148 149 150 151 152
      {
#        ifdef PNG_WRITE_sRGB_SUPPORTED
            if (info_ptr->valid & PNG_INFO_sRGB)
               png_app_warning(png_ptr,
                  "profile matches sRGB but writing iCCP instead");
#        endif

153 154
         png_write_iCCP(png_ptr, info_ptr->iccp_name,
            info_ptr->iccp_profile);
155 156 157 158
      }
#     ifdef PNG_WRITE_sRGB_SUPPORTED
         else
#     endif
159
#  endif
160 161 162 163 164 165

#  ifdef PNG_WRITE_sRGB_SUPPORTED
      if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&
         (info_ptr->valid & PNG_INFO_sRGB))
         png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent);
#  endif /* WRITE_sRGB */
166 167
#endif /* COLORSPACE */

168
#ifdef PNG_WRITE_sBIT_SUPPORTED
A
Andreas Dilger 已提交
169 170
   if (info_ptr->valid & PNG_INFO_sBIT)
      png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
G
Guy Schalnat 已提交
171
#endif
172 173 174 175 176 177 178 179

#ifdef PNG_COLORSPACE_SUPPORTED
#  ifdef PNG_WRITE_cHRM_SUPPORTED
      if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) &&
         (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) &&
         (info_ptr->valid & PNG_INFO_cHRM))
         png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy);
#  endif
180
#endif
181

182
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
183
      write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR);
G
Guy Schalnat 已提交
184
#endif
185

186 187 188 189
      png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
   }
}

190
void PNGAPI
191
png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)
192
{
193
#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
194 195 196
   int i;
#endif

197
   png_debug(1, "in png_write_info");
198

199 200 201
   if (png_ptr == NULL || info_ptr == NULL)
      return;

202 203
   png_write_info_before_PLTE(png_ptr, info_ptr);

A
Andreas Dilger 已提交
204
   if (info_ptr->valid & PNG_INFO_PLTE)
A
Andreas Dilger 已提交
205
      png_write_PLTE(png_ptr, info_ptr->palette,
206
          (png_uint_32)info_ptr->num_palette);
207

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

211
#ifdef PNG_WRITE_tRNS_SUPPORTED
A
Andreas Dilger 已提交
212
   if (info_ptr->valid & PNG_INFO_tRNS)
213
   {
214
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
215
      /* Invert the alpha channel (in tRNS) */
216
      if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&
217
          info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
218 219
      {
         int j;
220
         for (j = 0; j<(int)info_ptr->num_trans; j++)
G
[devel]  
Glenn Randers-Pehrson 已提交
221
            info_ptr->trans_alpha[j] =
222
               (png_byte)(255 - info_ptr->trans_alpha[j]);
223
      }
224
#endif
225
      png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color),
226
          info_ptr->num_trans, info_ptr->color_type);
227
   }
G
Guy Schalnat 已提交
228
#endif
229
#ifdef PNG_WRITE_bKGD_SUPPORTED
A
Andreas Dilger 已提交
230 231
   if (info_ptr->valid & PNG_INFO_bKGD)
      png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
G
Guy Schalnat 已提交
232
#endif
233

234
#ifdef PNG_WRITE_hIST_SUPPORTED
A
Andreas Dilger 已提交
235 236
   if (info_ptr->valid & PNG_INFO_hIST)
      png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
G
Guy Schalnat 已提交
237
#endif
238

239
#ifdef PNG_WRITE_oFFs_SUPPORTED
A
Andreas Dilger 已提交
240 241
   if (info_ptr->valid & PNG_INFO_oFFs)
      png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
242
          info_ptr->offset_unit_type);
G
Guy Schalnat 已提交
243
#endif
244

245
#ifdef PNG_WRITE_pCAL_SUPPORTED
A
Andreas Dilger 已提交
246 247
   if (info_ptr->valid & PNG_INFO_pCAL)
      png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
248 249
          info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
          info_ptr->pcal_units, info_ptr->pcal_params);
A
Andreas Dilger 已提交
250
#endif
251

252
#ifdef PNG_WRITE_sCAL_SUPPORTED
G
[devel]  
Glenn Randers-Pehrson 已提交
253
   if (info_ptr->valid & PNG_INFO_sCAL)
254
      png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
255
          info_ptr->scal_s_width, info_ptr->scal_s_height);
256 257
#endif /* sCAL */

258
#ifdef PNG_WRITE_pHYs_SUPPORTED
A
Andreas Dilger 已提交
259 260
   if (info_ptr->valid & PNG_INFO_pHYs)
      png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
261
          info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
262 263
#endif /* pHYs */

264
#ifdef PNG_WRITE_tIME_SUPPORTED
A
Andreas Dilger 已提交
265
   if (info_ptr->valid & PNG_INFO_tIME)
G
Guy Schalnat 已提交
266
   {
A
Andreas Dilger 已提交
267
      png_write_tIME(png_ptr, &(info_ptr->mod_time));
268
      png_ptr->mode |= PNG_WROTE_tIME;
G
Guy Schalnat 已提交
269
   }
270 271
#endif /* tIME */

272
#ifdef PNG_WRITE_sPLT_SUPPORTED
273
   if (info_ptr->valid & PNG_INFO_sPLT)
274 275
      for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
         png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
276 277
#endif /* sPLT */

278
#ifdef PNG_WRITE_TEXT_SUPPORTED
G
Guy Schalnat 已提交
279
   /* Check to see if we need to write text chunks */
A
Andreas Dilger 已提交
280
   for (i = 0; i < info_ptr->num_text; i++)
G
Guy Schalnat 已提交
281
   {
282
      png_debug2(2, "Writing header text chunk %d, type %d", i,
283
          info_ptr->text[i].compression);
284
      /* An internationalized chunk? */
285
      if (info_ptr->text[i].compression > 0)
286
      {
287
#ifdef PNG_WRITE_iTXt_SUPPORTED
288 289 290 291 292 293 294
         /* 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);
295
#else
296
          png_warning(png_ptr, "Unable to write international text");
297 298 299 300
#endif
          /* Mark this chunk as written */
          info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
      }
301

A
Andreas Dilger 已提交
302
      /* If we want a compressed text chunk */
303
      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
G
Guy Schalnat 已提交
304
      {
305
#ifdef PNG_WRITE_zTXt_SUPPORTED
306
         /* Write compressed chunk */
A
Andreas Dilger 已提交
307
         png_write_zTXt(png_ptr, info_ptr->text[i].key,
308 309
             info_ptr->text[i].text, 0,
             info_ptr->text[i].compression);
G
Guy Schalnat 已提交
310
#else
311
         png_warning(png_ptr, "Unable to write compressed text");
G
Guy Schalnat 已提交
312
#endif
A
Andreas Dilger 已提交
313 314 315
         /* Mark this chunk as written */
         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
      }
316

A
Andreas Dilger 已提交
317 318
      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
      {
319
#ifdef PNG_WRITE_tEXt_SUPPORTED
320
         /* Write uncompressed chunk */
A
Andreas Dilger 已提交
321
         png_write_tEXt(png_ptr, info_ptr->text[i].key,
322 323
             info_ptr->text[i].text,
             0);
324 325
         /* Mark this chunk as written */
         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
G
Guy Schalnat 已提交
326
#else
327
         /* Can't get here */
328
         png_warning(png_ptr, "Unable to write uncompressed text");
G
Guy Schalnat 已提交
329
#endif
G
Guy Schalnat 已提交
330 331
      }
   }
332 333
#endif /* tEXt */

334
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
335
   write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE);
336
#endif
G
Guy Schalnat 已提交
337 338
}

A
Andreas Dilger 已提交
339
/* Writes the end of the PNG file.  If you don't want to write comments or
340 341 342 343
 * 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.
 */
344
void PNGAPI
345
png_write_end(png_structrp png_ptr, png_inforp info_ptr)
G
Guy Schalnat 已提交
346
{
347
   png_debug(1, "in png_write_end");
348

349 350
   if (png_ptr == NULL)
      return;
351

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

355 356
#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
   if (png_ptr->num_palette_max > png_ptr->num_palette)
357
      png_benign_error(png_ptr, "Wrote palette index exceeding num_palette");
358 359
#endif

360
   /* See if user wants us to write information chunks */
A
Andreas Dilger 已提交
361
   if (info_ptr != NULL)
G
Guy Schalnat 已提交
362
   {
363
#ifdef PNG_WRITE_TEXT_SUPPORTED
A
Andreas Dilger 已提交
364
      int i; /* local index variable */
365
#endif
366
#ifdef PNG_WRITE_tIME_SUPPORTED
367
      /* Check to see if user has supplied a time chunk */
368
      if ((info_ptr->valid & PNG_INFO_tIME) &&
369
          !(png_ptr->mode & PNG_WROTE_tIME))
A
Andreas Dilger 已提交
370
         png_write_tIME(png_ptr, &(info_ptr->mod_time));
371

G
Guy Schalnat 已提交
372
#endif
373
#ifdef PNG_WRITE_TEXT_SUPPORTED
374
      /* Loop through comment chunks */
A
Andreas Dilger 已提交
375
      for (i = 0; i < info_ptr->num_text; i++)
G
Guy Schalnat 已提交
376
      {
377
         png_debug2(2, "Writing trailer text chunk %d, type %d", i,
A
Andreas Dilger 已提交
378
            info_ptr->text[i].compression);
379
         /* An internationalized chunk? */
380 381
         if (info_ptr->text[i].compression > 0)
         {
382
#ifdef PNG_WRITE_iTXt_SUPPORTED
383
            /* Write international chunk */
384
            png_write_iTXt(png_ptr,
385 386 387 388 389
                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);
390
#else
391
            png_warning(png_ptr, "Unable to write international text");
392
#endif
393 394
            /* Mark this chunk as written */
            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
395
         }
396

397
         else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
G
Guy Schalnat 已提交
398
         {
399
#ifdef PNG_WRITE_zTXt_SUPPORTED
400
            /* Write compressed chunk */
A
Andreas Dilger 已提交
401
            png_write_zTXt(png_ptr, info_ptr->text[i].key,
402 403
                info_ptr->text[i].text, 0,
                info_ptr->text[i].compression);
A
Andreas Dilger 已提交
404
#else
405
            png_warning(png_ptr, "Unable to write compressed text");
G
Guy Schalnat 已提交
406
#endif
A
Andreas Dilger 已提交
407 408 409
            /* Mark this chunk as written */
            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
         }
410

A
Andreas Dilger 已提交
411 412
         else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
         {
413
#ifdef PNG_WRITE_tEXt_SUPPORTED
414
            /* Write uncompressed chunk */
A
Andreas Dilger 已提交
415
            png_write_tEXt(png_ptr, info_ptr->text[i].key,
416
                info_ptr->text[i].text, 0);
A
Andreas Dilger 已提交
417
#else
418
            png_warning(png_ptr, "Unable to write uncompressed text");
G
Guy Schalnat 已提交
419
#endif
A
Andreas Dilger 已提交
420 421 422

            /* Mark this chunk as written */
            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
G
Guy Schalnat 已提交
423 424
         }
      }
425
#endif
426
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
427
      write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT);
G
Guy Schalnat 已提交
428
#endif
G
Guy Schalnat 已提交
429
   }
G
Guy Schalnat 已提交
430 431 432

   png_ptr->mode |= PNG_AFTER_IDAT;

433
   /* Write end of PNG file */
G
Guy Schalnat 已提交
434
   png_write_IEND(png_ptr);
435 436 437
   /* 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
438 439
    * experiences a problem, please try building libpng with
    * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to
440 441
    * png-mng-implement at lists.sf.net .
    */
442
#ifdef PNG_WRITE_FLUSH_SUPPORTED
443
#  ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
444
   png_flush(png_ptr);
445
#  endif
446
#endif
G
Guy Schalnat 已提交
447 448
}

449
#ifdef PNG_CONVERT_tIME_SUPPORTED
450
/* "tm" structure is not supported on WindowsCE */
451
void PNGAPI
452
png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime)
G
Guy Schalnat 已提交
453
{
454
   png_debug(1, "in png_convert_from_struct_tm");
455

G
Guy Schalnat 已提交
456 457 458 459 460 461
   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 已提交
462 463
}

464
void PNGAPI
G
Guy Schalnat 已提交
465
png_convert_from_time_t(png_timep ptime, time_t ttime)
G
Guy Schalnat 已提交
466 467 468
{
   struct tm *tbuf;

469
   png_debug(1, "in png_convert_from_time_t");
470

G
Guy Schalnat 已提交
471 472 473
   tbuf = gmtime(&ttime);
   png_convert_from_struct_tm(ptime, tbuf);
}
G
Guy Schalnat 已提交
474
#endif
475

A
Andreas Dilger 已提交
476
/* Initialize png_ptr structure, and allocate any memory needed */
477 478 479
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 已提交
480
{
481
#ifndef PNG_USER_MEM_SUPPORTED
482
   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
483 484 485 486
      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);
487 488 489
}

/* Alternate initialize png_ptr structure, and allocate any memory needed */
490 491
PNG_FUNCTION(png_structp,PNGAPI
png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
492
    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
493
    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
494
{
495
   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
496
      error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
497
#endif /* PNG_USER_MEM_SUPPORTED */
498

499 500 501
   /* Set the zlib control values to defaults; they can be overridden by the
    * application after the struct has been created.
    */
502 503
   png_ptr->zbuffer_size = PNG_ZBUF_SIZE;

504 505 506 507 508 509 510 511 512 513 514 515 516 517
   png_ptr->zlib_strategy = Z_FILTERED; /* may be overridden if no filters */
   png_ptr->zlib_level = Z_DEFAULT_COMPRESSION;
   png_ptr->zlib_mem_level = 8;
   png_ptr->zlib_window_bits = 15;
   png_ptr->zlib_method = 8;

#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
   png_ptr->zlib_text_strategy = Z_DEFAULT_STRATEGY;
   png_ptr->zlib_text_level = Z_DEFAULT_COMPRESSION;
   png_ptr->zlib_text_mem_level = 8;
   png_ptr->zlib_text_window_bits = 15;
   png_ptr->zlib_text_method = 8;
#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */

518 519 520 521 522 523 524
   /* This is a highly dubious configuration option; by default it is off, but
    * it may be appropriate for private builds that are testing extensions not
    * conformant to the current specification, or of applications that must not
    * fail to write at all costs!
    */
#  ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED
      png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;
525 526 527 528 529 530 531 532 533 534
      /* In stable builds only warn if an application error can be completely
       * handled.
       */
#  endif

   /* App warnings are warnings in release (or release candidate) builds but
    * are errors during development.
    */
#  if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC
      png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;
535 536
#  endif

537
   if (png_ptr != NULL)
538
   {
539 540 541 542 543
      /* 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);
544
   }
545

546
   return png_ptr;
G
Guy Schalnat 已提交
547 548
}

549

550 551 552 553 554
/* 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.
 */
555
void PNGAPI
556
png_write_rows(png_structrp png_ptr, png_bytepp row,
557
    png_uint_32 num_rows)
G
Guy Schalnat 已提交
558 559
{
   png_uint_32 i; /* row counter */
G
Guy Schalnat 已提交
560
   png_bytepp rp; /* row pointer */
G
Guy Schalnat 已提交
561

562
   png_debug(1, "in png_write_rows");
563 564 565 566

   if (png_ptr == NULL)
      return;

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

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

584 585 586
   if (png_ptr == NULL)
      return;

587
   png_debug(1, "in png_write_image");
588

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

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

615 616
   if (png_ptr == NULL)
      return;
617

618
   png_debug2(1, "in png_write_row (row %u, pass %d)",
619
      png_ptr->row_number, png_ptr->pass);
620

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

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

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

646
#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
647 648
      if (png_ptr->transformations & PNG_PACK)
         png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");
649
#endif
650

651
#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
652 653
      if (png_ptr->transformations & PNG_SHIFT)
         png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");
654
#endif
655

656
#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
657 658
      if (png_ptr->transformations & PNG_BGR)
         png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");
659
#endif
660

661
#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
662 663
      if (png_ptr->transformations & PNG_SWAP_BYTES)
         png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");
664 665
#endif

G
Guy Schalnat 已提交
666 667 668
      png_write_start_row(png_ptr);
   }

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

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

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

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

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

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

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

         default: /* error: ignore it */
            break;
G
Guy Schalnat 已提交
733 734
      }
   }
G
Guy Schalnat 已提交
735
#endif
G
Guy Schalnat 已提交
736

737
   /* Set up row info for transformations */
738 739 740 741 742 743 744 745 746 747 748 749 750
   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 已提交
751 752

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

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

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

776 777 778 779 780 781 782
   /* 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");

783
#ifdef PNG_MNG_FEATURES_SUPPORTED
784 785 786 787 788 789 790 791 792
   /* 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
    */
793
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
794
       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
795 796
   {
      /* Intrapixel differencing */
797
      png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);
798 799 800
   }
#endif

801 802
/* Added at libpng-1.5.10 */
#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
803
   /* Check for out-of-range palette index */
804 805
   if (row_info.color_type == PNG_COLOR_TYPE_PALETTE &&
       png_ptr->num_palette_max >= 0)
806
      png_do_check_palette_indexes(png_ptr, &row_info);
807 808
#endif

A
Andreas Dilger 已提交
809
   /* Find a filter if necessary, filter the row and write it out. */
810
   png_write_find_filter(png_ptr, &row_info);
811 812 813

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

816
#ifdef PNG_WRITE_FLUSH_SUPPORTED
G
Guy Schalnat 已提交
817
/* Set the automatic flush interval or 0 to turn flushing off */
818
void PNGAPI
819
png_set_flush(png_structrp png_ptr, int nrows)
G
Guy Schalnat 已提交
820
{
821
   png_debug(1, "in png_set_flush");
822

823 824
   if (png_ptr == NULL)
      return;
825

G
Guy Schalnat 已提交
826
   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
G
Guy Schalnat 已提交
827 828
}

829
/* Flush the current output buffers now */
830
void PNGAPI
831
png_write_flush(png_structrp png_ptr)
G
Guy Schalnat 已提交
832
{
833
   png_debug(1, "in png_write_flush");
834

835 836
   if (png_ptr == NULL)
      return;
837

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

842
   png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH);
G
Guy Schalnat 已提交
843 844
   png_ptr->flush_rows = 0;
   png_flush(png_ptr);
G
Guy Schalnat 已提交
845
}
G
Guy Schalnat 已提交
846
#endif /* PNG_WRITE_FLUSH_SUPPORTED */
G
Guy Schalnat 已提交
847

848
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
849
static void png_reset_filter_heuristics(png_structrp png_ptr);/* forward decl */
850
#endif
G
Guy Schalnat 已提交
851

852 853
/* Free any memory used in png_ptr struct without freeing the struct itself. */
static void
854
png_write_destroy(png_structrp png_ptr)
G
Guy Schalnat 已提交
855
{
856
   png_debug(1, "in png_write_destroy");
857 858

   /* Free any memory zlib uses */
859
   if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)
860
      deflateEnd(&png_ptr->zstream);
G
Guy Schalnat 已提交
861

862
   /* Free our memory.  png_free checks NULL for us. */
863
   png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
A
Andreas Dilger 已提交
864
   png_free(png_ptr, png_ptr->row_buf);
865
#ifdef PNG_WRITE_FILTER_SUPPORTED
A
Andreas Dilger 已提交
866 867 868 869 870
   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);
871
#endif
872

873
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
874 875
   /* Use this to save a little code space, it doesn't free the filter_costs */
   png_reset_filter_heuristics(png_ptr);
A
Andreas Dilger 已提交
876 877
   png_free(png_ptr, png_ptr->filter_costs);
   png_free(png_ptr, png_ptr->inv_filter_costs);
878
#endif
G
Guy Schalnat 已提交
879

880
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
881
   png_free(png_ptr, png_ptr->chunk_list);
882
#endif
G
Guy Schalnat 已提交
883

884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
   /* 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 已提交
901

902 903
   if (png_ptr_ptr != NULL)
   {
904
      png_structrp png_ptr = *png_ptr_ptr;
G
Guy Schalnat 已提交
905

906 907 908
      if (png_ptr != NULL) /* added in libpng 1.6.0 */
      {
         png_destroy_info_struct(png_ptr, info_ptr_ptr);
G
Guy Schalnat 已提交
909

910 911 912 913 914
         *png_ptr_ptr = NULL;
         png_write_destroy(png_ptr);
         png_destroy_png_struct(png_ptr);
      }
   }
G
Guy Schalnat 已提交
915
}
G
Guy Schalnat 已提交
916

A
Andreas Dilger 已提交
917
/* Allow the application to select one or more row filters to use. */
918
void PNGAPI
919
png_set_filter(png_structrp png_ptr, int method, int filters)
G
Guy Schalnat 已提交
920
{
921
   png_debug(1, "in png_set_filter");
922

923 924
   if (png_ptr == NULL)
      return;
925

926
#ifdef PNG_MNG_FEATURES_SUPPORTED
927
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
928 929
       (method == PNG_INTRAPIXEL_DIFFERENCING))
      method = PNG_FILTER_TYPE_BASE;
930

931
#endif
A
Andreas Dilger 已提交
932
   if (method == PNG_FILTER_TYPE_BASE)
G
Guy Schalnat 已提交
933 934 935
   {
      switch (filters & (PNG_ALL_FILTERS | 0x07))
      {
936
#ifdef PNG_WRITE_FILTER_SUPPORTED
G
Guy Schalnat 已提交
937 938
         case 5:
         case 6:
939 940
         case 7: png_app_error(png_ptr, "Unknown row filter for method 0");
            /* Fall through */
941
#endif /* PNG_WRITE_FILTER_SUPPORTED */
942
         case PNG_FILTER_VALUE_NONE:
943
            png_ptr->do_filter = PNG_FILTER_NONE; break;
944

945
#ifdef PNG_WRITE_FILTER_SUPPORTED
946
         case PNG_FILTER_VALUE_SUB:
947
            png_ptr->do_filter = PNG_FILTER_SUB; break;
948

949
         case PNG_FILTER_VALUE_UP:
950
            png_ptr->do_filter = PNG_FILTER_UP; break;
951

952
         case PNG_FILTER_VALUE_AVG:
953
            png_ptr->do_filter = PNG_FILTER_AVG; break;
954

955
         case PNG_FILTER_VALUE_PAETH:
956
            png_ptr->do_filter = PNG_FILTER_PAETH; break;
957

958 959
         default:
            png_ptr->do_filter = (png_byte)filters; break;
960
#else
961
         default:
962
            png_app_error(png_ptr, "Unknown row filter for method 0");
963
#endif /* PNG_WRITE_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
964 965
      }

A
Andreas Dilger 已提交
966 967 968 969 970 971 972 973
      /* 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 已提交
974
       */
A
Andreas Dilger 已提交
975
      if (png_ptr->row_buf != NULL)
G
Guy Schalnat 已提交
976
      {
977
#ifdef PNG_WRITE_FILTER_SUPPORTED
978
         if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)
G
Guy Schalnat 已提交
979
         {
A
Andreas Dilger 已提交
980
            png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
981
                (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
982
            png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
G
Guy Schalnat 已提交
983 984
         }

985
         if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)
G
Guy Schalnat 已提交
986
         {
A
Andreas Dilger 已提交
987
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
988
            {
A
Andreas Dilger 已提交
989
               png_warning(png_ptr, "Can't add Up filter after starting");
990 991
               png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
                   ~PNG_FILTER_UP);
G
Guy Schalnat 已提交
992
            }
993

G
Guy Schalnat 已提交
994 995
            else
            {
A
Andreas Dilger 已提交
996
               png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
997
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
998
               png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
G
Guy Schalnat 已提交
999 1000 1001
            }
         }

1002
         if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)
G
Guy Schalnat 已提交
1003
         {
A
Andreas Dilger 已提交
1004
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
1005
            {
A
Andreas Dilger 已提交
1006
               png_warning(png_ptr, "Can't add Average filter after starting");
1007 1008
               png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
                   ~PNG_FILTER_AVG);
G
Guy Schalnat 已提交
1009
            }
1010

G
Guy Schalnat 已提交
1011 1012
            else
            {
A
Andreas Dilger 已提交
1013
               png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
1014
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
1015
               png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
G
Guy Schalnat 已提交
1016 1017 1018
            }
         }

1019
         if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&
A
Andreas Dilger 已提交
1020
             png_ptr->paeth_row == NULL)
G
Guy Schalnat 已提交
1021
         {
A
Andreas Dilger 已提交
1022
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
1023 1024
            {
               png_warning(png_ptr, "Can't add Paeth filter after starting");
1025
               png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
G
Guy Schalnat 已提交
1026
            }
1027

G
Guy Schalnat 已提交
1028 1029
            else
            {
1030
               png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
1031
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
1032
               png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
G
Guy Schalnat 已提交
1033 1034 1035 1036
            }
         }

         if (png_ptr->do_filter == PNG_NO_FILTERS)
1037
#endif /* PNG_WRITE_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
1038 1039 1040 1041
            png_ptr->do_filter = PNG_FILTER_NONE;
      }
   }
   else
A
Andreas Dilger 已提交
1042 1043 1044 1045 1046 1047 1048 1049
      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
1050 1051
 * better compression.
 */
1052
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED      /* GRR 970116 */
1053
/* Convenience reset API. */
1054
static void
1055
png_reset_filter_heuristics(png_structrp png_ptr)
A
Andreas Dilger 已提交
1056
{
1057 1058 1059 1060 1061 1062 1063 1064
   /* 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 已提交
1065
   {
1066 1067 1068
      png_bytep old = png_ptr->prev_filters;
      png_ptr->prev_filters = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1069
   }
1070
   if (png_ptr->filter_weights != NULL)
A
Andreas Dilger 已提交
1071
   {
1072 1073 1074
      png_uint_16p old = png_ptr->filter_weights;
      png_ptr->filter_weights = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1075 1076
   }

1077
   if (png_ptr->inv_filter_weights != NULL)
A
Andreas Dilger 已提交
1078
   {
1079 1080 1081
      png_uint_16p old = png_ptr->inv_filter_weights;
      png_ptr->inv_filter_weights = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1082 1083
   }

1084 1085 1086 1087
   /* Leave the filter_costs - this array is fixed size. */
}

static int
1088
png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method,
1089 1090 1091 1092 1093 1094 1095
   int num_weights)
{
   if (png_ptr == NULL)
      return 0;

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

1097 1098 1099 1100 1101
   /* 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 已提交
1102
   {
1103 1104 1105
      int i;

      if (num_weights > 0)
A
Andreas Dilger 已提交
1106 1107
      {
         png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
1108
             (png_uint_32)((sizeof (png_byte)) * num_weights));
A
Andreas Dilger 已提交
1109

1110 1111
         /* To make sure that the weighting starts out fairly */
         for (i = 0; i < num_weights; i++)
A
Andreas Dilger 已提交
1112
         {
1113
            png_ptr->prev_filters[i] = 255;
A
Andreas Dilger 已提交
1114 1115
         }

1116
         png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
1117
             (png_uint_32)((sizeof (png_uint_16)) * num_weights));
A
Andreas Dilger 已提交
1118

1119
         png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
1120
             (png_uint_32)((sizeof (png_uint_16)) * num_weights));
1121

1122 1123 1124 1125 1126
         for (i = 0; i < num_weights; i++)
         {
            png_ptr->inv_filter_weights[i] =
            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
         }
1127

1128 1129
         /* Safe to set this now */
         png_ptr->num_prev_filters = (png_byte)num_weights;
A
Andreas Dilger 已提交
1130 1131
      }

1132 1133 1134 1135
      /* 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 已提交
1136
      {
1137
         png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
1138
             (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST));
1139

1140
         png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
1141
             (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST));
1142
      }
1143

1144 1145
      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
      {
1146 1147
         png_ptr->inv_filter_costs[i] =
         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
A
Andreas Dilger 已提交
1148
      }
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164

      /* 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 已提交
1165
   }
1166
}
A
Andreas Dilger 已提交
1167

1168 1169 1170
/* Provide floating and fixed point APIs */
#ifdef PNG_FLOATING_POINT_SUPPORTED
void PNGAPI
1171
png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method,
1172 1173
    int num_weights, png_const_doublep filter_weights,
    png_const_doublep filter_costs)
1174 1175 1176 1177 1178
{
   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 已提交
1179
    */
1180 1181
   if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
      return;
A
Andreas Dilger 已提交
1182

1183 1184 1185 1186 1187 1188
   /* 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++)
      {
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
         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);
         }
1203
      }
A
Andreas Dilger 已提交
1204

1205 1206 1207 1208 1209 1210 1211 1212
      /* 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 已提交
1213
      {
1214 1215
         png_ptr->inv_filter_costs[i] =
             (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5);
1216

1217 1218
         png_ptr->filter_costs[i] =
             (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5);
A
Andreas Dilger 已提交
1219 1220
      }
   }
1221 1222
}
#endif /* FLOATING_POINT */
A
Andreas Dilger 已提交
1223

1224 1225
#ifdef PNG_FIXED_POINT_SUPPORTED
void PNGAPI
1226
png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method,
1227 1228
    int num_weights, png_const_fixed_point_p filter_weights,
    png_const_fixed_point_p filter_costs)
1229 1230 1231 1232 1233
{
   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 已提交
1234
    */
1235 1236 1237 1238 1239
   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 已提交
1240
   {
1241 1242
      int i;
      for (i = 0; i < num_weights; i++)
A
Andreas Dilger 已提交
1243
      {
1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
         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 已提交
1258
      }
1259

1260 1261 1262 1263 1264 1265 1266 1267 1268
      /* 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 已提交
1269
      {
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
         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;
1283

1284
         png_ptr->filter_costs[i] = (png_uint_16)tmp;
A
Andreas Dilger 已提交
1285 1286
      }
   }
G
Guy Schalnat 已提交
1287
}
1288
#endif /* FIXED_POINT */
A
Andreas Dilger 已提交
1289
#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
1290

1291
void PNGAPI
1292
png_set_compression_level(png_structrp png_ptr, int level)
G
Guy Schalnat 已提交
1293
{
1294
   png_debug(1, "in png_set_compression_level");
1295

1296 1297
   if (png_ptr == NULL)
      return;
1298

G
Guy Schalnat 已提交
1299 1300 1301
   png_ptr->zlib_level = level;
}

1302
void PNGAPI
1303
png_set_compression_mem_level(png_structrp png_ptr, int mem_level)
G
Guy Schalnat 已提交
1304
{
1305
   png_debug(1, "in png_set_compression_mem_level");
1306

1307 1308
   if (png_ptr == NULL)
      return;
1309

G
Guy Schalnat 已提交
1310
   png_ptr->zlib_mem_level = mem_level;
G
Guy Schalnat 已提交
1311 1312
}

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

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

1321 1322
   /* The flag setting here prevents the libpng dynamic selection of strategy.
    */
G
Guy Schalnat 已提交
1323
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
G
Guy Schalnat 已提交
1324 1325 1326
   png_ptr->zlib_strategy = strategy;
}

1327 1328 1329
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
 * smaller value of window_bits if it can do so safely.
 */
1330
void PNGAPI
1331
png_set_compression_window_bits(png_structrp png_ptr, int window_bits)
G
Guy Schalnat 已提交
1332
{
1333 1334
   if (png_ptr == NULL)
      return;
1335

1336 1337 1338
   /* Prior to 1.6.0 this would warn but then set the window_bits value, this
    * meant that negative window bits values could be selected which would cause
    * libpng to write a non-standard PNG file with raw deflate or gzip
1339
    * compressed IDAT or ancillary chunks.  Such files can be read and there is
1340 1341
    * no warning on read, so this seems like a very bad idea.
    */
G
Guy Schalnat 已提交
1342
   if (window_bits > 15)
1343
   {
G
Guy Schalnat 已提交
1344
      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
1345 1346
      window_bits = 15;
   }
1347

1348
   else if (window_bits < 8)
1349
   {
1350
      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
1351 1352
      window_bits = 8;
   }
1353

G
Guy Schalnat 已提交
1354 1355 1356
   png_ptr->zlib_window_bits = window_bits;
}

1357
void PNGAPI
1358
png_set_compression_method(png_structrp png_ptr, int method)
G
Guy Schalnat 已提交
1359
{
1360
   png_debug(1, "in png_set_compression_method");
1361

1362 1363
   if (png_ptr == NULL)
      return;
1364

1365 1366 1367
   /* This would produce an invalid PNG file if it worked, but it doesn't and
    * deflate will fault it, so it is harmless to just warn here.
    */
G
Guy Schalnat 已提交
1368 1369
   if (method != 8)
      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
1370

G
Guy Schalnat 已提交
1371
   png_ptr->zlib_method = method;
G
Guy Schalnat 已提交
1372 1373
}

1374
/* The following were added to libpng-1.5.4 */
1375
#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
1376
void PNGAPI
1377
png_set_text_compression_level(png_structrp png_ptr, int level)
1378 1379 1380 1381 1382 1383 1384 1385 1386 1387
{
   png_debug(1, "in png_set_text_compression_level");

   if (png_ptr == NULL)
      return;

   png_ptr->zlib_text_level = level;
}

void PNGAPI
1388
png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
{
   png_debug(1, "in png_set_text_compression_mem_level");

   if (png_ptr == NULL)
      return;

   png_ptr->zlib_text_mem_level = mem_level;
}

void PNGAPI
1399
png_set_text_compression_strategy(png_structrp png_ptr, int strategy)
1400 1401 1402 1403 1404 1405 1406 1407 1408
{
   png_debug(1, "in png_set_text_compression_strategy");

   if (png_ptr == NULL)
      return;

   png_ptr->zlib_text_strategy = strategy;
}

1409 1410 1411
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
 * smaller value of window_bits if it can do so safely.
 */
1412
void PNGAPI
1413
png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits)
1414 1415 1416 1417 1418
{
   if (png_ptr == NULL)
      return;

   if (window_bits > 15)
1419
   {
1420
      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
1421 1422
      window_bits = 15;
   }
1423 1424

   else if (window_bits < 8)
1425
   {
1426
      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
1427 1428
      window_bits = 8;
   }
1429 1430 1431 1432 1433

   png_ptr->zlib_text_window_bits = window_bits;
}

void PNGAPI
1434
png_set_text_compression_method(png_structrp png_ptr, int method)
1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445
{
   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->zlib_text_method = method;
}
1446
#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
1447
/* end of API added to libpng-1.5.4 */
1448

1449
void PNGAPI
1450
png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)
1451
{
1452 1453
   if (png_ptr == NULL)
      return;
1454

1455 1456 1457
   png_ptr->write_row_fn = write_row_fn;
}

1458
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
1459
void PNGAPI
1460
png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
1461
    write_user_transform_fn)
1462
{
1463
   png_debug(1, "in png_set_write_user_transform_fn");
1464

1465 1466
   if (png_ptr == NULL)
      return;
1467

1468 1469 1470 1471
   png_ptr->transformations |= PNG_USER_TRANSFORM;
   png_ptr->write_user_transform_fn = write_user_transform_fn;
}
#endif
1472 1473


1474
#ifdef PNG_INFO_IMAGE_SUPPORTED
1475
void PNGAPI
1476
png_write_png(png_structrp png_ptr, png_inforp info_ptr,
1477
    int transforms, voidp params)
1478
{
1479 1480
   if (png_ptr == NULL || info_ptr == NULL)
      return;
1481 1482 1483 1484 1485 1486

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

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

1487
#ifdef PNG_WRITE_INVERT_SUPPORTED
1488
   /* Invert monochrome pixels */
1489
   if (transforms & PNG_TRANSFORM_INVERT_MONO)
1490
      png_set_invert_mono(png_ptr);
1491 1492
#endif

1493
#ifdef PNG_WRITE_SHIFT_SUPPORTED
1494 1495 1496 1497
   /* Shift the pixels up to a legal bit depth and fill in
    * as appropriate to correctly scale the image.
    */
   if ((transforms & PNG_TRANSFORM_SHIFT)
1498
       && (info_ptr->valid & PNG_INFO_sBIT))
1499
      png_set_shift(png_ptr, &info_ptr->sig_bit);
1500 1501
#endif

1502
#ifdef PNG_WRITE_PACK_SUPPORTED
1503
   /* Pack pixels into bytes */
1504 1505 1506 1507
   if (transforms & PNG_TRANSFORM_PACKING)
       png_set_packing(png_ptr);
#endif

1508
#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
1509
   /* Swap location of alpha bytes from ARGB to RGBA */
1510
   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
1511
      png_set_swap_alpha(png_ptr);
1512 1513
#endif

1514
#ifdef PNG_WRITE_FILLER_SUPPORTED
1515
   /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */
1516
   if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)
1517
      png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
1518

1519
   else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)
1520
      png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
1521 1522
#endif

1523
#ifdef PNG_WRITE_BGR_SUPPORTED
1524
   /* Flip BGR pixels to RGB */
1525
   if (transforms & PNG_TRANSFORM_BGR)
1526
      png_set_bgr(png_ptr);
1527 1528
#endif

1529
#ifdef PNG_WRITE_SWAP_SUPPORTED
1530
   /* Swap bytes of 16-bit files to most significant byte first */
1531
   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
1532
      png_set_swap(png_ptr);
1533 1534
#endif

1535
#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
1536
   /* Swap bits of 1, 2, 4 bit packed pixel formats */
1537
   if (transforms & PNG_TRANSFORM_PACKSWAP)
1538
      png_set_packswap(png_ptr);
1539 1540
#endif

1541
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
1542 1543 1544 1545 1546
   /* Invert the alpha channel from opacity to transparency */
   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
      png_set_invert_alpha(png_ptr);
#endif

1547 1548
   /* ----------------------- end of transformations ------------------- */

1549
   /* Write the bits */
1550 1551 1552 1553 1554
   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);
1555

1556 1557
   PNG_UNUSED(transforms)   /* Quiet compiler warnings */
   PNG_UNUSED(params)
1558 1559
}
#endif
1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576


#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)
      {
1577
         png_controlp control = png_voidcast(png_controlp,
1578
            png_malloc_warn(png_ptr, (sizeof *control)));
1579 1580 1581

         if (control != NULL)
         {
1582
            memset(control, 0, (sizeof *control));
1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598

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

1599
   return png_image_error(image, "png_image_write_: out of memory");
1600 1601 1602 1603 1604 1605 1606 1607 1608
}

/* Arguments to png_image_write_main: */
typedef struct
{
   /* Arguments: */
   png_imagep      image;
   png_const_voidp buffer;
   png_int_32      row_stride;
1609
   png_const_voidp colormap;
1610 1611 1612 1613 1614 1615 1616
   int             convert_to_8bit;
   /* Local variables: */
   png_const_voidp first_row;
   ptrdiff_t       row_bytes;
   png_voidp       local_row;
} png_image_write_control;

1617 1618
/* 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
1619 1620 1621 1622 1623
 * png_image format value.
 */
static int
png_write_image_16bit(png_voidp argument)
{
1624 1625
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1626
   png_imagep image = display->image;
1627
   png_structrp png_ptr = image->opaque->png_ptr;
1628

1629 1630 1631
   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);
1632
   png_uint_16p row_end;
1633
   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654
   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
1655
    * row; this is correct.
1656 1657 1658 1659 1660 1661 1662 1663 1664 1665
    */
   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)
      {
1666
         const png_uint_16 alpha = in_ptr[aindex];
1667 1668 1669 1670 1671 1672
         png_uint_32 reciprocal = 0;
         int c;

         out_ptr[aindex] = alpha;

         /* Calculate a reciprocal.  The correct calculation is simply
1673
          * component/alpha*65535 << 15. (I.e. 15 bits of precision); this
1674 1675 1676 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
          * 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;
      }

1714
      png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));
1715 1716 1717 1718 1719 1720 1721 1722 1723
      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.
1724 1725 1726 1727
 *
 * 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.
1728
 */
1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 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
#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;
}

1776 1777 1778
static int
png_write_image_8bit(png_voidp argument)
{
1779 1780
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1781
   png_imagep image = display->image;
1782
   png_structrp png_ptr = image->opaque->png_ptr;
1783

1784 1785 1786
   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);
1787
   png_uint_32 y = image->height;
1788
   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;
1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812

   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;

1813
         while (out_ptr < row_end)
1814 1815
         {
            png_uint_16 alpha = in_ptr[aindex];
1816
            png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
1817 1818 1819
            png_uint_32 reciprocal = 0;
            int c;

1820 1821
            /* Scale and write the alpha channel. */
            out_ptr[aindex] = alphabyte;
1822

1823 1824
            if (alphabyte > 0 && alphabyte < 255)
               reciprocal = UNP_RECIPROCAL(alpha);
1825 1826 1827

            c = channels;
            do /* always at least one channel */
1828
               *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal);
1829 1830 1831 1832 1833 1834
            while (--c > 0);

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

1836 1837
         png_write_row(png_ptr, png_voidcast(png_const_bytep,
            display->local_row));
1838
         input_row += display->row_bytes/(sizeof (png_uint_16));
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
      } /* 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;
}

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
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];

1899 1900
   memset(tRNS, 255, (sizeof tRNS));
   memset(palette, 0, (sizeof palette));
1901 1902 1903 1904 1905 1906 1907 1908 1909

   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);
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
         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);
1966

1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014
         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;
}

2015 2016 2017
static int
png_image_write_main(png_voidp argument)
{
2018 2019
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
2020
   png_imagep image = display->image;
2021 2022
   png_structrp png_ptr = image->opaque->png_ptr;
   png_inforp info_ptr = image->opaque->info_ptr;
2023 2024
   png_uint_32 format = image->format;

2025 2026 2027 2028
   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;
2029

2030 2031 2032 2033 2034
#  ifdef PNG_BENIGN_ERRORS_SUPPORTED
      /* Make sure we error out on any bad situation */
      png_set_benign_errors(png_ptr, 0/*error*/);
#  endif

2035 2036 2037 2038
   /* Default the 'row_stride' parameter if required. */
   if (display->row_stride == 0)
      display->row_stride = PNG_IMAGE_ROW_STRIDE(*image);

2039
   /* Set the required transforms then write the rows in the correct order. */
2040
   if (format & PNG_FORMAT_FLAG_COLORMAP)
2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057
   {
      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");
   }
2058 2059 2060 2061 2062 2063 2064

   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);
2065 2066 2067 2068 2069 2070

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

2072 2073 2074 2075
   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);
2076 2077 2078 2079 2080 2081 2082 2083 2084

      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
         );
2085 2086
   }

2087
   else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))
2088 2089
      png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);

2090 2091 2092 2093 2094 2095
   /* 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);

2096 2097 2098 2099 2100
   /* 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.
2101 2102
    *
    * First check for a little endian system if writing 16 bit files.
2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114
    */
   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)
      {
2115
         if (!colormap && (format & PNG_FORMAT_FLAG_COLOR) != 0)
2116
            png_set_bgr(png_ptr);
2117 2118 2119 2120 2121 2122 2123
         format &= ~PNG_FORMAT_FLAG_BGR;
      }
#  endif

#  ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
      if (format & PNG_FORMAT_FLAG_AFIRST)
      {
2124
         if (!colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0)
2125
            png_set_swap_alpha(png_ptr);
2126 2127 2128 2129
         format &= ~PNG_FORMAT_FLAG_AFIRST;
      }
#  endif

2130 2131 2132 2133 2134 2135
   /* 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);

2136
   /* That should have handled all (both) the transforms. */
2137
   if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |
2138
         PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0)
2139 2140 2141
      png_error(png_ptr, "png_write_image: unsupported transformation");

   {
2142
      png_const_bytep row = png_voidcast(png_const_bytep, display->buffer);
2143 2144 2145
      ptrdiff_t row_bytes = display->row_stride;

      if (linear)
2146
         row_bytes *= (sizeof (png_uint_16));
2147 2148 2149 2150 2151 2152 2153 2154

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

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

2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166
   /* Apply 'fast' options if the flag is set. */
   if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0)
   {
      png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS);
      /* NOTE: determined by experiment using pngstest, this reflects some
       * balance between the time to write the image once and the time to read
       * it about 50 times.  The speed-up in pngstest was about 10-20% of the
       * total (user) time on a heavily loaded system.
       */
      png_set_compression_level(png_ptr, 3);
   }

2167 2168 2169 2170
   /* 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.
    */
2171
   if ((linear && alpha) || (!colormap && display->convert_to_8bit))
2172
   {
2173 2174
      png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
         png_get_rowbytes(png_ptr, info_ptr)));
2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195
      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
   {
2196
      png_const_bytep row = png_voidcast(png_const_bytep, display->first_row);
2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211
      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
2212
png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
2213
   const void *buffer, png_int_32 row_stride, const void *colormap)
2214 2215
{
   /* Write the image to the given (FILE*). */
2216
   if (image != NULL && image->version == PNG_IMAGE_VERSION)
2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230
   {
      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;

2231
            memset(&display, 0, (sizeof display));
2232 2233 2234
            display.image = image;
            display.buffer = buffer;
            display.row_stride = row_stride;
2235
            display.colormap = colormap;
2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251
            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");
   }

2252 2253 2254 2255
   else if (image != NULL)
      return png_image_error(image,
         "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");

2256 2257 2258 2259 2260
   else
      return 0;
}

int PNGAPI
2261
png_image_write_to_file(png_imagep image, const char *file_name,
2262 2263
   int convert_to_8bit, const void *buffer, png_int_32 row_stride,
   const void *colormap)
2264 2265
{
   /* Write the image to the named file. */
2266
   if (image != NULL && image->version == PNG_IMAGE_VERSION)
2267 2268 2269 2270 2271 2272 2273
   {
      if (file_name != NULL)
      {
         FILE *fp = fopen(file_name, "wb");

         if (fp != NULL)
         {
2274
            if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,
2275
               row_stride, colormap))
2276
            {
2277
               int error; /* from fflush/fclose */
2278

2279 2280 2281 2282 2283
               /* Make sure the file is flushed correctly. */
               if (fflush(fp) == 0 && ferror(fp) == 0)
               {
                  if (fclose(fp) == 0)
                     return 1;
2284

2285 2286
                  error = errno; /* from fclose */
               }
2287

2288
               else
2289
               {
2290 2291
                  error = errno; /* from fflush or ferror */
                  (void)fclose(fp);
2292 2293
               }

2294 2295 2296 2297 2298
               (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));
2299 2300 2301 2302 2303 2304
            }

            else
            {
               /* Clean up: just the opened file. */
               (void)fclose(fp);
2305
               (void)remove(file_name);
2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318
               return 0;
            }
         }

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

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

2319 2320 2321 2322
   else if (image != NULL)
      return png_image_error(image,
         "png_image_write_to_file: incorrect PNG_IMAGE_VERSION");

2323 2324 2325 2326 2327
   else
      return 0;
}
#endif /* PNG_STDIO_SUPPORTED */
#endif /* SIMPLIFIED_WRITE */
2328
#endif /* PNG_WRITE_SUPPORTED */