pngwrite.c 70.1 KB
Newer Older
1

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

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

19 20
#ifdef PNG_WRITE_SUPPORTED

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

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

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

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

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

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

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

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

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

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

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

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

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

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

136 137
   png_write_info_before_PLTE(png_ptr, info_ptr);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

   png_ptr->mode |= PNG_AFTER_IDAT;

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

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

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

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

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

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

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

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

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

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

482

483 484 485 486 487
/* Write a few rows of image data.  If the image is interlaced,
 * either you will have to write the 7 sub images, or, if you
 * have called png_set_interlace_handling(), you will have to
 * "write" the image seven times.
 */
488
void PNGAPI
489
png_write_rows(png_structrp png_ptr, png_bytepp row,
490
    png_uint_32 num_rows)
G
Guy Schalnat 已提交
491 492
{
   png_uint_32 i; /* row counter */
G
Guy Schalnat 已提交
493
   png_bytepp rp; /* row pointer */
G
Guy Schalnat 已提交
494

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

   if (png_ptr == NULL)
      return;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

670
   /* Set up row info for transformations */
671 672 673 674 675 676 677 678 679 680 681 682 683
   row_info.color_type = png_ptr->color_type;
   row_info.width = png_ptr->usr_width;
   row_info.channels = png_ptr->usr_channels;
   row_info.bit_depth = png_ptr->usr_bit_depth;
   row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels);
   row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width);

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

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

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

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

709 710 711 712 713 714 715
   /* At this point the row_info pixel depth must match the 'transformed' depth,
    * which is also the output depth.
    */
   if (row_info.pixel_depth != png_ptr->pixel_depth ||
      row_info.pixel_depth != png_ptr->transformed_pixel_depth)
      png_error(png_ptr, "internal write transform logic error");

716
#ifdef PNG_MNG_FEATURES_SUPPORTED
717 718 719 720 721 722 723 724 725
   /* Write filter_method 64 (intrapixel differencing) only if
    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
    * 2. Libpng did not write a PNG signature (this filter_method is only
    *    used in PNG datastreams that are embedded in MNG datastreams) and
    * 3. The application called png_permit_mng_features with a mask that
    *    included PNG_FLAG_MNG_FILTER_64 and
    * 4. The filter_method is 64 and
    * 5. The color_type is RGB or RGBA
    */
726
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
727
       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
728 729
   {
      /* Intrapixel differencing */
730
      png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);
731 732 733
   }
#endif

734 735
/* Added at libpng-1.5.10 */
#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
736
   /* Check for out-of-range palette index */
737
#if 0 /* To do: implement png_do_check_palette_indexes() */
738 739 740
   if (png_ptr->num_palette < (1 << png_ptr->bit_depth))
      png_do_check_palette_indexes(&row_info, png_ptr->row_buf + 1,
        png_ptr->num_palette_max);
741 742
#endif
   if (png_ptr->num_palette_max > png_ptr->num_palette + 1)
743 744 745
      png_warning(png_ptr, "Palette index exceeded num_palette");
#endif

A
Andreas Dilger 已提交
746
   /* Find a filter if necessary, filter the row and write it out. */
747
   png_write_find_filter(png_ptr, &row_info);
748 749 750

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

753
#ifdef PNG_WRITE_FLUSH_SUPPORTED
G
Guy Schalnat 已提交
754
/* Set the automatic flush interval or 0 to turn flushing off */
755
void PNGAPI
756
png_set_flush(png_structrp png_ptr, int nrows)
G
Guy Schalnat 已提交
757
{
758
   png_debug(1, "in png_set_flush");
759

760 761
   if (png_ptr == NULL)
      return;
762

G
Guy Schalnat 已提交
763
   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
G
Guy Schalnat 已提交
764 765
}

766
/* Flush the current output buffers now */
767
void PNGAPI
768
png_write_flush(png_structrp png_ptr)
G
Guy Schalnat 已提交
769
{
G
Guy Schalnat 已提交
770 771
   int wrote_IDAT;

772
   png_debug(1, "in png_write_flush");
773

774 775
   if (png_ptr == NULL)
      return;
776

G
Guy Schalnat 已提交
777 778
   /* We have already written out all of the data */
   if (png_ptr->row_number >= png_ptr->num_rows)
779
      return;
G
Guy Schalnat 已提交
780 781 782 783 784

   do
   {
      int ret;

785
      /* Compress the data */
A
Andreas Dilger 已提交
786
      ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
G
Guy Schalnat 已提交
787 788
      wrote_IDAT = 0;

789
      /* Check for compression errors */
G
Guy Schalnat 已提交
790 791
      if (ret != Z_OK)
      {
A
Andreas Dilger 已提交
792
         if (png_ptr->zstream.msg != NULL)
A
Andreas Dilger 已提交
793
            png_error(png_ptr, png_ptr->zstream.msg);
794

G
Guy Schalnat 已提交
795 796 797 798
         else
            png_error(png_ptr, "zlib error");
      }

A
Andreas Dilger 已提交
799
      if (!(png_ptr->zstream.avail_out))
G
Guy Schalnat 已提交
800
      {
801
         /* Write the IDAT and reset the zlib output buffer */
802
         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
G
Guy Schalnat 已提交
803 804
         wrote_IDAT = 1;
      }
805
   } while (wrote_IDAT == 1);
G
Guy Schalnat 已提交
806 807

   /* If there is any data left to be output, write it into a new IDAT */
A
Andreas Dilger 已提交
808
   if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
G
Guy Schalnat 已提交
809
   {
810
      /* Write the IDAT and reset the zlib output buffer */
G
Guy Schalnat 已提交
811
      png_write_IDAT(png_ptr, png_ptr->zbuf,
812
          png_ptr->zbuf_size - png_ptr->zstream.avail_out);
G
Guy Schalnat 已提交
813 814 815
   }
   png_ptr->flush_rows = 0;
   png_flush(png_ptr);
G
Guy Schalnat 已提交
816
}
G
Guy Schalnat 已提交
817
#endif /* PNG_WRITE_FLUSH_SUPPORTED */
G
Guy Schalnat 已提交
818

819
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
820
static void png_reset_filter_heuristics(png_structrp png_ptr); /* forward decl */
821
#endif
G
Guy Schalnat 已提交
822

823 824
/* Free any memory used in png_ptr struct without freeing the struct itself. */
static void
825
png_write_destroy(png_structrp png_ptr)
G
Guy Schalnat 已提交
826
{
827
   png_debug(1, "in png_write_destroy");
828 829

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

833
   /* Free our memory.  png_free checks NULL for us. */
A
Andreas Dilger 已提交
834 835
   png_free(png_ptr, png_ptr->zbuf);
   png_free(png_ptr, png_ptr->row_buf);
836
#ifdef PNG_WRITE_FILTER_SUPPORTED
A
Andreas Dilger 已提交
837 838 839 840 841
   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);
842
#endif
843

844
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
845 846
   /* Use this to save a little code space, it doesn't free the filter_costs */
   png_reset_filter_heuristics(png_ptr);
A
Andreas Dilger 已提交
847 848
   png_free(png_ptr, png_ptr->filter_costs);
   png_free(png_ptr, png_ptr->inv_filter_costs);
849
#endif
G
Guy Schalnat 已提交
850

851 852
#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
   png_free(png_ptr, png_ptr->chunk_list);
853
#endif
G
Guy Schalnat 已提交
854

855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
   /* 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 已提交
872

873 874
   if (png_ptr_ptr != NULL)
   {
875
      png_structrp png_ptr = *png_ptr_ptr;
G
Guy Schalnat 已提交
876

877 878 879
      if (png_ptr != NULL) /* added in libpng 1.6.0 */
      {
         png_destroy_info_struct(png_ptr, info_ptr_ptr);
G
Guy Schalnat 已提交
880

881 882 883 884 885
         *png_ptr_ptr = NULL;
         png_write_destroy(png_ptr);
         png_destroy_png_struct(png_ptr);
      }
   }
G
Guy Schalnat 已提交
886
}
G
Guy Schalnat 已提交
887

A
Andreas Dilger 已提交
888
/* Allow the application to select one or more row filters to use. */
889
void PNGAPI
890
png_set_filter(png_structrp png_ptr, int method, int filters)
G
Guy Schalnat 已提交
891
{
892
   png_debug(1, "in png_set_filter");
893

894 895
   if (png_ptr == NULL)
      return;
896

897
#ifdef PNG_MNG_FEATURES_SUPPORTED
898
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
899 900
       (method == PNG_INTRAPIXEL_DIFFERENCING))
      method = PNG_FILTER_TYPE_BASE;
901

902
#endif
A
Andreas Dilger 已提交
903
   if (method == PNG_FILTER_TYPE_BASE)
G
Guy Schalnat 已提交
904 905 906
   {
      switch (filters & (PNG_ALL_FILTERS | 0x07))
      {
907
#ifdef PNG_WRITE_FILTER_SUPPORTED
G
Guy Schalnat 已提交
908 909
         case 5:
         case 6:
A
Andreas Dilger 已提交
910
         case 7: png_warning(png_ptr, "Unknown row filter for method 0");
911
#endif /* PNG_WRITE_FILTER_SUPPORTED */
912
         case PNG_FILTER_VALUE_NONE:
913
            png_ptr->do_filter = PNG_FILTER_NONE; break;
914

915
#ifdef PNG_WRITE_FILTER_SUPPORTED
916
         case PNG_FILTER_VALUE_SUB:
917
            png_ptr->do_filter = PNG_FILTER_SUB; break;
918

919
         case PNG_FILTER_VALUE_UP:
920
            png_ptr->do_filter = PNG_FILTER_UP; break;
921

922
         case PNG_FILTER_VALUE_AVG:
923
            png_ptr->do_filter = PNG_FILTER_AVG; break;
924

925
         case PNG_FILTER_VALUE_PAETH:
926
            png_ptr->do_filter = PNG_FILTER_PAETH; break;
927

928 929
         default:
            png_ptr->do_filter = (png_byte)filters; break;
930
#else
931 932
         default:
            png_warning(png_ptr, "Unknown row filter for method 0");
933
#endif /* PNG_WRITE_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
934 935
      }

A
Andreas Dilger 已提交
936 937 938 939 940 941 942 943
      /* 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 已提交
944
       */
A
Andreas Dilger 已提交
945
      if (png_ptr->row_buf != NULL)
G
Guy Schalnat 已提交
946
      {
947
#ifdef PNG_WRITE_FILTER_SUPPORTED
948
         if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)
G
Guy Schalnat 已提交
949
         {
A
Andreas Dilger 已提交
950
            png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
951
                (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
952
            png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
G
Guy Schalnat 已提交
953 954
         }

955
         if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)
G
Guy Schalnat 已提交
956
         {
A
Andreas Dilger 已提交
957
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
958
            {
A
Andreas Dilger 已提交
959
               png_warning(png_ptr, "Can't add Up filter after starting");
960 961
               png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
                   ~PNG_FILTER_UP);
G
Guy Schalnat 已提交
962
            }
963

G
Guy Schalnat 已提交
964 965
            else
            {
A
Andreas Dilger 已提交
966
               png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
967
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
968
               png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
G
Guy Schalnat 已提交
969 970 971
            }
         }

972
         if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)
G
Guy Schalnat 已提交
973
         {
A
Andreas Dilger 已提交
974
            if (png_ptr->prev_row == NULL)
G
Guy Schalnat 已提交
975
            {
A
Andreas Dilger 已提交
976
               png_warning(png_ptr, "Can't add Average filter after starting");
977 978
               png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
                   ~PNG_FILTER_AVG);
G
Guy Schalnat 已提交
979
            }
980

G
Guy Schalnat 已提交
981 982
            else
            {
A
Andreas Dilger 已提交
983
               png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
984
                   (png_ptr->rowbytes + 1));
A
Andreas Dilger 已提交
985
               png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
G
Guy Schalnat 已提交
986 987 988
            }
         }

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

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

         if (png_ptr->do_filter == PNG_NO_FILTERS)
1007
#endif /* PNG_WRITE_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
1008 1009 1010 1011
            png_ptr->do_filter = PNG_FILTER_NONE;
      }
   }
   else
A
Andreas Dilger 已提交
1012 1013 1014 1015 1016 1017 1018 1019
      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
1020 1021
 * better compression.
 */
1022
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED      /* GRR 970116 */
1023
/* Convenience reset API. */
1024
static void
1025
png_reset_filter_heuristics(png_structrp png_ptr)
A
Andreas Dilger 已提交
1026
{
1027 1028 1029 1030 1031 1032 1033 1034
   /* 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 已提交
1035
   {
1036 1037 1038
      png_bytep old = png_ptr->prev_filters;
      png_ptr->prev_filters = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1039
   }
1040
   if (png_ptr->filter_weights != NULL)
A
Andreas Dilger 已提交
1041
   {
1042 1043 1044
      png_uint_16p old = png_ptr->filter_weights;
      png_ptr->filter_weights = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1045 1046
   }

1047
   if (png_ptr->inv_filter_weights != NULL)
A
Andreas Dilger 已提交
1048
   {
1049 1050 1051
      png_uint_16p old = png_ptr->inv_filter_weights;
      png_ptr->inv_filter_weights = NULL;
      png_free(png_ptr, old);
A
Andreas Dilger 已提交
1052 1053
   }

1054 1055 1056 1057
   /* Leave the filter_costs - this array is fixed size. */
}

static int
1058
png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method,
1059 1060 1061 1062 1063 1064 1065
   int num_weights)
{
   if (png_ptr == NULL)
      return 0;

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

1067 1068 1069 1070 1071
   /* 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 已提交
1072
   {
1073 1074 1075
      int i;

      if (num_weights > 0)
A
Andreas Dilger 已提交
1076 1077
      {
         png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
1078
             (png_uint_32)(png_sizeof(png_byte) * num_weights));
A
Andreas Dilger 已提交
1079

1080 1081
         /* To make sure that the weighting starts out fairly */
         for (i = 0; i < num_weights; i++)
A
Andreas Dilger 已提交
1082
         {
1083
            png_ptr->prev_filters[i] = 255;
A
Andreas Dilger 已提交
1084 1085
         }

1086 1087
         png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
A
Andreas Dilger 已提交
1088

1089 1090
         png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
1091

1092 1093 1094 1095 1096
         for (i = 0; i < num_weights; i++)
         {
            png_ptr->inv_filter_weights[i] =
            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
         }
1097

1098 1099
         /* Safe to set this now */
         png_ptr->num_prev_filters = (png_byte)num_weights;
A
Andreas Dilger 已提交
1100 1101
      }

1102 1103 1104 1105
      /* 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 已提交
1106
      {
1107 1108
         png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
1109

1110 1111
         png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
             (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
1112
      }
1113

1114 1115
      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
      {
1116 1117
         png_ptr->inv_filter_costs[i] =
         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
A
Andreas Dilger 已提交
1118
      }
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134

      /* 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 已提交
1135
   }
1136
}
A
Andreas Dilger 已提交
1137

1138 1139 1140
/* Provide floating and fixed point APIs */
#ifdef PNG_FLOATING_POINT_SUPPORTED
void PNGAPI
1141
png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method,
1142 1143
    int num_weights, png_const_doublep filter_weights,
    png_const_doublep filter_costs)
1144 1145 1146 1147 1148
{
   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 已提交
1149
    */
1150 1151
   if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
      return;
A
Andreas Dilger 已提交
1152

1153 1154 1155 1156 1157 1158
   /* 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++)
      {
1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
         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);
         }
1173
      }
A
Andreas Dilger 已提交
1174

1175 1176 1177 1178 1179 1180 1181 1182
      /* 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 已提交
1183
      {
1184 1185
         png_ptr->inv_filter_costs[i] =
             (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5);
1186

1187 1188
         png_ptr->filter_costs[i] =
             (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5);
A
Andreas Dilger 已提交
1189 1190
      }
   }
1191 1192
}
#endif /* FLOATING_POINT */
A
Andreas Dilger 已提交
1193

1194 1195
#ifdef PNG_FIXED_POINT_SUPPORTED
void PNGAPI
1196
png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method,
1197 1198
    int num_weights, png_const_fixed_point_p filter_weights,
    png_const_fixed_point_p filter_costs)
1199 1200 1201 1202 1203
{
   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 已提交
1204
    */
1205 1206 1207 1208 1209
   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 已提交
1210
   {
1211 1212
      int i;
      for (i = 0; i < num_weights; i++)
A
Andreas Dilger 已提交
1213
      {
1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227
         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 已提交
1228
      }
1229

1230 1231 1232 1233 1234 1235 1236 1237 1238
      /* 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 已提交
1239
      {
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
         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;
1253

1254
         png_ptr->filter_costs[i] = (png_uint_16)tmp;
A
Andreas Dilger 已提交
1255 1256
      }
   }
G
Guy Schalnat 已提交
1257
}
1258
#endif /* FIXED_POINT */
A
Andreas Dilger 已提交
1259
#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
G
Guy Schalnat 已提交
1260

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

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

G
Guy Schalnat 已提交
1269
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
G
Guy Schalnat 已提交
1270 1271 1272
   png_ptr->zlib_level = level;
}

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

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

G
Guy Schalnat 已提交
1281
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
G
Guy Schalnat 已提交
1282
   png_ptr->zlib_mem_level = mem_level;
G
Guy Schalnat 已提交
1283 1284
}

1285
void PNGAPI
1286
png_set_compression_strategy(png_structrp png_ptr, int strategy)
G
Guy Schalnat 已提交
1287
{
1288
   png_debug(1, "in png_set_compression_strategy");
1289

1290 1291
   if (png_ptr == NULL)
      return;
1292

G
Guy Schalnat 已提交
1293
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
G
Guy Schalnat 已提交
1294 1295 1296
   png_ptr->zlib_strategy = strategy;
}

1297 1298 1299
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
 * smaller value of window_bits if it can do so safely.
 */
1300
void PNGAPI
1301
png_set_compression_window_bits(png_structrp png_ptr, int window_bits)
G
Guy Schalnat 已提交
1302
{
1303 1304
   if (png_ptr == NULL)
      return;
1305

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

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

1312
#ifndef WBITS_8_OK
1313
   /* Avoid libpng bug with 256-byte windows */
1314
   if (window_bits == 8)
1315 1316 1317 1318
      {
        png_warning(png_ptr, "Compression window is being reset to 512");
        window_bits = 9;
      }
1319

1320
#endif
G
Guy Schalnat 已提交
1321
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
G
Guy Schalnat 已提交
1322 1323 1324
   png_ptr->zlib_window_bits = window_bits;
}

1325
void PNGAPI
1326
png_set_compression_method(png_structrp png_ptr, int method)
G
Guy Schalnat 已提交
1327
{
1328
   png_debug(1, "in png_set_compression_method");
1329

1330 1331
   if (png_ptr == NULL)
      return;
1332

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

G
Guy Schalnat 已提交
1336
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
G
Guy Schalnat 已提交
1337
   png_ptr->zlib_method = method;
G
Guy Schalnat 已提交
1338 1339
}

1340
/* The following were added to libpng-1.5.4 */
1341
#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
1342
void PNGAPI
1343
png_set_text_compression_level(png_structrp png_ptr, int level)
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
{
   png_debug(1, "in png_set_text_compression_level");

   if (png_ptr == NULL)
      return;

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

void PNGAPI
1355
png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)
1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
{
   png_debug(1, "in png_set_text_compression_mem_level");

   if (png_ptr == NULL)
      return;

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

void PNGAPI
1367
png_set_text_compression_strategy(png_structrp png_ptr, int strategy)
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377
{
   png_debug(1, "in png_set_text_compression_strategy");

   if (png_ptr == NULL)
      return;

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

1378 1379 1380
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
 * smaller value of window_bits if it can do so safely.
 */
1381
void PNGAPI
1382
png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits)
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
{
   if (png_ptr == NULL)
      return;

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

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

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

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

void PNGAPI
1407
png_set_text_compression_method(png_structrp png_ptr, int method)
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419
{
   png_debug(1, "in png_set_text_compression_method");

   if (png_ptr == NULL)
      return;

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

   png_ptr->flags |= PNG_FLAG_ZTXT_CUSTOM_METHOD;
   png_ptr->zlib_text_method = method;
}
1420
#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
1421
/* end of API added to libpng-1.5.4 */
1422

1423
void PNGAPI
1424
png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)
1425
{
1426 1427
   if (png_ptr == NULL)
      return;
1428

1429 1430 1431
   png_ptr->write_row_fn = write_row_fn;
}

1432
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
1433
void PNGAPI
1434
png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
1435
    write_user_transform_fn)
1436
{
1437
   png_debug(1, "in png_set_write_user_transform_fn");
1438

1439 1440
   if (png_ptr == NULL)
      return;
1441

1442 1443 1444 1445
   png_ptr->transformations |= PNG_USER_TRANSFORM;
   png_ptr->write_user_transform_fn = write_user_transform_fn;
}
#endif
1446 1447


1448
#ifdef PNG_INFO_IMAGE_SUPPORTED
1449
void PNGAPI
1450
png_write_png(png_structrp png_ptr, png_inforp info_ptr,
1451
    int transforms, voidp params)
1452
{
1453 1454
   if (png_ptr == NULL || info_ptr == NULL)
      return;
1455 1456 1457 1458 1459 1460

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

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

1461
#ifdef PNG_WRITE_INVERT_SUPPORTED
1462
   /* Invert monochrome pixels */
1463
   if (transforms & PNG_TRANSFORM_INVERT_MONO)
1464
      png_set_invert_mono(png_ptr);
1465 1466
#endif

1467
#ifdef PNG_WRITE_SHIFT_SUPPORTED
1468 1469 1470 1471
   /* Shift the pixels up to a legal bit depth and fill in
    * as appropriate to correctly scale the image.
    */
   if ((transforms & PNG_TRANSFORM_SHIFT)
1472
       && (info_ptr->valid & PNG_INFO_sBIT))
1473
      png_set_shift(png_ptr, &info_ptr->sig_bit);
1474 1475
#endif

1476
#ifdef PNG_WRITE_PACK_SUPPORTED
1477
   /* Pack pixels into bytes */
1478 1479 1480 1481
   if (transforms & PNG_TRANSFORM_PACKING)
       png_set_packing(png_ptr);
#endif

1482
#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
1483
   /* Swap location of alpha bytes from ARGB to RGBA */
1484
   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
1485
      png_set_swap_alpha(png_ptr);
1486 1487
#endif

1488
#ifdef PNG_WRITE_FILLER_SUPPORTED
1489
   /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */
1490
   if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)
1491
      png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
1492

1493
   else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)
1494
      png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
1495 1496
#endif

1497
#ifdef PNG_WRITE_BGR_SUPPORTED
1498
   /* Flip BGR pixels to RGB */
1499
   if (transforms & PNG_TRANSFORM_BGR)
1500
      png_set_bgr(png_ptr);
1501 1502
#endif

1503
#ifdef PNG_WRITE_SWAP_SUPPORTED
1504
   /* Swap bytes of 16-bit files to most significant byte first */
1505
   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
1506
      png_set_swap(png_ptr);
1507 1508
#endif

1509
#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
1510
   /* Swap bits of 1, 2, 4 bit packed pixel formats */
1511
   if (transforms & PNG_TRANSFORM_PACKSWAP)
1512
      png_set_packswap(png_ptr);
1513 1514
#endif

1515
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
1516 1517 1518 1519 1520
   /* Invert the alpha channel from opacity to transparency */
   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
      png_set_invert_alpha(png_ptr);
#endif

1521 1522
   /* ----------------------- end of transformations ------------------- */

1523
   /* Write the bits */
1524 1525 1526 1527 1528
   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);
1529

1530 1531
   PNG_UNUSED(transforms)   /* Quiet compiler warnings */
   PNG_UNUSED(params)
1532 1533
}
#endif
1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550


#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)
      {
1551 1552
         png_controlp control = png_voidcast(png_controlp,
            png_malloc_warn(png_ptr, sizeof *control));
1553 1554 1555

         if (control != NULL)
         {
1556
            png_memset(control, 0, sizeof *control);
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582

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

            image->opaque = control;
            return 1;
         }

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

      png_destroy_write_struct(&png_ptr, NULL);
   }

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

/* Arguments to png_image_write_main: */
typedef struct
{
   /* Arguments: */
   png_imagep      image;
   png_const_voidp buffer;
   png_int_32      row_stride;
1583
   png_const_voidp colormap;
1584 1585 1586 1587 1588 1589 1590
   int             convert_to_8bit;
   /* Local variables: */
   png_const_voidp first_row;
   ptrdiff_t       row_bytes;
   png_voidp       local_row;
} png_image_write_control;

1591 1592
/* 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
1593 1594 1595 1596 1597
 * png_image format value.
 */
static int
png_write_image_16bit(png_voidp argument)
{
1598 1599
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1600
   png_imagep image = display->image;
1601
   png_structrp png_ptr = image->opaque->png_ptr;
1602

1603 1604 1605
   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);
1606
   png_uint_16p row_end;
1607
   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;
1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628
   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
1629
    * row; this is correct.
1630 1631 1632 1633 1634 1635 1636 1637 1638 1639
    */
   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)
      {
1640
         const png_uint_16 alpha = in_ptr[aindex];
1641 1642 1643 1644 1645 1646
         png_uint_32 reciprocal = 0;
         int c;

         out_ptr[aindex] = alpha;

         /* Calculate a reciprocal.  The correct calculation is simply
1647
          * component/alpha*65535 << 15. (I.e. 15 bits of precision); this
1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687
          * 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;
      }

1688
      png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));
1689 1690 1691 1692 1693 1694 1695 1696 1697
      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.
1698 1699 1700 1701
 *
 * 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.
1702
 */
1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749
#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;
}

1750 1751 1752
static int
png_write_image_8bit(png_voidp argument)
{
1753 1754
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1755
   png_imagep image = display->image;
1756
   png_structrp png_ptr = image->opaque->png_ptr;
1757

1758 1759 1760
   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);
1761
   png_uint_32 y = image->height;
1762
   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1;
1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786

   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;

1787
         while (out_ptr < row_end)
1788 1789
         {
            png_uint_16 alpha = in_ptr[aindex];
1790
            png_byte alphabyte = (png_byte)PNG_DIV257(alpha);
1791 1792 1793
            png_uint_32 reciprocal = 0;
            int c;

1794 1795
            /* Scale and write the alpha channel. */
            out_ptr[aindex] = alphabyte;
1796

1797 1798
            if (alphabyte > 0 && alphabyte < 255)
               reciprocal = UNP_RECIPROCAL(alpha);
1799 1800 1801

            c = channels;
            do /* always at least one channel */
1802
               *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal);
1803 1804 1805 1806 1807 1808
            while (--c > 0);

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

1810 1811
         png_write_row(png_ptr, png_voidcast(png_const_bytep,
            display->local_row));
1812
         input_row += display->row_bytes/(sizeof (png_uint_16));
1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843
      } /* 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;
}

1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988
static void
png_image_set_PLTE(png_image_write_control *display)
{
   const png_imagep image = display->image;
   const void *cmap = display->colormap;
   const int entries = image->colormap_entries > 256 ? 256 :
      (int)image->colormap_entries;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            default:
               break;
         }
      }
   }

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

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

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

   image->colormap_entries = entries;
}

1989 1990 1991
static int
png_image_write_main(png_voidp argument)
{
1992 1993
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
1994
   png_imagep image = display->image;
1995 1996
   png_structrp png_ptr = image->opaque->png_ptr;
   png_inforp info_ptr = image->opaque->info_ptr;
1997 1998
   png_uint_32 format = image->format;

1999 2000 2001 2002
   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;
2003

2004 2005 2006 2007
   /* Default the 'row_stride' parameter if required. */
   if (display->row_stride == 0)
      display->row_stride = PNG_IMAGE_ROW_STRIDE(*image);

2008
   /* Set the required transforms then write the rows in the correct order. */
2009
   if (format & PNG_FORMAT_FLAG_COLORMAP)
2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026
   {
      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");
   }
2027 2028 2029 2030 2031 2032 2033

   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);
2034 2035 2036 2037 2038 2039

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

2041 2042 2043 2044
   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);
2045 2046 2047 2048 2049 2050 2051 2052 2053

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

2056
   else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB))
2057 2058
      png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);

2059 2060 2061 2062 2063 2064
   /* 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);

2065 2066 2067 2068 2069
   /* 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.
2070 2071
    *
    * First check for a little endian system if writing 16 bit files.
2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083
    */
   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)
      {
2084
         if (!colormap && (format & PNG_FORMAT_FLAG_COLOR) != 0)
2085
            png_set_bgr(png_ptr);
2086 2087 2088 2089 2090 2091 2092
         format &= ~PNG_FORMAT_FLAG_BGR;
      }
#  endif

#  ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
      if (format & PNG_FORMAT_FLAG_AFIRST)
      {
2093
         if (!colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0)
2094
            png_set_swap_alpha(png_ptr);
2095 2096 2097 2098
         format &= ~PNG_FORMAT_FLAG_AFIRST;
      }
#  endif

2099 2100 2101 2102 2103 2104
   /* 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);

2105
   /* That should have handled all (both) the transforms. */
2106
   if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR |
2107
         PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0)
2108 2109 2110
      png_error(png_ptr, "png_write_image: unsupported transformation");

   {
2111
      png_const_bytep row = png_voidcast(png_const_bytep, display->buffer);
2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123
      ptrdiff_t row_bytes = display->row_stride;

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

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

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

2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135
   /* 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);
   }

2136 2137 2138 2139
   /* 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.
    */
2140
   if ((linear && alpha) || (!colormap && display->convert_to_8bit))
2141
   {
2142 2143
      png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
         png_get_rowbytes(png_ptr, info_ptr)));
2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164
      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
   {
2165
      png_const_bytep row = png_voidcast(png_const_bytep, display->first_row);
2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180
      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
2181
png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
2182
   const void *buffer, png_int_32 row_stride, const void *colormap)
2183 2184
{
   /* Write the image to the given (FILE*). */
2185
   if (image != NULL || image->version != PNG_IMAGE_VERSION)
2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199
   {
      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;

2200
            png_memset(&display, 0, sizeof display);
2201 2202 2203
            display.image = image;
            display.buffer = buffer;
            display.row_stride = row_stride;
2204
            display.colormap = colormap;
2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225
            display.convert_to_8bit = convert_to_8bit;

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

         else
            return 0;
      }

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

   else
      return 0;
}

int PNGAPI
2226
png_image_write_to_file(png_imagep image, const char *file_name,
2227 2228
   int convert_to_8bit, const void *buffer, png_int_32 row_stride,
   const void *colormap)
2229 2230
{
   /* Write the image to the named file. */
2231
   if (image != NULL || image->version != PNG_IMAGE_VERSION)
2232 2233 2234 2235 2236 2237 2238
   {
      if (file_name != NULL)
      {
         FILE *fp = fopen(file_name, "wb");

         if (fp != NULL)
         {
2239
            if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,
2240
               row_stride, colormap))
2241
            {
2242
               int error; /* from fflush/fclose */
2243

2244 2245 2246 2247 2248
               /* Make sure the file is flushed correctly. */
               if (fflush(fp) == 0 && ferror(fp) == 0)
               {
                  if (fclose(fp) == 0)
                     return 1;
2249

2250 2251
                  error = errno; /* from fclose */
               }
2252

2253
               else
2254
               {
2255 2256
                  error = errno; /* from fflush or ferror */
                  (void)fclose(fp);
2257 2258
               }

2259 2260 2261 2262 2263
               (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));
2264 2265 2266 2267 2268 2269
            }

            else
            {
               /* Clean up: just the opened file. */
               (void)fclose(fp);
2270
               (void)remove(file_name);
2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288
               return 0;
            }
         }

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

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

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