pngwrite.c 72.4 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 [February 14, 2013]
5
 * Copyright (c) 1998-2013 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
void PNGAPI
451
png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime)
G
Guy Schalnat 已提交
452
{
453
   png_debug(1, "in png_convert_from_struct_tm");
454

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

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

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

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

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

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

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

503 504 505 506 507 508
   /* The 'zlib_strategy' setting is irrelevant because png_default_claim in
    * pngwutil.c defaults it according to whether or not filters will be used,
    * and ignores this setting.
    */
   png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY;
   png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION;
509 510 511 512 513
   png_ptr->zlib_mem_level = 8;
   png_ptr->zlib_window_bits = 15;
   png_ptr->zlib_method = 8;

#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
514 515
   png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY;
   png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION;
516 517 518 519 520
   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 */

521 522 523 524 525 526 527
   /* 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;
528 529 530 531 532 533 534 535 536 537
      /* 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;
538 539
#  endif

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

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

552

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

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

   if (png_ptr == NULL)
      return;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

826 827
   if (png_ptr == NULL)
      return;
828

G
Guy Schalnat 已提交
829
   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
G
Guy Schalnat 已提交
830 831
}

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

838 839
   if (png_ptr == NULL)
      return;
840

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

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

851
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
852
static void png_reset_filter_heuristics(png_structrp png_ptr);/* forward decl */
853
#endif
G
Guy Schalnat 已提交
854

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

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

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

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

883
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
884
   png_free(png_ptr, png_ptr->chunk_list);
885
#endif
G
Guy Schalnat 已提交
886

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

905 906
   if (png_ptr_ptr != NULL)
   {
907
      png_structrp png_ptr = *png_ptr_ptr;
G
Guy Schalnat 已提交
908

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

913 914 915 916 917
         *png_ptr_ptr = NULL;
         png_write_destroy(png_ptr);
         png_destroy_png_struct(png_ptr);
      }
   }
G
Guy Schalnat 已提交
918
}
G
Guy Schalnat 已提交
919

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

926 927
   if (png_ptr == NULL)
      return;
928

929
#ifdef PNG_MNG_FEATURES_SUPPORTED
930
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
931 932
       (method == PNG_INTRAPIXEL_DIFFERENCING))
      method = PNG_FILTER_TYPE_BASE;
933

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

948
#ifdef PNG_WRITE_FILTER_SUPPORTED
949
         case PNG_FILTER_VALUE_SUB:
950
            png_ptr->do_filter = PNG_FILTER_SUB; break;
951

952
         case PNG_FILTER_VALUE_UP:
953
            png_ptr->do_filter = PNG_FILTER_UP; break;
954

955
         case PNG_FILTER_VALUE_AVG:
956
            png_ptr->do_filter = PNG_FILTER_AVG; break;
957

958
         case PNG_FILTER_VALUE_PAETH:
959
            png_ptr->do_filter = PNG_FILTER_PAETH; break;
960

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

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

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

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

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

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

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

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

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

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

1087 1088 1089 1090
   /* Leave the filter_costs - this array is fixed size. */
}

static int
1091
png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method,
1092 1093 1094 1095 1096 1097 1098
   int num_weights)
{
   if (png_ptr == NULL)
      return 0;

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

1100 1101 1102 1103 1104
   /* 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 已提交
1105
   {
1106 1107 1108
      int i;

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

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

1119
         png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
1120
             (png_uint_32)((sizeof (png_uint_16)) * num_weights));
A
Andreas Dilger 已提交
1121

1122
         png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
1123
             (png_uint_32)((sizeof (png_uint_16)) * num_weights));
1124

1125 1126 1127 1128 1129
         for (i = 0; i < num_weights; i++)
         {
            png_ptr->inv_filter_weights[i] =
            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
         }
1130

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

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

1143
         png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
1144
             (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST));
1145
      }
1146

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

      /* 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 已提交
1168
   }
1169
}
A
Andreas Dilger 已提交
1170

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

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

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

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

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

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

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

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

1299 1300
   if (png_ptr == NULL)
      return;
1301

G
Guy Schalnat 已提交
1302 1303 1304
   png_ptr->zlib_level = level;
}

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

1310 1311
   if (png_ptr == NULL)
      return;
1312

G
Guy Schalnat 已提交
1313
   png_ptr->zlib_mem_level = mem_level;
G
Guy Schalnat 已提交
1314 1315
}

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

1321 1322
   if (png_ptr == NULL)
      return;
1323

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

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

1339 1340 1341
   /* 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
1342
    * compressed IDAT or ancillary chunks.  Such files can be read and there is
1343 1344
    * no warning on read, so this seems like a very bad idea.
    */
G
Guy Schalnat 已提交
1345
   if (window_bits > 15)
1346
   {
G
Guy Schalnat 已提交
1347
      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
1348 1349
      window_bits = 15;
   }
1350

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

G
Guy Schalnat 已提交
1357 1358 1359
   png_ptr->zlib_window_bits = window_bits;
}

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

1365 1366
   if (png_ptr == NULL)
      return;
1367

1368 1369 1370
   /* 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 已提交
1371 1372
   if (method != 8)
      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
1373

G
Guy Schalnat 已提交
1374
   png_ptr->zlib_method = method;
G
Guy Schalnat 已提交
1375 1376
}

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

   if (png_ptr == NULL)
      return;

   png_ptr->zlib_text_level = level;
}

void PNGAPI
1391
png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)
1392 1393 1394 1395 1396 1397 1398 1399 1400 1401
{
   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
1402
png_set_text_compression_strategy(png_structrp png_ptr, int strategy)
1403 1404 1405 1406 1407 1408 1409 1410 1411
{
   png_debug(1, "in png_set_text_compression_strategy");

   if (png_ptr == NULL)
      return;

   png_ptr->zlib_text_strategy = strategy;
}

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

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

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

   png_ptr->zlib_text_window_bits = window_bits;
}

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

1452
void PNGAPI
1453
png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)
1454
{
1455 1456
   if (png_ptr == NULL)
      return;
1457

1458 1459 1460
   png_ptr->write_row_fn = write_row_fn;
}

1461
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
1462
void PNGAPI
1463
png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
1464
    write_user_transform_fn)
1465
{
1466
   png_debug(1, "in png_set_write_user_transform_fn");
1467

1468 1469
   if (png_ptr == NULL)
      return;
1470

1471 1472 1473 1474
   png_ptr->transformations |= PNG_USER_TRANSFORM;
   png_ptr->write_user_transform_fn = write_user_transform_fn;
}
#endif
1475 1476


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

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

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

1490
#ifdef PNG_WRITE_INVERT_SUPPORTED
1491
   /* Invert monochrome pixels */
1492
   if (transforms & PNG_TRANSFORM_INVERT_MONO)
1493
      png_set_invert_mono(png_ptr);
1494 1495
#endif

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

1505
#ifdef PNG_WRITE_PACK_SUPPORTED
1506
   /* Pack pixels into bytes */
1507 1508 1509 1510
   if (transforms & PNG_TRANSFORM_PACKING)
       png_set_packing(png_ptr);
#endif

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

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

1522
   else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)
1523
      png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
1524 1525
#endif

1526
#ifdef PNG_WRITE_BGR_SUPPORTED
1527
   /* Flip BGR pixels to RGB */
1528
   if (transforms & PNG_TRANSFORM_BGR)
1529
      png_set_bgr(png_ptr);
1530 1531
#endif

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

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

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

1550 1551
   /* ----------------------- end of transformations ------------------- */

1552
   /* Write the bits */
1553 1554 1555 1556 1557
   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);
1558

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


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

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

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

1602
   return png_image_error(image, "png_image_write_: out of memory");
1603 1604 1605 1606 1607 1608 1609 1610 1611
}

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

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

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

         out_ptr[aindex] = alpha;

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

1717
      png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));
1718 1719 1720 1721 1722 1723 1724 1725 1726
      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.
1727 1728 1729 1730
 *
 * 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.
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 1776 1777 1778
#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;
}

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

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

   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;

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

1823 1824
            /* Scale and write the alpha channel. */
            out_ptr[aindex] = alphabyte;
1825

1826 1827
            if (alphabyte > 0 && alphabyte < 255)
               reciprocal = UNP_RECIPROCAL(alpha);
1828 1829 1830

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

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

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

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

1902 1903
   memset(tRNS, 255, (sizeof tRNS));
   memset(palette, 0, (sizeof palette));
1904 1905 1906 1907 1908 1909 1910 1911 1912

   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);
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
         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);
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 2015 2016 2017
         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;
}

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

2028 2029 2030 2031
   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;
2032

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

2038 2039 2040 2041
   /* Default the 'row_stride' parameter if required. */
   if (display->row_stride == 0)
      display->row_stride = PNG_IMAGE_ROW_STRIDE(*image);

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

   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);
2068 2069 2070 2071 2072 2073

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

2075 2076 2077 2078
   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);
2079 2080 2081 2082 2083 2084 2085 2086 2087

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

2090
   else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))
2091 2092
      png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);

2093 2094 2095 2096 2097 2098
   /* 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);

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

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

2133 2134 2135 2136 2137 2138
   /* 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);

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

   {
2145
      png_const_bytep row = png_voidcast(png_const_bytep, display->buffer);
2146 2147 2148
      ptrdiff_t row_bytes = display->row_stride;

      if (linear)
2149
         row_bytes *= (sizeof (png_uint_16));
2150 2151 2152 2153 2154 2155 2156 2157

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

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

2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169
   /* 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);
   }

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

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

2255 2256 2257 2258
   else if (image != NULL)
      return png_image_error(image,
         "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");

2259 2260 2261 2262 2263
   else
      return 0;
}

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

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

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

2288 2289
                  error = errno; /* from fclose */
               }
2290

2291
               else
2292
               {
2293 2294
                  error = errno; /* from fflush or ferror */
                  (void)fclose(fp);
2295 2296
               }

2297 2298 2299 2300 2301
               (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));
2302 2303 2304 2305 2306 2307
            }

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

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

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

2322 2323 2324 2325
   else if (image != NULL)
      return png_image_error(image,
         "png_image_write_to_file: incorrect PNG_IMAGE_VERSION");

2326 2327 2328 2329 2330
   else
      return 0;
}
#endif /* PNG_STDIO_SUPPORTED */
#endif /* SIMPLIFIED_WRITE */
2331
#endif /* PNG_WRITE_SUPPORTED */