pngwrite.c 75.6 KB
Newer Older
D
duke 已提交
1 2 3 4 5
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
6
 * published by the Free Software Foundation.  Oracle designates this
D
duke 已提交
7
 * particular file as subject to the "Classpath" exception as provided
8
 * by Oracle in the LICENSE file that accompanied this code.
D
duke 已提交
9 10 11 12 13 14 15 16 17 18 19
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
20 21 22
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
D
duke 已提交
23 24 25 26 27 28 29 30 31
 */

/* pngwrite.c - general routines to write a PNG file
 *
 * This file is available under and governed by the GNU General Public
 * License version 2 only, as published by the Free Software Foundation.
 * However, the following notice accompanied the original version of this
 * file and, per its terms, should not be removed:
 *
32
 * Last changed in libpng 1.6.19 [November 12, 2015]
33
 * Copyright (c) 1998-2002,2004,2006-2015 Glenn Randers-Pehrson
D
duke 已提交
34 35
 * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
 * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
B
bae 已提交
36 37 38 39
 *
 * This code is released under the libpng license.
 * For conditions of distribution and use, see the disclaimer
 * and license in png.h
D
duke 已提交
40 41
 */

B
bae 已提交
42
#include "pngpriv.h"
43
#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
44
#  include <errno.h>
45
#endif /* SIMPLIFIED_WRITE_STDIO */
B
bae 已提交
46

D
duke 已提交
47 48
#ifdef PNG_WRITE_SUPPORTED

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
#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 != 0)
   {
      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) != 0)
      {
         /* 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
         int keep = png_handle_as_unknown(png_ptr, up->name);

         /* NOTE: this code is radically different from the read side in the
          * matter of handling an ancillary unknown chunk.  In the read side
          * 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)))
#endif
         {
            /* 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 /* WRITE_UNKNOWN_CHUNKS */

D
duke 已提交
102 103 104 105 106 107 108 109 110 111
/* 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
 * after the image data, put it in png_write_end().  I strongly encourage
 * 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.
 */
void PNGAPI
112
png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
D
duke 已提交
113
{
B
bae 已提交
114 115
   png_debug(1, "in png_write_info_before_PLTE");

D
duke 已提交
116 117
   if (png_ptr == NULL || info_ptr == NULL)
      return;
B
bae 已提交
118

119
   if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0)
D
duke 已提交
120
   {
121 122
      /* Write PNG signature */
      png_write_sig(png_ptr);
B
bae 已提交
123 124

#ifdef PNG_MNG_FEATURES_SUPPORTED
125 126 127 128 129 130 131
      if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0 && \
          png_ptr->mng_features_permitted != 0)
      {
         png_warning(png_ptr,
             "MNG features are not allowed in a PNG datastream");
         png_ptr->mng_features_permitted = 0;
      }
D
duke 已提交
132
#endif
B
bae 已提交
133

134 135 136 137
      /* Write IHDR information. */
      png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
          info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
          info_ptr->filter_type,
B
bae 已提交
138
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
139
          info_ptr->interlace_type
D
duke 已提交
140
#else
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
          0
#endif
         );

      /* The rest of these check to see if the valid field has the appropriate
       * flag set, and if it does, writes the chunk.
       *
       * 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
       * an 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.
       */
#ifdef PNG_GAMMA_SUPPORTED
#  ifdef PNG_WRITE_gAMA_SUPPORTED
      if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
          (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 &&
          (info_ptr->valid & PNG_INFO_gAMA) != 0)
         png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma);
#  endif
D
duke 已提交
166
#endif
B
bae 已提交
167

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
#ifdef PNG_COLORSPACE_SUPPORTED
      /* 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.
       */
#  ifdef PNG_WRITE_iCCP_SUPPORTED
         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
             (info_ptr->valid & PNG_INFO_iCCP) != 0)
         {
#    ifdef PNG_WRITE_sRGB_SUPPORTED
               if ((info_ptr->valid & PNG_INFO_sRGB) != 0)
                  png_app_warning(png_ptr,
                     "profile matches sRGB but writing iCCP instead");
#     endif

            png_write_iCCP(png_ptr, info_ptr->iccp_name,
               info_ptr->iccp_profile);
         }
#     ifdef PNG_WRITE_sRGB_SUPPORTED
         else
#     endif
#  endif

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

B
bae 已提交
197
#ifdef PNG_WRITE_sBIT_SUPPORTED
198 199
         if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
            png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
D
duke 已提交
200
#endif
201 202 203 204 205 206 207 208

#ifdef PNG_COLORSPACE_SUPPORTED
#  ifdef PNG_WRITE_cHRM_SUPPORTED
         if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
             (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) != 0 &&
             (info_ptr->valid & PNG_INFO_cHRM) != 0)
            png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy);
#  endif
D
duke 已提交
209
#endif
B
bae 已提交
210 211

#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
212
         write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR);
A
azvegint 已提交
213
#endif
214

D
duke 已提交
215 216 217 218 219
      png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
   }
}

void PNGAPI
220
png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)
D
duke 已提交
221 222 223 224 225
{
#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
   int i;
#endif

B
bae 已提交
226
   png_debug(1, "in png_write_info");
D
duke 已提交
227 228 229 230 231 232

   if (png_ptr == NULL || info_ptr == NULL)
      return;

   png_write_info_before_PLTE(png_ptr, info_ptr);

233
   if ((info_ptr->valid & PNG_INFO_PLTE) != 0)
D
duke 已提交
234
      png_write_PLTE(png_ptr, info_ptr->palette,
B
bae 已提交
235 236
          (png_uint_32)info_ptr->num_palette);

A
azvegint 已提交
237
   else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
D
duke 已提交
238 239
      png_error(png_ptr, "Valid palette required for paletted images");

B
bae 已提交
240
#ifdef PNG_WRITE_tRNS_SUPPORTED
241
   if ((info_ptr->valid & PNG_INFO_tRNS) !=0)
B
bae 已提交
242 243 244
   {
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
      /* Invert the alpha channel (in tRNS) */
245
      if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0 &&
B
bae 已提交
246
          info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
D
duke 已提交
247
      {
248 249 250 251 252 253 254
         int j, jend;

         jend = info_ptr->num_trans;
         if (jend > PNG_MAX_PALETTE_LENGTH)
            jend = PNG_MAX_PALETTE_LENGTH;

         for (j = 0; j<jend; ++j)
B
bae 已提交
255 256
            info_ptr->trans_alpha[j] =
               (png_byte)(255 - info_ptr->trans_alpha[j]);
D
duke 已提交
257 258
      }
#endif
B
bae 已提交
259 260 261 262 263
      png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color),
          info_ptr->num_trans, info_ptr->color_type);
   }
#endif
#ifdef PNG_WRITE_bKGD_SUPPORTED
264
   if ((info_ptr->valid & PNG_INFO_bKGD) != 0)
D
duke 已提交
265 266
      png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
#endif
B
bae 已提交
267 268

#ifdef PNG_WRITE_hIST_SUPPORTED
269
   if ((info_ptr->valid & PNG_INFO_hIST) != 0)
D
duke 已提交
270 271
      png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
#endif
B
bae 已提交
272 273

#ifdef PNG_WRITE_oFFs_SUPPORTED
274
   if ((info_ptr->valid & PNG_INFO_oFFs) != 0)
D
duke 已提交
275
      png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
B
bae 已提交
276
          info_ptr->offset_unit_type);
D
duke 已提交
277
#endif
B
bae 已提交
278 279

#ifdef PNG_WRITE_pCAL_SUPPORTED
280
   if ((info_ptr->valid & PNG_INFO_pCAL) != 0)
D
duke 已提交
281
      png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
B
bae 已提交
282 283
          info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
          info_ptr->pcal_units, info_ptr->pcal_params);
D
duke 已提交
284
#endif
B
bae 已提交
285 286

#ifdef PNG_WRITE_sCAL_SUPPORTED
287
   if ((info_ptr->valid & PNG_INFO_sCAL) != 0)
D
duke 已提交
288 289
      png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
          info_ptr->scal_s_width, info_ptr->scal_s_height);
B
bae 已提交
290 291 292
#endif /* sCAL */

#ifdef PNG_WRITE_pHYs_SUPPORTED
293
   if ((info_ptr->valid & PNG_INFO_pHYs) != 0)
D
duke 已提交
294
      png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
B
bae 已提交
295 296 297 298
          info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
#endif /* pHYs */

#ifdef PNG_WRITE_tIME_SUPPORTED
299
   if ((info_ptr->valid & PNG_INFO_tIME) != 0)
D
duke 已提交
300 301 302 303
   {
      png_write_tIME(png_ptr, &(info_ptr->mod_time));
      png_ptr->mode |= PNG_WROTE_tIME;
   }
B
bae 已提交
304 305 306
#endif /* tIME */

#ifdef PNG_WRITE_sPLT_SUPPORTED
307
   if ((info_ptr->valid & PNG_INFO_sPLT) != 0)
B
bae 已提交
308 309 310 311 312
      for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
         png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
#endif /* sPLT */

#ifdef PNG_WRITE_TEXT_SUPPORTED
D
duke 已提交
313 314 315
   /* Check to see if we need to write text chunks */
   for (i = 0; i < info_ptr->num_text; i++)
   {
B
bae 已提交
316 317 318
      png_debug2(2, "Writing header text chunk %d, type %d", i,
          info_ptr->text[i].compression);
      /* An internationalized chunk? */
D
duke 已提交
319 320
      if (info_ptr->text[i].compression > 0)
      {
B
bae 已提交
321 322 323 324 325 326 327 328
#ifdef PNG_WRITE_iTXt_SUPPORTED
         /* 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);
329 330 331 332 333
         /* Mark this chunk as written */
         if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
         else
            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
D
duke 已提交
334
#else
335
         png_warning(png_ptr, "Unable to write international text");
D
duke 已提交
336 337
#endif
      }
B
bae 已提交
338

D
duke 已提交
339 340 341
      /* If we want a compressed text chunk */
      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
      {
B
bae 已提交
342 343
#ifdef PNG_WRITE_zTXt_SUPPORTED
         /* Write compressed chunk */
D
duke 已提交
344
         png_write_zTXt(png_ptr, info_ptr->text[i].key,
345 346 347
             info_ptr->text[i].text, info_ptr->text[i].compression);
         /* Mark this chunk as written */
         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
D
duke 已提交
348 349 350 351
#else
         png_warning(png_ptr, "Unable to write compressed text");
#endif
      }
B
bae 已提交
352

D
duke 已提交
353 354
      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
      {
B
bae 已提交
355 356
#ifdef PNG_WRITE_tEXt_SUPPORTED
         /* Write uncompressed chunk */
D
duke 已提交
357
         png_write_tEXt(png_ptr, info_ptr->text[i].key,
B
bae 已提交
358 359 360 361
             info_ptr->text[i].text,
             0);
         /* Mark this chunk as written */
         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
D
duke 已提交
362
#else
B
bae 已提交
363
         /* Can't get here */
D
duke 已提交
364 365 366 367
         png_warning(png_ptr, "Unable to write uncompressed text");
#endif
      }
   }
B
bae 已提交
368 369 370
#endif /* tEXt */

#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
371
   write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE);
D
duke 已提交
372 373 374 375 376 377 378 379 380
#endif
}

/* Writes the end of the PNG file.  If you don't want to write comments or
 * 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.
 */
void PNGAPI
381
png_write_end(png_structrp png_ptr, png_inforp info_ptr)
D
duke 已提交
382
{
B
bae 已提交
383 384
   png_debug(1, "in png_write_end");

D
duke 已提交
385 386
   if (png_ptr == NULL)
      return;
B
bae 已提交
387

388
   if ((png_ptr->mode & PNG_HAVE_IDAT) == 0)
D
duke 已提交
389 390
      png_error(png_ptr, "No IDATs written into file");

391 392 393 394 395
#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
   if (png_ptr->num_palette_max > png_ptr->num_palette)
      png_benign_error(png_ptr, "Wrote palette index exceeding num_palette");
#endif

B
bae 已提交
396
   /* See if user wants us to write information chunks */
D
duke 已提交
397 398
   if (info_ptr != NULL)
   {
B
bae 已提交
399
#ifdef PNG_WRITE_TEXT_SUPPORTED
D
duke 已提交
400 401
      int i; /* local index variable */
#endif
B
bae 已提交
402 403
#ifdef PNG_WRITE_tIME_SUPPORTED
      /* Check to see if user has supplied a time chunk */
404 405
      if ((info_ptr->valid & PNG_INFO_tIME) != 0 &&
          (png_ptr->mode & PNG_WROTE_tIME) == 0)
D
duke 已提交
406
         png_write_tIME(png_ptr, &(info_ptr->mod_time));
B
bae 已提交
407

D
duke 已提交
408
#endif
B
bae 已提交
409 410
#ifdef PNG_WRITE_TEXT_SUPPORTED
      /* Loop through comment chunks */
D
duke 已提交
411 412
      for (i = 0; i < info_ptr->num_text; i++)
      {
B
bae 已提交
413
         png_debug2(2, "Writing trailer text chunk %d, type %d", i,
D
duke 已提交
414
            info_ptr->text[i].compression);
B
bae 已提交
415
         /* An internationalized chunk? */
D
duke 已提交
416 417
         if (info_ptr->text[i].compression > 0)
         {
B
bae 已提交
418 419 420 421 422 423 424 425
#ifdef PNG_WRITE_iTXt_SUPPORTED
            /* 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);
426 427 428 429 430
            /* Mark this chunk as written */
            if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
               info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
            else
               info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
D
duke 已提交
431
#else
B
bae 已提交
432
            png_warning(png_ptr, "Unable to write international text");
D
duke 已提交
433 434
#endif
         }
B
bae 已提交
435

D
duke 已提交
436 437
         else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
         {
B
bae 已提交
438 439
#ifdef PNG_WRITE_zTXt_SUPPORTED
            /* Write compressed chunk */
D
duke 已提交
440
            png_write_zTXt(png_ptr, info_ptr->text[i].key,
441 442 443
                info_ptr->text[i].text, info_ptr->text[i].compression);
            /* Mark this chunk as written */
            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
D
duke 已提交
444 445 446 447
#else
            png_warning(png_ptr, "Unable to write compressed text");
#endif
         }
B
bae 已提交
448

D
duke 已提交
449 450
         else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
         {
B
bae 已提交
451 452
#ifdef PNG_WRITE_tEXt_SUPPORTED
            /* Write uncompressed chunk */
D
duke 已提交
453
            png_write_tEXt(png_ptr, info_ptr->text[i].key,
B
bae 已提交
454
                info_ptr->text[i].text, 0);
455 456
            /* Mark this chunk as written */
            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
D
duke 已提交
457 458 459 460 461 462
#else
            png_warning(png_ptr, "Unable to write uncompressed text");
#endif
         }
      }
#endif
B
bae 已提交
463
#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
464
      write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT);
D
duke 已提交
465 466 467 468 469
#endif
   }

   png_ptr->mode |= PNG_AFTER_IDAT;

B
bae 已提交
470
   /* Write end of PNG file */
D
duke 已提交
471
   png_write_IEND(png_ptr);
472

B
bae 已提交
473 474 475 476 477 478 479 480 481
   /* 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
    * experiences a problem, please try building libpng with
    * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to
    * png-mng-implement at lists.sf.net .
    */
#ifdef PNG_WRITE_FLUSH_SUPPORTED
#  ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
D
duke 已提交
482
   png_flush(png_ptr);
B
bae 已提交
483
#  endif
D
duke 已提交
484 485 486
#endif
}

B
bae 已提交
487
#ifdef PNG_CONVERT_tIME_SUPPORTED
D
duke 已提交
488
void PNGAPI
489
png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime)
D
duke 已提交
490
{
B
bae 已提交
491 492
   png_debug(1, "in png_convert_from_struct_tm");

D
duke 已提交
493 494 495 496 497 498 499 500 501 502 503 504 505
   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;
}

void PNGAPI
png_convert_from_time_t(png_timep ptime, time_t ttime)
{
   struct tm *tbuf;

B
bae 已提交
506 507
   png_debug(1, "in png_convert_from_time_t");

D
duke 已提交
508 509 510 511 512 513
   tbuf = gmtime(&ttime);
   png_convert_from_struct_tm(ptime, tbuf);
}
#endif

/* Initialize png_ptr structure, and allocate any memory needed */
B
bae 已提交
514 515 516
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)
D
duke 已提交
517
{
518 519 520 521 522 523
#ifndef PNG_USER_MEM_SUPPORTED
   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
       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);
D
duke 已提交
524 525 526
}

/* Alternate initialize png_ptr structure, and allocate any memory needed */
B
bae 已提交
527 528 529 530
PNG_FUNCTION(png_structp,PNGAPI
png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
D
duke 已提交
531
{
532 533 534 535 536 537 538 539 540
   png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
       error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
#endif /* USER_MEM */
   if (png_ptr != NULL)
   {
      /* Set the zlib control values to defaults; they can be overridden by the
       * application after the struct has been created.
       */
      png_ptr->zbuffer_size = PNG_ZBUF_SIZE;
A
azvegint 已提交
541

542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
      /* 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;
      png_ptr->zlib_mem_level = 8;
      png_ptr->zlib_window_bits = 15;
      png_ptr->zlib_method = 8;

#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
      png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY;
      png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION;
      png_ptr->zlib_text_mem_level = 8;
      png_ptr->zlib_text_window_bits = 15;
      png_ptr->zlib_text_method = 8;
#endif /* WRITE_COMPRESSED_TEXT */

      /* 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
      /* In stable builds only warn if an application error can be completely
       * handled.
       */
      png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;
D
duke 已提交
570 571
#endif

572 573 574 575 576
      /* App warnings are warnings in release (or release candidate) builds but
       * are errors during development.
       */
#if PNG_RELEASE_BUILD
      png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN;
D
duke 已提交
577 578
#endif

579 580 581 582 583
      /* 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);
B
bae 已提交
584
   }
D
duke 已提交
585

586
   return png_ptr;
D
duke 已提交
587 588
}

B
bae 已提交
589

D
duke 已提交
590 591 592 593 594 595
/* 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.
 */
void PNGAPI
596
png_write_rows(png_structrp png_ptr, png_bytepp row,
B
bae 已提交
597
    png_uint_32 num_rows)
D
duke 已提交
598 599 600 601
{
   png_uint_32 i; /* row counter */
   png_bytepp rp; /* row pointer */

B
bae 已提交
602
   png_debug(1, "in png_write_rows");
D
duke 已提交
603 604 605 606

   if (png_ptr == NULL)
      return;

B
bae 已提交
607
   /* Loop through the rows */
D
duke 已提交
608 609 610 611 612 613 614 615 616 617
   for (i = 0, rp = row; i < num_rows; i++, rp++)
   {
      png_write_row(png_ptr, *rp);
   }
}

/* Write the image.  You only need to call this function once, even
 * if you are writing an interlaced image.
 */
void PNGAPI
618
png_write_image(png_structrp png_ptr, png_bytepp image)
D
duke 已提交
619 620 621 622 623 624 625 626
{
   png_uint_32 i; /* row index */
   int pass, num_pass; /* pass variables */
   png_bytepp rp; /* points to current row */

   if (png_ptr == NULL)
      return;

B
bae 已提交
627 628 629 630 631 632
   png_debug(1, "in png_write_image");

#ifdef PNG_WRITE_INTERLACING_SUPPORTED
   /* Initialize interlace handling.  If image is not interlaced,
    * this will set pass to 1
    */
D
duke 已提交
633 634 635 636
   num_pass = png_set_interlace_handling(png_ptr);
#else
   num_pass = 1;
#endif
B
bae 已提交
637
   /* Loop through passes */
D
duke 已提交
638 639
   for (pass = 0; pass < num_pass; pass++)
   {
B
bae 已提交
640
      /* Loop through image */
D
duke 已提交
641 642 643 644 645 646 647
      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
      {
         png_write_row(png_ptr, *rp);
      }
   }
}

648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
#ifdef PNG_MNG_FEATURES_SUPPORTED
/* Performs intrapixel differencing  */
static void
png_do_write_intrapixel(png_row_infop row_info, png_bytep row)
{
   png_debug(1, "in png_do_write_intrapixel");

   if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
   {
      int bytes_per_pixel;
      png_uint_32 row_width = row_info->width;
      if (row_info->bit_depth == 8)
      {
         png_bytep rp;
         png_uint_32 i;

         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
            bytes_per_pixel = 3;

         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
            bytes_per_pixel = 4;

         else
            return;

         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
         {
            *(rp)     = (png_byte)(*rp       - *(rp + 1));
            *(rp + 2) = (png_byte)(*(rp + 2) - *(rp + 1));
         }
      }

#ifdef PNG_WRITE_16BIT_SUPPORTED
      else if (row_info->bit_depth == 16)
      {
         png_bytep rp;
         png_uint_32 i;

         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
            bytes_per_pixel = 6;

         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
            bytes_per_pixel = 8;

         else
            return;

         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
         {
            png_uint_32 s0   = (*(rp    ) << 8) | *(rp + 1);
            png_uint_32 s1   = (*(rp + 2) << 8) | *(rp + 3);
            png_uint_32 s2   = (*(rp + 4) << 8) | *(rp + 5);
            png_uint_32 red  = (png_uint_32)((s0 - s1) & 0xffffL);
            png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL);
            *(rp    ) = (png_byte)(red >> 8);
            *(rp + 1) = (png_byte)red;
            *(rp + 4) = (png_byte)(blue >> 8);
            *(rp + 5) = (png_byte)blue;
         }
      }
#endif /* WRITE_16BIT */
   }
}
#endif /* MNG_FEATURES */

B
bae 已提交
713
/* Called by user to write a row of image data */
D
duke 已提交
714
void PNGAPI
715
png_write_row(png_structrp png_ptr, png_const_bytep row)
D
duke 已提交
716
{
717 718 719
   /* 1.5.6: moved from png_struct to be a local structure: */
   png_row_info row_info;

D
duke 已提交
720 721
   if (png_ptr == NULL)
      return;
B
bae 已提交
722 723

   png_debug2(1, "in png_write_row (row %u, pass %d)",
D
duke 已提交
724 725
      png_ptr->row_number, png_ptr->pass);

B
bae 已提交
726
   /* Initialize transformations and other stuff if first time */
D
duke 已提交
727 728
   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
   {
B
bae 已提交
729
      /* Make sure we wrote the header info */
730
      if ((png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE) == 0)
B
bae 已提交
731 732
         png_error(png_ptr,
             "png_write_info was never called before png_write_row");
D
duke 已提交
733

B
bae 已提交
734
      /* Check for transforms that have been set but were defined out */
D
duke 已提交
735
#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
736
      if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
B
bae 已提交
737
         png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined");
D
duke 已提交
738
#endif
B
bae 已提交
739

D
duke 已提交
740
#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
741
      if ((png_ptr->transformations & PNG_FILLER) != 0)
B
bae 已提交
742
         png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined");
D
duke 已提交
743
#endif
B
bae 已提交
744 745
#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
    defined(PNG_READ_PACKSWAP_SUPPORTED)
746
      if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
B
bae 已提交
747 748
         png_warning(png_ptr,
             "PNG_WRITE_PACKSWAP_SUPPORTED is not defined");
D
duke 已提交
749
#endif
B
bae 已提交
750

D
duke 已提交
751
#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
752
      if ((png_ptr->transformations & PNG_PACK) != 0)
B
bae 已提交
753
         png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");
D
duke 已提交
754
#endif
B
bae 已提交
755

D
duke 已提交
756
#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
757
      if ((png_ptr->transformations & PNG_SHIFT) != 0)
B
bae 已提交
758
         png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");
D
duke 已提交
759
#endif
B
bae 已提交
760

D
duke 已提交
761
#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
762
      if ((png_ptr->transformations & PNG_BGR) != 0)
B
bae 已提交
763
         png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");
D
duke 已提交
764
#endif
B
bae 已提交
765

D
duke 已提交
766
#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
767
      if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
B
bae 已提交
768
         png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");
D
duke 已提交
769 770 771 772 773
#endif

      png_write_start_row(png_ptr);
   }

B
bae 已提交
774 775
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
   /* If interlaced and not interested in row, return */
776 777
   if (png_ptr->interlaced != 0 &&
       (png_ptr->transformations & PNG_INTERLACE) != 0)
D
duke 已提交
778 779 780 781
   {
      switch (png_ptr->pass)
      {
         case 0:
782
            if ((png_ptr->row_number & 0x07) != 0)
D
duke 已提交
783 784 785 786 787
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
B
bae 已提交
788

D
duke 已提交
789
         case 1:
790
            if ((png_ptr->row_number & 0x07) != 0 || png_ptr->width < 5)
D
duke 已提交
791 792 793 794 795
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
B
bae 已提交
796

D
duke 已提交
797 798 799 800 801 802 803
         case 2:
            if ((png_ptr->row_number & 0x07) != 4)
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
B
bae 已提交
804

D
duke 已提交
805
         case 3:
806
            if ((png_ptr->row_number & 0x03) != 0 || png_ptr->width < 3)
D
duke 已提交
807 808 809 810 811
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
B
bae 已提交
812

D
duke 已提交
813 814 815 816 817 818 819
         case 4:
            if ((png_ptr->row_number & 0x03) != 2)
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
B
bae 已提交
820

D
duke 已提交
821
         case 5:
822
            if ((png_ptr->row_number & 0x01) != 0 || png_ptr->width < 2)
D
duke 已提交
823 824 825 826 827
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
B
bae 已提交
828

D
duke 已提交
829
         case 6:
830
            if ((png_ptr->row_number & 0x01) == 0)
D
duke 已提交
831 832 833 834 835
            {
               png_write_finish_row(png_ptr);
               return;
            }
            break;
B
bae 已提交
836 837 838

         default: /* error: ignore it */
            break;
D
duke 已提交
839 840 841 842
      }
   }
#endif

B
bae 已提交
843
   /* Set up row info for transformations */
844 845 846 847 848 849 850 851 852 853 854 855 856
   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);
D
duke 已提交
857 858

   /* Copy user's row into buffer, leaving room for filter byte. */
859
   memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);
D
duke 已提交
860

B
bae 已提交
861 862
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
   /* Handle interlacing */
D
duke 已提交
863
   if (png_ptr->interlaced && png_ptr->pass < 6 &&
864
       (png_ptr->transformations & PNG_INTERLACE) != 0)
D
duke 已提交
865
   {
866
      png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass);
B
bae 已提交
867
      /* This should always get caught above, but still ... */
868
      if (row_info.width == 0)
D
duke 已提交
869 870 871 872 873 874 875
      {
         png_write_finish_row(png_ptr);
         return;
      }
   }
#endif

B
bae 已提交
876 877
#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
   /* Handle other transformations */
878 879
   if (png_ptr->transformations != 0)
      png_do_write_transformations(png_ptr, &row_info);
B
bae 已提交
880
#endif
D
duke 已提交
881

882 883 884 885 886 887 888
   /* 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");

B
bae 已提交
889
#ifdef PNG_MNG_FEATURES_SUPPORTED
D
duke 已提交
890 891 892 893 894 895 896 897 898
   /* 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
    */
899
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
B
bae 已提交
900
       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
D
duke 已提交
901 902
   {
      /* Intrapixel differencing */
903
      png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1);
D
duke 已提交
904 905 906
   }
#endif

907 908 909 910 911 912 913 914
/* Added at libpng-1.5.10 */
#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
   /* Check for out-of-range palette index */
   if (row_info.color_type == PNG_COLOR_TYPE_PALETTE &&
       png_ptr->num_palette_max >= 0)
      png_do_check_palette_indexes(png_ptr, &row_info);
#endif

D
duke 已提交
915
   /* Find a filter if necessary, filter the row and write it out. */
916
   png_write_find_filter(png_ptr, &row_info);
D
duke 已提交
917 918 919 920 921

   if (png_ptr->write_row_fn != NULL)
      (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
}

B
bae 已提交
922
#ifdef PNG_WRITE_FLUSH_SUPPORTED
D
duke 已提交
923 924
/* Set the automatic flush interval or 0 to turn flushing off */
void PNGAPI
925
png_set_flush(png_structrp png_ptr, int nrows)
D
duke 已提交
926
{
B
bae 已提交
927 928
   png_debug(1, "in png_set_flush");

D
duke 已提交
929 930
   if (png_ptr == NULL)
      return;
B
bae 已提交
931

D
duke 已提交
932 933 934
   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
}

B
bae 已提交
935
/* Flush the current output buffers now */
D
duke 已提交
936
void PNGAPI
937
png_write_flush(png_structrp png_ptr)
D
duke 已提交
938
{
B
bae 已提交
939 940
   png_debug(1, "in png_write_flush");

D
duke 已提交
941 942
   if (png_ptr == NULL)
      return;
B
bae 已提交
943

D
duke 已提交
944 945
   /* We have already written out all of the data */
   if (png_ptr->row_number >= png_ptr->num_rows)
B
bae 已提交
946
      return;
D
duke 已提交
947

948 949 950 951 952
   png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH);
   png_ptr->flush_rows = 0;
   png_flush(png_ptr);
}
#endif /* WRITE_FLUSH */
A
azvegint 已提交
953

954 955 956 957 958
/* Free any memory used in png_ptr struct without freeing the struct itself. */
static void
png_write_destroy(png_structrp png_ptr)
{
   png_debug(1, "in png_write_destroy");
A
azvegint 已提交
959

960 961 962
   /* Free any memory zlib uses */
   if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
      deflateEnd(&png_ptr->zstream);
A
azvegint 已提交
963

964 965 966 967 968 969 970 971 972 973 974 975
   /* Free our memory.  png_free checks NULL for us. */
   png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list);
   png_free(png_ptr, png_ptr->row_buf);
   png_ptr->row_buf = NULL;
#ifdef PNG_WRITE_FILTER_SUPPORTED
   png_free(png_ptr, png_ptr->prev_row);
   png_free(png_ptr, png_ptr->try_row);
   png_free(png_ptr, png_ptr->tst_row);
   png_ptr->prev_row = NULL;
   png_ptr->try_row = NULL;
   png_ptr->tst_row = NULL;
#endif
A
azvegint 已提交
976

977 978 979 980
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
   png_free(png_ptr, png_ptr->chunk_list);
   png_ptr->chunk_list = NULL;
#endif
A
azvegint 已提交
981

982 983 984 985
   /* 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.
    */
D
duke 已提交
986
}
B
bae 已提交
987

988 989 990 991 992 993 994
/* 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.)
 */
A
azvegint 已提交
995 996 997 998 999 1000 1001
void PNGAPI
png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
{
   png_debug(1, "in png_destroy_write_struct");

   if (png_ptr_ptr != NULL)
   {
1002
      png_structrp png_ptr = *png_ptr_ptr;
A
azvegint 已提交
1003

1004
      if (png_ptr != NULL) /* added in libpng 1.6.0 */
A
azvegint 已提交
1005
      {
1006
         png_destroy_info_struct(png_ptr, info_ptr_ptr);
A
azvegint 已提交
1007

1008 1009 1010
         *png_ptr_ptr = NULL;
         png_write_destroy(png_ptr);
         png_destroy_png_struct(png_ptr);
A
azvegint 已提交
1011 1012 1013 1014
      }
   }
}

1015 1016 1017
/* Allow the application to select one or more row filters to use. */
void PNGAPI
png_set_filter(png_structrp png_ptr, int method, int filters)
D
duke 已提交
1018
{
1019
   png_debug(1, "in png_set_filter");
D
duke 已提交
1020

1021 1022
   if (png_ptr == NULL)
      return;
B
bae 已提交
1023

1024 1025 1026 1027
#ifdef PNG_MNG_FEATURES_SUPPORTED
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
       (method == PNG_INTRAPIXEL_DIFFERENCING))
      method = PNG_FILTER_TYPE_BASE;
B
bae 已提交
1028

D
duke 已提交
1029 1030 1031 1032 1033
#endif
   if (method == PNG_FILTER_TYPE_BASE)
   {
      switch (filters & (PNG_ALL_FILTERS | 0x07))
      {
B
bae 已提交
1034
#ifdef PNG_WRITE_FILTER_SUPPORTED
D
duke 已提交
1035 1036
         case 5:
         case 6:
1037 1038 1039
         case 7: png_app_error(png_ptr, "Unknown row filter for method 0");
            /* FALL THROUGH */
#endif /* WRITE_FILTER */
B
bae 已提交
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
         case PNG_FILTER_VALUE_NONE:
            png_ptr->do_filter = PNG_FILTER_NONE; break;

#ifdef PNG_WRITE_FILTER_SUPPORTED
         case PNG_FILTER_VALUE_SUB:
            png_ptr->do_filter = PNG_FILTER_SUB; break;

         case PNG_FILTER_VALUE_UP:
            png_ptr->do_filter = PNG_FILTER_UP; break;

         case PNG_FILTER_VALUE_AVG:
            png_ptr->do_filter = PNG_FILTER_AVG; break;

         case PNG_FILTER_VALUE_PAETH:
            png_ptr->do_filter = PNG_FILTER_PAETH; break;

         default:
            png_ptr->do_filter = (png_byte)filters; break;
#else
         default:
1060 1061
            png_app_error(png_ptr, "Unknown row filter for method 0");
#endif /* WRITE_FILTER */
D
duke 已提交
1062 1063
      }

1064
#ifdef PNG_WRITE_FILTER_SUPPORTED
D
duke 已提交
1065 1066 1067 1068 1069 1070
      /* 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,
1071 1072 1073 1074 1075 1076
       * it should start out with all of the filters, and then remove them
       * or add them back after the start of compression.
       *
       * NOTE: this is a nasty constraint on the code, because it means that the
       * prev_row buffer must be maintained even if there are currently no
       * 'prev_row' requiring filters active.
D
duke 已提交
1077 1078 1079
       */
      if (png_ptr->row_buf != NULL)
      {
1080 1081
         int num_filters;
         png_alloc_size_t buf_size;
B
bae 已提交
1082

1083 1084 1085 1086 1087 1088
         /* Repeat the checks in png_write_start_row; 1 pixel high or wide
          * images cannot benefit from certain filters.  If this isn't done here
          * the check below will fire on 1 pixel high images.
          */
         if (png_ptr->height == 1)
            filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
B
bae 已提交
1089

1090 1091
         if (png_ptr->width == 1)
            filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);
D
duke 已提交
1092

1093 1094
         if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0
            && png_ptr->prev_row == NULL)
D
duke 已提交
1095
         {
1096 1097 1098 1099 1100 1101
            /* This is the error case, however it is benign - the previous row
             * is not available so the filter can't be used.  Just warn here.
             */
            png_app_warning(png_ptr,
               "png_set_filter: UP/AVG/PAETH cannot be added after start");
            filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
D
duke 已提交
1102 1103
         }

1104
         num_filters = 0;
D
duke 已提交
1105

1106 1107
         if (filters & PNG_FILTER_SUB)
            num_filters++;
B
bae 已提交
1108

1109 1110
         if (filters & PNG_FILTER_UP)
            num_filters++;
B
bae 已提交
1111

1112 1113
         if (filters & PNG_FILTER_AVG)
            num_filters++;
D
duke 已提交
1114

1115 1116
         if (filters & PNG_FILTER_PAETH)
            num_filters++;
D
duke 已提交
1117

1118 1119 1120 1121 1122
         /* Allocate needed row buffers if they have not already been
          * allocated.
          */
         buf_size = PNG_ROWBYTES(png_ptr->usr_channels * png_ptr->usr_bit_depth,
             png_ptr->width) + 1;
D
duke 已提交
1123

1124 1125 1126
         if (png_ptr->try_row == NULL)
            png_ptr->try_row = png_voidcast(png_bytep,
               png_malloc(png_ptr, buf_size));
B
bae 已提交
1127

1128
         if (num_filters > 1)
D
duke 已提交
1129
         {
1130 1131 1132
            if (png_ptr->tst_row == NULL)
               png_ptr->tst_row = png_voidcast(png_bytep,
                  png_malloc(png_ptr, buf_size));
D
duke 已提交
1133 1134
         }
      }
1135 1136
      png_ptr->do_filter = (png_byte)filters;
#endif
B
bae 已提交
1137 1138
   }
   else
1139
      png_error(png_ptr, "Unknown custom filter method");
B
bae 已提交
1140 1141
}

1142
#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */
B
bae 已提交
1143 1144 1145
/* Provide floating and fixed point APIs */
#ifdef PNG_FLOATING_POINT_SUPPORTED
void PNGAPI
1146
png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method,
B
bae 已提交
1147 1148 1149
    int num_weights, png_const_doublep filter_weights,
    png_const_doublep filter_costs)
{
1150 1151 1152 1153 1154
   PNG_UNUSED(png_ptr)
   PNG_UNUSED(heuristic_method)
   PNG_UNUSED(num_weights)
   PNG_UNUSED(filter_weights)
   PNG_UNUSED(filter_costs)
B
bae 已提交
1155 1156
}
#endif /* FLOATING_POINT */
D
duke 已提交
1157

B
bae 已提交
1158 1159
#ifdef PNG_FIXED_POINT_SUPPORTED
void PNGAPI
1160
png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method,
B
bae 已提交
1161 1162 1163
    int num_weights, png_const_fixed_point_p filter_weights,
    png_const_fixed_point_p filter_costs)
{
1164 1165 1166 1167 1168
   PNG_UNUSED(png_ptr)
   PNG_UNUSED(heuristic_method)
   PNG_UNUSED(num_weights)
   PNG_UNUSED(filter_weights)
   PNG_UNUSED(filter_costs)
D
duke 已提交
1169
}
B
bae 已提交
1170
#endif /* FIXED_POINT */
1171
#endif /* WRITE_WEIGHTED_FILTER */
D
duke 已提交
1172

1173
#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
D
duke 已提交
1174
void PNGAPI
1175
png_set_compression_level(png_structrp png_ptr, int level)
D
duke 已提交
1176
{
B
bae 已提交
1177 1178
   png_debug(1, "in png_set_compression_level");

D
duke 已提交
1179 1180
   if (png_ptr == NULL)
      return;
B
bae 已提交
1181

D
duke 已提交
1182 1183 1184 1185
   png_ptr->zlib_level = level;
}

void PNGAPI
1186
png_set_compression_mem_level(png_structrp png_ptr, int mem_level)
D
duke 已提交
1187
{
B
bae 已提交
1188 1189
   png_debug(1, "in png_set_compression_mem_level");

D
duke 已提交
1190 1191
   if (png_ptr == NULL)
      return;
B
bae 已提交
1192

D
duke 已提交
1193 1194 1195 1196
   png_ptr->zlib_mem_level = mem_level;
}

void PNGAPI
1197
png_set_compression_strategy(png_structrp png_ptr, int strategy)
D
duke 已提交
1198
{
B
bae 已提交
1199 1200
   png_debug(1, "in png_set_compression_strategy");

D
duke 已提交
1201 1202
   if (png_ptr == NULL)
      return;
B
bae 已提交
1203

1204 1205
   /* The flag setting here prevents the libpng dynamic selection of strategy.
    */
D
duke 已提交
1206 1207 1208 1209
   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
   png_ptr->zlib_strategy = strategy;
}

B
bae 已提交
1210 1211 1212
/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
 * smaller value of window_bits if it can do so safely.
 */
D
duke 已提交
1213
void PNGAPI
1214
png_set_compression_window_bits(png_structrp png_ptr, int window_bits)
D
duke 已提交
1215 1216 1217
{
   if (png_ptr == NULL)
      return;
B
bae 已提交
1218

1219 1220 1221 1222 1223 1224
   /* 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 that would cause
    * libpng to write a non-standard PNG file with raw deflate or gzip
    * compressed IDAT or ancillary chunks.  Such files can be read and there is
    * no warning on read, so this seems like a very bad idea.
    */
D
duke 已提交
1225
   if (window_bits > 15)
1226
   {
D
duke 已提交
1227
      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
1228 1229
      window_bits = 15;
   }
B
bae 已提交
1230

D
duke 已提交
1231
   else if (window_bits < 8)
1232
   {
D
duke 已提交
1233
      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
1234 1235
      window_bits = 8;
   }
B
bae 已提交
1236

D
duke 已提交
1237 1238 1239 1240
   png_ptr->zlib_window_bits = window_bits;
}

void PNGAPI
1241
png_set_compression_method(png_structrp png_ptr, int method)
D
duke 已提交
1242
{
B
bae 已提交
1243 1244
   png_debug(1, "in png_set_compression_method");

D
duke 已提交
1245 1246
   if (png_ptr == NULL)
      return;
B
bae 已提交
1247

1248 1249 1250
   /* 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.
    */
D
duke 已提交
1251 1252
   if (method != 8)
      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
B
bae 已提交
1253

D
duke 已提交
1254 1255
   png_ptr->zlib_method = method;
}
1256
#endif /* WRITE_CUSTOMIZE_COMPRESSION */
D
duke 已提交
1257

B
bae 已提交
1258 1259 1260
/* The following were added to libpng-1.5.4 */
#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
void PNGAPI
1261
png_set_text_compression_level(png_structrp png_ptr, int level)
B
bae 已提交
1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
{
   png_debug(1, "in png_set_text_compression_level");

   if (png_ptr == NULL)
      return;

   png_ptr->zlib_text_level = level;
}

void PNGAPI
1272
png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level)
B
bae 已提交
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
{
   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
1283
png_set_text_compression_strategy(png_structrp png_ptr, int strategy)
B
bae 已提交
1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
{
   png_debug(1, "in png_set_text_compression_strategy");

   if (png_ptr == NULL)
      return;

   png_ptr->zlib_text_strategy = strategy;
}

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

   if (window_bits > 15)
1303
   {
B
bae 已提交
1304
      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
1305 1306
      window_bits = 15;
   }
B
bae 已提交
1307 1308

   else if (window_bits < 8)
1309
   {
B
bae 已提交
1310
      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
1311 1312
      window_bits = 8;
   }
B
bae 已提交
1313 1314 1315 1316 1317

   png_ptr->zlib_text_window_bits = window_bits;
}

void PNGAPI
1318
png_set_text_compression_method(png_structrp png_ptr, int method)
B
bae 已提交
1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
{
   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;
}
1330
#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
B
bae 已提交
1331 1332
/* end of API added to libpng-1.5.4 */

D
duke 已提交
1333
void PNGAPI
1334
png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn)
D
duke 已提交
1335 1336 1337
{
   if (png_ptr == NULL)
      return;
B
bae 已提交
1338

D
duke 已提交
1339 1340 1341
   png_ptr->write_row_fn = write_row_fn;
}

B
bae 已提交
1342
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
D
duke 已提交
1343
void PNGAPI
1344
png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
B
bae 已提交
1345
    write_user_transform_fn)
D
duke 已提交
1346
{
B
bae 已提交
1347 1348
   png_debug(1, "in png_set_write_user_transform_fn");

D
duke 已提交
1349 1350
   if (png_ptr == NULL)
      return;
B
bae 已提交
1351

D
duke 已提交
1352 1353 1354 1355 1356 1357
   png_ptr->transformations |= PNG_USER_TRANSFORM;
   png_ptr->write_user_transform_fn = write_user_transform_fn;
}
#endif


B
bae 已提交
1358
#ifdef PNG_INFO_IMAGE_SUPPORTED
D
duke 已提交
1359
void PNGAPI
1360
png_write_png(png_structrp png_ptr, png_inforp info_ptr,
B
bae 已提交
1361
    int transforms, voidp params)
D
duke 已提交
1362 1363 1364 1365
{
   if (png_ptr == NULL || info_ptr == NULL)
      return;

1366 1367 1368 1369 1370 1371
   if ((info_ptr->valid & PNG_INFO_IDAT) == 0)
   {
      png_app_error(png_ptr, "no rows for png_write_image to write");
      return;
   }

D
duke 已提交
1372 1373 1374 1375 1376
   /* Write the file header information. */
   png_write_info(png_ptr, info_ptr);

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

A
azvegint 已提交
1377
   /* Invert monochrome pixels */
1378 1379
   if ((transforms & PNG_TRANSFORM_INVERT_MONO) != 0)
#ifdef PNG_WRITE_INVERT_SUPPORTED
B
bae 已提交
1380
      png_set_invert_mono(png_ptr);
1381 1382
#else
      png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_MONO not supported");
D
duke 已提交
1383 1384 1385 1386 1387
#endif

   /* Shift the pixels up to a legal bit depth and fill in
    * as appropriate to correctly scale the image.
    */
1388 1389 1390 1391 1392 1393
   if ((transforms & PNG_TRANSFORM_SHIFT) != 0)
#ifdef PNG_WRITE_SHIFT_SUPPORTED
      if ((info_ptr->valid & PNG_INFO_sBIT) != 0)
         png_set_shift(png_ptr, &info_ptr->sig_bit);
#else
      png_app_error(png_ptr, "PNG_TRANSFORM_SHIFT not supported");
D
duke 已提交
1394 1395
#endif

A
azvegint 已提交
1396
   /* Pack pixels into bytes */
1397 1398 1399 1400 1401
   if ((transforms & PNG_TRANSFORM_PACKING) != 0)
#ifdef PNG_WRITE_PACK_SUPPORTED
      png_set_packing(png_ptr);
#else
      png_app_error(png_ptr, "PNG_TRANSFORM_PACKING not supported");
D
duke 已提交
1402 1403
#endif

A
azvegint 已提交
1404
   /* Swap location of alpha bytes from ARGB to RGBA */
1405 1406
   if ((transforms & PNG_TRANSFORM_SWAP_ALPHA) != 0)
#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
B
bae 已提交
1407
      png_set_swap_alpha(png_ptr);
1408 1409
#else
      png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ALPHA not supported");
D
duke 已提交
1410 1411
#endif

1412 1413 1414 1415 1416 1417 1418
   /* Remove a filler (X) from XRGB/RGBX/AG/GA into to convert it into
    * RGB, note that the code expects the input color type to be G or RGB; no
    * alpha channel.
    */
   if ((transforms & (PNG_TRANSFORM_STRIP_FILLER_AFTER|
       PNG_TRANSFORM_STRIP_FILLER_BEFORE)) != 0)
   {
B
bae 已提交
1419
#ifdef PNG_WRITE_FILLER_SUPPORTED
1420 1421 1422 1423 1424
      if ((transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) != 0)
      {
         if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0)
            png_app_error(png_ptr,
                "PNG_TRANSFORM_STRIP_FILLER: BEFORE+AFTER not supported");
B
bae 已提交
1425

1426 1427 1428 1429 1430 1431 1432 1433
         /* Continue if ignored - this is the pre-1.6.10 behavior */
         png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
      }

      else if ((transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) != 0)
         png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
#else
      png_app_error(png_ptr, "PNG_TRANSFORM_STRIP_FILLER not supported");
D
duke 已提交
1434
#endif
1435
   }
D
duke 已提交
1436

A
azvegint 已提交
1437
   /* Flip BGR pixels to RGB */
1438 1439
   if ((transforms & PNG_TRANSFORM_BGR) != 0)
#ifdef PNG_WRITE_BGR_SUPPORTED
B
bae 已提交
1440
      png_set_bgr(png_ptr);
1441 1442
#else
      png_app_error(png_ptr, "PNG_TRANSFORM_BGR not supported");
D
duke 已提交
1443 1444
#endif

A
azvegint 已提交
1445
   /* Swap bytes of 16-bit files to most significant byte first */
1446 1447
   if ((transforms & PNG_TRANSFORM_SWAP_ENDIAN) != 0)
#ifdef PNG_WRITE_SWAP_SUPPORTED
B
bae 已提交
1448
      png_set_swap(png_ptr);
1449 1450
#else
      png_app_error(png_ptr, "PNG_TRANSFORM_SWAP_ENDIAN not supported");
D
duke 已提交
1451 1452
#endif

1453 1454
   /* Swap bits of 1-bit, 2-bit, 4-bit packed pixel formats */
   if ((transforms & PNG_TRANSFORM_PACKSWAP) != 0)
A
azvegint 已提交
1455
#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
B
bae 已提交
1456
      png_set_packswap(png_ptr);
1457 1458
#else
      png_app_error(png_ptr, "PNG_TRANSFORM_PACKSWAP not supported");
B
bae 已提交
1459 1460
#endif

A
azvegint 已提交
1461
   /* Invert the alpha channel from opacity to transparency */
1462 1463
   if ((transforms & PNG_TRANSFORM_INVERT_ALPHA) != 0)
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
B
bae 已提交
1464
      png_set_invert_alpha(png_ptr);
1465 1466
#else
      png_app_error(png_ptr, "PNG_TRANSFORM_INVERT_ALPHA not supported");
D
duke 已提交
1467 1468 1469 1470
#endif

   /* ----------------------- end of transformations ------------------- */

B
bae 已提交
1471
   /* Write the bits */
1472
   png_write_image(png_ptr, info_ptr->row_pointers);
D
duke 已提交
1473 1474 1475 1476

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

B
bae 已提交
1477
   PNG_UNUSED(params)
D
duke 已提交
1478 1479
}
#endif
1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533


#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
/* 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)
      {
         png_controlp control = png_voidcast(png_controlp,
             png_malloc_warn(png_ptr, (sizeof *control)));

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

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

            image->opaque = control;
            return 1;
         }

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

      png_destroy_write_struct(&png_ptr, NULL);
   }

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

/* Arguments to png_image_write_main: */
typedef struct
{
   /* Arguments: */
   png_imagep      image;
   png_const_voidp buffer;
   png_int_32      row_stride;
   png_const_voidp colormap;
   int             convert_to_8bit;
   /* Local variables: */
   png_const_voidp first_row;
   ptrdiff_t       row_bytes;
   png_voidp       local_row;
1534 1535 1536 1537
   /* Byte count for memory writing */
   png_bytep        memory;
   png_alloc_size_t memory_bytes; /* not used for STDIO */
   png_alloc_size_t output_bytes; /* running total */
1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 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 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 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 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 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
} png_image_write_control;

/* 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
 * png_image format value.
 */
static int
png_write_image_16bit(png_voidp argument)
{
   png_image_write_control *display = png_voidcast(png_image_write_control*,
       argument);
   png_imagep image = display->image;
   png_structrp png_ptr = image->opaque->png_ptr;

   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);
   png_uint_16p row_end;
   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
   int aindex = 0;
   png_uint_32 y = image->height;

   if ((image->format & PNG_FORMAT_FLAG_ALPHA) != 0)
   {
#   ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
      if ((image->format & PNG_FORMAT_FLAG_AFIRST) != 0)
      {
         aindex = -1;
         ++input_row; /* To point to the first component */
         ++output_row;
      }
         else
            aindex = channels;
#     else
         aindex = channels;
#     endif
   }

   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
    * row; this is correct.
    */
   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)
      {
         const png_uint_16 alpha = in_ptr[aindex];
         png_uint_32 reciprocal = 0;
         int c;

         out_ptr[aindex] = alpha;

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

      png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row));
      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.
 *
 * 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.
 */
#   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;
}

static int
png_write_image_8bit(png_voidp argument)
{
   png_image_write_control *display = png_voidcast(png_image_write_control*,
       argument);
   png_imagep image = display->image;
   png_structrp png_ptr = image->opaque->png_ptr;

   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);
   png_uint_32 y = image->height;
   const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;

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

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

      else
#   endif
      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;

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

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

            if (alphabyte > 0 && alphabyte < 255)
               reciprocal = UNP_RECIPROCAL(alpha);

            c = channels;
            do /* always at least one channel */
               *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal);
            while (--c > 0);

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

         png_write_row(png_ptr, png_voidcast(png_const_bytep,
             display->local_row));
         input_row += display->row_bytes/(sizeof (png_uint_16));
      } /* while y */
   }

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

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

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

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

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

   return 1;
}

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

#   if defined(PNG_FORMAT_BGR_SUPPORTED) &&\
      defined(PNG_SIMPLIFIED_WRITE_AFIRST_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) != 0 ? 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) != 0)
      {
         png_const_uint_16p entry = png_voidcast(png_const_uint_16p, cmap);

         entry += i * channels;

         if ((channels & 1) != 0) /* 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;
}

static int
png_image_write_main(png_voidp argument)
{
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);
   png_imagep image = display->image;
   png_structrp png_ptr = image->opaque->png_ptr;
   png_inforp info_ptr = image->opaque->info_ptr;
   png_uint_32 format = image->format;

   /* The following four ints are actually booleans */
   int colormap = (format & PNG_FORMAT_FLAG_COLORMAP);
   int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR); /* input */
   int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA);
   int write_16bit = linear && !colormap && (display->convert_to_8bit == 0);

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

1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001
   /* Default the 'row_stride' parameter if required, also check the row stride
    * and total image size to ensure that they are within the system limits.
    */
   {
      const unsigned int channels = PNG_IMAGE_PIXEL_CHANNELS(image->format);

      if (image->width <= 0x7FFFFFFFU/channels) /* no overflow */
      {
         png_uint_32 check;
         const png_uint_32 png_row_stride = image->width * channels;

         if (display->row_stride == 0)
            display->row_stride = (png_int_32)/*SAFE*/png_row_stride;

         if (display->row_stride < 0)
            check = -display->row_stride;

         else
            check = display->row_stride;

         if (check >= png_row_stride)
         {
            /* Now check for overflow of the image buffer calculation; this
             * limits the whole image size to 32 bits for API compatibility with
             * the current, 32-bit, PNG_IMAGE_BUFFER_SIZE macro.
             */
            if (image->height > 0xFFFFFFFF/png_row_stride)
               png_error(image->opaque->png_ptr, "memory image too large");
         }

         else
            png_error(image->opaque->png_ptr, "supplied row stride too small");
      }

      else
         png_error(image->opaque->png_ptr, "image row stride too large");
   }
2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177

   /* Set the required transforms then write the rows in the correct order. */
   if ((format & PNG_FORMAT_FLAG_COLORMAP) != 0)
   {
      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");
   }

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

   /* Counter-intuitively the data transformations must be called *after*
    * png_write_info, not before as in the read code, but the 'set' functions
    * must still be called before.  Just set the color space information, never
    * write an interlaced image.
    */

   if (write_16bit != 0)
   {
      /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */
      png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR);

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

   else if ((image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB) == 0)
      png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);

   /* 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);

   /* 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.
    *
    * First check for a little endian system if writing 16-bit files.
    */
   if (write_16bit != 0)
   {
      PNG_CONST png_uint_16 le = 0x0001;

      if ((*(png_const_bytep) & le) != 0)
         png_set_swap(png_ptr);
   }

#   ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
      if ((format & PNG_FORMAT_FLAG_BGR) != 0)
      {
         if (colormap == 0 && (format & PNG_FORMAT_FLAG_COLOR) != 0)
            png_set_bgr(png_ptr);
         format &= ~PNG_FORMAT_FLAG_BGR;
      }
#   endif

#   ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
      if ((format & PNG_FORMAT_FLAG_AFIRST) != 0)
      {
         if (colormap == 0 && (format & PNG_FORMAT_FLAG_ALPHA) != 0)
            png_set_swap_alpha(png_ptr);
         format &= ~PNG_FORMAT_FLAG_AFIRST;
      }
#   endif

   /* 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 != 0 && image->colormap_entries <= 16)
      png_set_packing(png_ptr);

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

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

      if (linear != 0)
         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;
   }

   /* 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.
       */
#   ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
      png_set_compression_level(png_ptr, 3);
#   endif
   }

   /* Check for the cases that currently require a pre-transform on the row
    * before it is written.  This only applies when the input is 16-bit and
    * either there is an alpha channel or it is converted to 8-bit.
    */
   if ((linear != 0 && alpha != 0 ) ||
       (colormap == 0 && display->convert_to_8bit != 0))
   {
      png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr,
         png_get_rowbytes(png_ptr, info_ptr)));
      int result;

      display->local_row = row;
      if (write_16bit != 0)
         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 == 0)
         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
   {
      png_const_bytep row = png_voidcast(png_const_bytep, display->first_row);
      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;
}

2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293

static void (PNGCBAPI
image_memory_write)(png_structp png_ptr, png_bytep/*const*/ data,
   png_size_t size)
{
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      png_ptr->io_ptr/*backdoor: png_get_io_ptr(png_ptr)*/);
   const png_alloc_size_t ob = display->output_bytes;

   /* Check for overflow; this should never happen: */
   if (size <= ((png_alloc_size_t)-1) - ob)
   {
      /* I don't think libpng ever does this, but just in case: */
      if (size > 0)
      {
         if (display->memory_bytes >= ob+size) /* writing */
            memcpy(display->memory+ob, data, size);

         /* Always update the size: */
         display->output_bytes = ob+size;
      }
   }

   else
      png_error(png_ptr, "png_image_write_to_memory: PNG too big");
}

static void (PNGCBAPI
image_memory_flush)(png_structp png_ptr)
{
   PNG_UNUSED(png_ptr)
}

static int
png_image_write_memory(png_voidp argument)
{
   png_image_write_control *display = png_voidcast(png_image_write_control*,
      argument);

   /* The rest of the memory-specific init and write_main in an error protected
    * environment.  This case needs to use callbacks for the write operations
    * since libpng has no built in support for writing to memory.
    */
   png_set_write_fn(display->image->opaque->png_ptr, display/*io_ptr*/,
         image_memory_write, image_memory_flush);

   return png_image_write_main(display);
}

int PNGAPI
png_image_write_to_memory(png_imagep image, void *memory,
   png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8bit,
   const void *buffer, png_int_32 row_stride, const void *colormap)
{
   /* Write the image to the given buffer, or count the bytes if it is NULL */
   if (image != NULL && image->version == PNG_IMAGE_VERSION)
   {
      if (memory_bytes != NULL && buffer != NULL)
      {
         /* This is to give the caller an easier error detection in the NULL
          * case and guard against uninitialized variable problems:
          */
         if (memory == NULL)
            *memory_bytes = 0;

         if (png_image_write_init(image) != 0)
         {
            png_image_write_control display;
            int result;

            memset(&display, 0, (sizeof display));
            display.image = image;
            display.buffer = buffer;
            display.row_stride = row_stride;
            display.colormap = colormap;
            display.convert_to_8bit = convert_to_8bit;
            display.memory = png_voidcast(png_bytep, memory);
            display.memory_bytes = *memory_bytes;
            display.output_bytes = 0;

            result = png_safe_execute(image, png_image_write_memory, &display);
            png_image_free(image);

            /* write_memory returns true even if we ran out of buffer. */
            if (result)
            {
               /* On out-of-buffer this function returns '0' but still updates
                * memory_bytes:
                */
               if (memory != NULL && display.output_bytes > *memory_bytes)
                  result = 0;

               *memory_bytes = display.output_bytes;
            }

            return result;
         }

         else
            return 0;
      }

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

   else if (image != NULL)
      return png_image_error(image,
         "png_image_write_to_memory: incorrect PNG_IMAGE_VERSION");

   else
      return 0;
}

#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
2294 2295 2296 2297 2298 2299 2300
int PNGAPI
png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit,
   const void *buffer, png_int_32 row_stride, const void *colormap)
{
   /* Write the image to the given (FILE*). */
   if (image != NULL && image->version == PNG_IMAGE_VERSION)
   {
2301
      if (file != NULL && buffer != NULL)
2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350
      {
         if (png_image_write_init(image) != 0)
         {
            png_image_write_control display;
            int result;

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

            memset(&display, 0, (sizeof display));
            display.image = image;
            display.buffer = buffer;
            display.row_stride = row_stride;
            display.colormap = colormap;
            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 if (image != NULL)
      return png_image_error(image,
         "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION");

   else
      return 0;
}

int PNGAPI
png_image_write_to_file(png_imagep image, const char *file_name,
   int convert_to_8bit, const void *buffer, png_int_32 row_stride,
   const void *colormap)
{
   /* Write the image to the named file. */
   if (image != NULL && image->version == PNG_IMAGE_VERSION)
   {
2351
      if (file_name != NULL && buffer != NULL)
2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408
      {
         FILE *fp = fopen(file_name, "wb");

         if (fp != NULL)
         {
            if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer,
               row_stride, colormap) != 0)
            {
               int error; /* from fflush/fclose */

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

                  error = errno; /* from fclose */
               }

               else
               {
                  error = errno; /* from fflush or ferror */
                  (void)fclose(fp);
               }

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

            else
            {
               /* Clean up: just the opened file. */
               (void)fclose(fp);
               (void)remove(file_name);
               return 0;
            }
         }

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

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

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

   else
      return 0;
}
2409
#endif /* SIMPLIFIED_WRITE_STDIO */
2410 2411
#endif /* SIMPLIFIED_WRITE */
#endif /* WRITE */