Home | History | Annotate | Download | only in libpng
      1 
      2 /* pngwrite.c - general routines to write a PNG file
      3  *
      4  * Last changed in libpng 1.2.42 [January 3, 2010]
      5  * Copyright (c) 1998-2010 Glenn Randers-Pehrson
      6  * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
      7  * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
      8  *
      9  * This code is released under the libpng license.
     10  * For conditions of distribution and use, see the disclaimer
     11  * and license in png.h
     12  */
     13 
     14 /* Get internal access to png.h */
     15 #define PNG_INTERNAL
     16 #define PNG_NO_PEDANTIC_WARNINGS
     17 #include "png.h"
     18 #ifdef PNG_WRITE_SUPPORTED
     19 
     20 /* Writes all the PNG information.  This is the suggested way to use the
     21  * library.  If you have a new chunk to add, make a function to write it,
     22  * and put it in the correct location here.  If you want the chunk written
     23  * after the image data, put it in png_write_end().  I strongly encourage
     24  * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
     25  * the chunk, as that will keep the code from breaking if you want to just
     26  * write a plain PNG file.  If you have long comments, I suggest writing
     27  * them in png_write_end(), and compressing them.
     28  */
     29 void PNGAPI
     30 png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr)
     31 {
     32    png_debug(1, "in png_write_info_before_PLTE");
     33 
     34    if (png_ptr == NULL || info_ptr == NULL)
     35       return;
     36    if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
     37    {
     38    /* Write PNG signature */
     39    png_write_sig(png_ptr);
     40 #ifdef PNG_MNG_FEATURES_SUPPORTED
     41    if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \
     42       (png_ptr->mng_features_permitted))
     43    {
     44       png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
     45       png_ptr->mng_features_permitted = 0;
     46    }
     47 #endif
     48    /* Write IHDR information. */
     49    png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
     50       info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
     51       info_ptr->filter_type,
     52 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
     53       info_ptr->interlace_type);
     54 #else
     55       0);
     56 #endif
     57    /* The rest of these check to see if the valid field has the appropriate
     58     * flag set, and if it does, writes the chunk.
     59     */
     60 #ifdef PNG_WRITE_gAMA_SUPPORTED
     61    if (info_ptr->valid & PNG_INFO_gAMA)
     62    {
     63 #  ifdef PNG_FLOATING_POINT_SUPPORTED
     64       png_write_gAMA(png_ptr, info_ptr->gamma);
     65 #else
     66 #ifdef PNG_FIXED_POINT_SUPPORTED
     67       png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma);
     68 #  endif
     69 #endif
     70    }
     71 #endif
     72 #ifdef PNG_WRITE_sRGB_SUPPORTED
     73    if (info_ptr->valid & PNG_INFO_sRGB)
     74       png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent);
     75 #endif
     76 #ifdef PNG_WRITE_iCCP_SUPPORTED
     77    if (info_ptr->valid & PNG_INFO_iCCP)
     78       png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE,
     79                      info_ptr->iccp_profile, (int)info_ptr->iccp_proflen);
     80 #endif
     81 #ifdef PNG_WRITE_sBIT_SUPPORTED
     82    if (info_ptr->valid & PNG_INFO_sBIT)
     83       png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
     84 #endif
     85 #ifdef PNG_WRITE_cHRM_SUPPORTED
     86    if (info_ptr->valid & PNG_INFO_cHRM)
     87    {
     88 #ifdef PNG_FLOATING_POINT_SUPPORTED
     89       png_write_cHRM(png_ptr,
     90          info_ptr->x_white, info_ptr->y_white,
     91          info_ptr->x_red, info_ptr->y_red,
     92          info_ptr->x_green, info_ptr->y_green,
     93          info_ptr->x_blue, info_ptr->y_blue);
     94 #else
     95 #  ifdef PNG_FIXED_POINT_SUPPORTED
     96       png_write_cHRM_fixed(png_ptr,
     97          info_ptr->int_x_white, info_ptr->int_y_white,
     98          info_ptr->int_x_red, info_ptr->int_y_red,
     99          info_ptr->int_x_green, info_ptr->int_y_green,
    100          info_ptr->int_x_blue, info_ptr->int_y_blue);
    101 #  endif
    102 #endif
    103    }
    104 #endif
    105 #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
    106    if (info_ptr->unknown_chunks_num)
    107    {
    108       png_unknown_chunk *up;
    109 
    110       png_debug(5, "writing extra chunks");
    111 
    112       for (up = info_ptr->unknown_chunks;
    113            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
    114            up++)
    115       {
    116          int keep = png_handle_as_unknown(png_ptr, up->name);
    117          if (keep != PNG_HANDLE_CHUNK_NEVER &&
    118             up->location && !(up->location & PNG_HAVE_PLTE) &&
    119             !(up->location & PNG_HAVE_IDAT) &&
    120             ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
    121             (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
    122          {
    123             if (up->size == 0)
    124                png_warning(png_ptr, "Writing zero-length unknown chunk");
    125             png_write_chunk(png_ptr, up->name, up->data, up->size);
    126          }
    127       }
    128    }
    129 #endif
    130       png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
    131    }
    132 }
    133 
    134 void PNGAPI
    135 png_write_info(png_structp png_ptr, png_infop info_ptr)
    136 {
    137 #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
    138    int i;
    139 #endif
    140 
    141    png_debug(1, "in png_write_info");
    142 
    143    if (png_ptr == NULL || info_ptr == NULL)
    144       return;
    145 
    146    png_write_info_before_PLTE(png_ptr, info_ptr);
    147 
    148    if (info_ptr->valid & PNG_INFO_PLTE)
    149       png_write_PLTE(png_ptr, info_ptr->palette,
    150          (png_uint_32)info_ptr->num_palette);
    151    else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
    152       png_error(png_ptr, "Valid palette required for paletted images");
    153 
    154 #ifdef PNG_WRITE_tRNS_SUPPORTED
    155    if (info_ptr->valid & PNG_INFO_tRNS)
    156    {
    157 #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
    158       /* Invert the alpha channel (in tRNS) */
    159       if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&
    160          info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
    161       {
    162          int j;
    163          for (j = 0; j<(int)info_ptr->num_trans; j++)
    164             info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]);
    165       }
    166 #endif
    167       png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values),
    168          info_ptr->num_trans, info_ptr->color_type);
    169    }
    170 #endif
    171 #ifdef PNG_WRITE_bKGD_SUPPORTED
    172    if (info_ptr->valid & PNG_INFO_bKGD)
    173       png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
    174 #endif
    175 #ifdef PNG_WRITE_hIST_SUPPORTED
    176    if (info_ptr->valid & PNG_INFO_hIST)
    177       png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
    178 #endif
    179 #ifdef PNG_WRITE_oFFs_SUPPORTED
    180    if (info_ptr->valid & PNG_INFO_oFFs)
    181       png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
    182          info_ptr->offset_unit_type);
    183 #endif
    184 #ifdef PNG_WRITE_pCAL_SUPPORTED
    185    if (info_ptr->valid & PNG_INFO_pCAL)
    186       png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
    187          info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
    188          info_ptr->pcal_units, info_ptr->pcal_params);
    189 #endif
    190 
    191 #ifdef PNG_sCAL_SUPPORTED
    192    if (info_ptr->valid & PNG_INFO_sCAL)
    193 #ifdef PNG_WRITE_sCAL_SUPPORTED
    194 #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED)
    195       png_write_sCAL(png_ptr, (int)info_ptr->scal_unit,
    196           info_ptr->scal_pixel_width, info_ptr->scal_pixel_height);
    197 #else /* !FLOATING_POINT */
    198 #ifdef PNG_FIXED_POINT_SUPPORTED
    199       png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
    200           info_ptr->scal_s_width, info_ptr->scal_s_height);
    201 #endif /* FIXED_POINT */
    202 #endif /* FLOATING_POINT */
    203 #else  /* !WRITE_sCAL */
    204       png_warning(png_ptr,
    205           "png_write_sCAL not supported; sCAL chunk not written.");
    206 #endif /* WRITE_sCAL */
    207 #endif /* sCAL */
    208 
    209 #ifdef PNG_WRITE_pHYs_SUPPORTED
    210    if (info_ptr->valid & PNG_INFO_pHYs)
    211       png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
    212          info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
    213 #endif /* pHYs */
    214 
    215 #ifdef PNG_WRITE_tIME_SUPPORTED
    216    if (info_ptr->valid & PNG_INFO_tIME)
    217    {
    218       png_write_tIME(png_ptr, &(info_ptr->mod_time));
    219       png_ptr->mode |= PNG_WROTE_tIME;
    220    }
    221 #endif /* tIME */
    222 
    223 #ifdef PNG_WRITE_sPLT_SUPPORTED
    224    if (info_ptr->valid & PNG_INFO_sPLT)
    225      for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
    226        png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
    227 #endif /* sPLT */
    228 
    229 #ifdef PNG_WRITE_TEXT_SUPPORTED
    230    /* Check to see if we need to write text chunks */
    231    for (i = 0; i < info_ptr->num_text; i++)
    232    {
    233       png_debug2(2, "Writing header text chunk %d, type %d", i,
    234          info_ptr->text[i].compression);
    235       /* An internationalized chunk? */
    236       if (info_ptr->text[i].compression > 0)
    237       {
    238 #ifdef PNG_WRITE_iTXt_SUPPORTED
    239           /* Write international chunk */
    240           png_write_iTXt(png_ptr,
    241                          info_ptr->text[i].compression,
    242                          info_ptr->text[i].key,
    243                          info_ptr->text[i].lang,
    244                          info_ptr->text[i].lang_key,
    245                          info_ptr->text[i].text);
    246 #else
    247           png_warning(png_ptr, "Unable to write international text");
    248 #endif
    249           /* Mark this chunk as written */
    250           info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
    251       }
    252       /* If we want a compressed text chunk */
    253       else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
    254       {
    255 #ifdef PNG_WRITE_zTXt_SUPPORTED
    256          /* Write compressed chunk */
    257          png_write_zTXt(png_ptr, info_ptr->text[i].key,
    258             info_ptr->text[i].text, 0,
    259             info_ptr->text[i].compression);
    260 #else
    261          png_warning(png_ptr, "Unable to write compressed text");
    262 #endif
    263          /* Mark this chunk as written */
    264          info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
    265       }
    266       else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
    267       {
    268 #ifdef PNG_WRITE_tEXt_SUPPORTED
    269          /* Write uncompressed chunk */
    270          png_write_tEXt(png_ptr, info_ptr->text[i].key,
    271                          info_ptr->text[i].text,
    272                          0);
    273          /* Mark this chunk as written */
    274          info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
    275 #else
    276          /* Can't get here */
    277          png_warning(png_ptr, "Unable to write uncompressed text");
    278 #endif
    279       }
    280    }
    281 #endif /* tEXt */
    282 
    283 #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
    284    if (info_ptr->unknown_chunks_num)
    285    {
    286       png_unknown_chunk *up;
    287 
    288       png_debug(5, "writing extra chunks");
    289 
    290       for (up = info_ptr->unknown_chunks;
    291            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
    292            up++)
    293       {
    294          int keep = png_handle_as_unknown(png_ptr, up->name);
    295          if (keep != PNG_HANDLE_CHUNK_NEVER &&
    296             up->location && (up->location & PNG_HAVE_PLTE) &&
    297             !(up->location & PNG_HAVE_IDAT) &&
    298             ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
    299             (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
    300          {
    301             png_write_chunk(png_ptr, up->name, up->data, up->size);
    302          }
    303       }
    304    }
    305 #endif
    306 }
    307 
    308 /* Writes the end of the PNG file.  If you don't want to write comments or
    309  * time information, you can pass NULL for info.  If you already wrote these
    310  * in png_write_info(), do not write them again here.  If you have long
    311  * comments, I suggest writing them here, and compressing them.
    312  */
    313 void PNGAPI
    314 png_write_end(png_structp png_ptr, png_infop info_ptr)
    315 {
    316    png_debug(1, "in png_write_end");
    317 
    318    if (png_ptr == NULL)
    319       return;
    320    if (!(png_ptr->mode & PNG_HAVE_IDAT))
    321       png_error(png_ptr, "No IDATs written into file");
    322 
    323    /* See if user wants us to write information chunks */
    324    if (info_ptr != NULL)
    325    {
    326 #ifdef PNG_WRITE_TEXT_SUPPORTED
    327       int i; /* local index variable */
    328 #endif
    329 #ifdef PNG_WRITE_tIME_SUPPORTED
    330       /* Check to see if user has supplied a time chunk */
    331       if ((info_ptr->valid & PNG_INFO_tIME) &&
    332          !(png_ptr->mode & PNG_WROTE_tIME))
    333          png_write_tIME(png_ptr, &(info_ptr->mod_time));
    334 #endif
    335 #ifdef PNG_WRITE_TEXT_SUPPORTED
    336       /* Loop through comment chunks */
    337       for (i = 0; i < info_ptr->num_text; i++)
    338       {
    339          png_debug2(2, "Writing trailer text chunk %d, type %d", i,
    340             info_ptr->text[i].compression);
    341          /* An internationalized chunk? */
    342          if (info_ptr->text[i].compression > 0)
    343          {
    344 #ifdef PNG_WRITE_iTXt_SUPPORTED
    345             /* Write international chunk */
    346             png_write_iTXt(png_ptr,
    347                         info_ptr->text[i].compression,
    348                         info_ptr->text[i].key,
    349                         info_ptr->text[i].lang,
    350                         info_ptr->text[i].lang_key,
    351                         info_ptr->text[i].text);
    352 #else
    353             png_warning(png_ptr, "Unable to write international text");
    354 #endif
    355             /* Mark this chunk as written */
    356             info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
    357          }
    358          else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
    359          {
    360 #ifdef PNG_WRITE_zTXt_SUPPORTED
    361             /* Write compressed chunk */
    362             png_write_zTXt(png_ptr, info_ptr->text[i].key,
    363                info_ptr->text[i].text, 0,
    364                info_ptr->text[i].compression);
    365 #else
    366             png_warning(png_ptr, "Unable to write compressed text");
    367 #endif
    368             /* Mark this chunk as written */
    369             info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
    370          }
    371          else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
    372          {
    373 #ifdef PNG_WRITE_tEXt_SUPPORTED
    374             /* Write uncompressed chunk */
    375             png_write_tEXt(png_ptr, info_ptr->text[i].key,
    376                info_ptr->text[i].text, 0);
    377 #else
    378             png_warning(png_ptr, "Unable to write uncompressed text");
    379 #endif
    380 
    381             /* Mark this chunk as written */
    382             info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
    383          }
    384       }
    385 #endif
    386 #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
    387    if (info_ptr->unknown_chunks_num)
    388    {
    389       png_unknown_chunk *up;
    390 
    391       png_debug(5, "writing extra chunks");
    392 
    393       for (up = info_ptr->unknown_chunks;
    394            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
    395            up++)
    396       {
    397          int keep = png_handle_as_unknown(png_ptr, up->name);
    398          if (keep != PNG_HANDLE_CHUNK_NEVER &&
    399             up->location && (up->location & PNG_AFTER_IDAT) &&
    400             ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
    401             (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
    402          {
    403             png_write_chunk(png_ptr, up->name, up->data, up->size);
    404          }
    405       }
    406    }
    407 #endif
    408    }
    409 
    410    png_ptr->mode |= PNG_AFTER_IDAT;
    411 
    412    /* Write end of PNG file */
    413    png_write_IEND(png_ptr);
    414    /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03,
    415     * and restored again in libpng-1.2.30, may cause some applications that
    416     * do not set png_ptr->output_flush_fn to crash.  If your application
    417     * experiences a problem, please try building libpng with
    418     * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to
    419     * png-mng-implement at lists.sf.net .
    420     */
    421 #ifdef PNG_WRITE_FLUSH_SUPPORTED
    422 #  ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
    423    png_flush(png_ptr);
    424 #  endif
    425 #endif
    426 }
    427 
    428 #ifdef PNG_CONVERT_tIME_SUPPORTED
    429 /* "tm" structure is not supported on WindowsCE */
    430 void PNGAPI
    431 png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime)
    432 {
    433    png_debug(1, "in png_convert_from_struct_tm");
    434 
    435    ptime->year = (png_uint_16)(1900 + ttime->tm_year);
    436    ptime->month = (png_byte)(ttime->tm_mon + 1);
    437    ptime->day = (png_byte)ttime->tm_mday;
    438    ptime->hour = (png_byte)ttime->tm_hour;
    439    ptime->minute = (png_byte)ttime->tm_min;
    440    ptime->second = (png_byte)ttime->tm_sec;
    441 }
    442 
    443 void PNGAPI
    444 png_convert_from_time_t(png_timep ptime, time_t ttime)
    445 {
    446    struct tm *tbuf;
    447 
    448    png_debug(1, "in png_convert_from_time_t");
    449 
    450    tbuf = gmtime(&ttime);
    451    png_convert_from_struct_tm(ptime, tbuf);
    452 }
    453 #endif
    454 
    455 /* Initialize png_ptr structure, and allocate any memory needed */
    456 png_structp PNGAPI
    457 png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr,
    458    png_error_ptr error_fn, png_error_ptr warn_fn)
    459 {
    460 #ifdef PNG_USER_MEM_SUPPORTED
    461    return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
    462       warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL));
    463 }
    464 
    465 /* Alternate initialize png_ptr structure, and allocate any memory needed */
    466 png_structp PNGAPI
    467 png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr,
    468    png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
    469    png_malloc_ptr malloc_fn, png_free_ptr free_fn)
    470 {
    471 #endif /* PNG_USER_MEM_SUPPORTED */
    472 #ifdef PNG_SETJMP_SUPPORTED
    473    volatile
    474 #endif
    475    png_structp png_ptr;
    476 #ifdef PNG_SETJMP_SUPPORTED
    477 #ifdef USE_FAR_KEYWORD
    478    jmp_buf jmpbuf;
    479 #endif
    480 #endif
    481    int i;
    482 
    483    png_debug(1, "in png_create_write_struct");
    484 
    485 #ifdef PNG_USER_MEM_SUPPORTED
    486    png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
    487       (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr);
    488 #else
    489    png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
    490 #endif /* PNG_USER_MEM_SUPPORTED */
    491    if (png_ptr == NULL)
    492       return (NULL);
    493 
    494    /* Added at libpng-1.2.6 */
    495 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
    496    png_ptr->user_width_max = PNG_USER_WIDTH_MAX;
    497    png_ptr->user_height_max = PNG_USER_HEIGHT_MAX;
    498 #endif
    499 
    500 #ifdef PNG_SETJMP_SUPPORTED
    501 #ifdef USE_FAR_KEYWORD
    502    if (setjmp(jmpbuf))
    503 #else
    504    if (setjmp(png_ptr->jmpbuf))
    505 #endif
    506    {
    507       png_free(png_ptr, png_ptr->zbuf);
    508       png_ptr->zbuf = NULL;
    509 #ifdef PNG_USER_MEM_SUPPORTED
    510       png_destroy_struct_2((png_voidp)png_ptr,
    511          (png_free_ptr)free_fn, (png_voidp)mem_ptr);
    512 #else
    513       png_destroy_struct((png_voidp)png_ptr);
    514 #endif
    515       return (NULL);
    516    }
    517 #ifdef USE_FAR_KEYWORD
    518    png_memcpy(png_ptr->jmpbuf, jmpbuf, png_sizeof(jmp_buf));
    519 #endif
    520 #endif
    521 
    522 #ifdef PNG_USER_MEM_SUPPORTED
    523    png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
    524 #endif /* PNG_USER_MEM_SUPPORTED */
    525    png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
    526 
    527    if (user_png_ver)
    528    {
    529       i = 0;
    530       do
    531       {
    532          if (user_png_ver[i] != png_libpng_ver[i])
    533             png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
    534       } while (png_libpng_ver[i++]);
    535    }
    536 
    537    if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
    538    {
    539      /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
    540       * we must recompile any applications that use any older library version.
    541       * For versions after libpng 1.0, we will be compatible, so we need
    542       * only check the first digit.
    543       */
    544      if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
    545          (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
    546          (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
    547      {
    548 #if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE)
    549         char msg[80];
    550         if (user_png_ver)
    551         {
    552            png_snprintf(msg, 80,
    553               "Application was compiled with png.h from libpng-%.20s",
    554               user_png_ver);
    555            png_warning(png_ptr, msg);
    556         }
    557         png_snprintf(msg, 80,
    558            "Application  is  running with png.c from libpng-%.20s",
    559            png_libpng_ver);
    560         png_warning(png_ptr, msg);
    561 #endif
    562 #ifdef PNG_ERROR_NUMBERS_SUPPORTED
    563         png_ptr->flags = 0;
    564 #endif
    565         png_error(png_ptr,
    566            "Incompatible libpng version in application and library");
    567      }
    568    }
    569 
    570    /* Initialize zbuf - compression buffer */
    571    png_ptr->zbuf_size = PNG_ZBUF_SIZE;
    572    png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
    573      (png_uint_32)png_ptr->zbuf_size);
    574 
    575    png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL,
    576       png_flush_ptr_NULL);
    577 
    578 #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
    579    png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
    580       1, png_doublep_NULL, png_doublep_NULL);
    581 #endif
    582 
    583 #ifdef PNG_SETJMP_SUPPORTED
    584    /* Applications that neglect to set up their own setjmp() and then
    585     * encounter a png_error() will longjmp here.  Since the jmpbuf is
    586     * then meaningless we abort instead of returning.
    587     */
    588 #ifdef USE_FAR_KEYWORD
    589    if (setjmp(jmpbuf))
    590       PNG_ABORT();
    591    png_memcpy(png_ptr->jmpbuf, jmpbuf, png_sizeof(jmp_buf));
    592 #else
    593    if (setjmp(png_ptr->jmpbuf))
    594       PNG_ABORT();
    595 #endif
    596 #endif
    597    return (png_ptr);
    598 }
    599 
    600 /* Initialize png_ptr structure, and allocate any memory needed */
    601 #if defined(PNG_1_0_X) || defined(PNG_1_2_X)
    602 /* Deprecated. */
    603 #undef png_write_init
    604 void PNGAPI
    605 png_write_init(png_structp png_ptr)
    606 {
    607    /* We only come here via pre-1.0.7-compiled applications */
    608    png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0);
    609 }
    610 
    611 void PNGAPI
    612 png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver,
    613    png_size_t png_struct_size, png_size_t png_info_size)
    614 {
    615    /* We only come here via pre-1.0.12-compiled applications */
    616    if (png_ptr == NULL) return;
    617 #if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE)
    618    if (png_sizeof(png_struct) > png_struct_size ||
    619       png_sizeof(png_info) > png_info_size)
    620    {
    621       char msg[80];
    622       png_ptr->warning_fn = NULL;
    623       if (user_png_ver)
    624       {
    625          png_snprintf(msg, 80,
    626             "Application was compiled with png.h from libpng-%.20s",
    627             user_png_ver);
    628          png_warning(png_ptr, msg);
    629       }
    630       png_snprintf(msg, 80,
    631          "Application  is  running with png.c from libpng-%.20s",
    632          png_libpng_ver);
    633       png_warning(png_ptr, msg);
    634    }
    635 #endif
    636    if (png_sizeof(png_struct) > png_struct_size)
    637    {
    638       png_ptr->error_fn = NULL;
    639 #ifdef PNG_ERROR_NUMBERS_SUPPORTED
    640       png_ptr->flags = 0;
    641 #endif
    642       png_error(png_ptr,
    643       "The png struct allocated by the application for writing is"
    644       " too small.");
    645    }
    646    if (png_sizeof(png_info) > png_info_size)
    647    {
    648       png_ptr->error_fn = NULL;
    649 #ifdef PNG_ERROR_NUMBERS_SUPPORTED
    650       png_ptr->flags = 0;
    651 #endif
    652       png_error(png_ptr,
    653       "The info struct allocated by the application for writing is"
    654       " too small.");
    655    }
    656    png_write_init_3(&png_ptr, user_png_ver, png_struct_size);
    657 }
    658 #endif /* PNG_1_0_X || PNG_1_2_X */
    659 
    660 
    661 void PNGAPI
    662 png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver,
    663    png_size_t png_struct_size)
    664 {
    665    png_structp png_ptr = *ptr_ptr;
    666 #ifdef PNG_SETJMP_SUPPORTED
    667    jmp_buf tmp_jmp; /* to save current jump buffer */
    668 #endif
    669 
    670    int i = 0;
    671 
    672    if (png_ptr == NULL)
    673       return;
    674 
    675    do
    676    {
    677       if (user_png_ver[i] != png_libpng_ver[i])
    678       {
    679 #ifdef PNG_LEGACY_SUPPORTED
    680          png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
    681 #else
    682          png_ptr->warning_fn = NULL;
    683          png_warning(png_ptr,
    684  "Application uses deprecated png_write_init() and should be recompiled.");
    685 #endif
    686       }
    687    } while (png_libpng_ver[i++]);
    688 
    689    png_debug(1, "in png_write_init_3");
    690 
    691 #ifdef PNG_SETJMP_SUPPORTED
    692    /* Save jump buffer and error functions */
    693    png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf));
    694 #endif
    695 
    696    if (png_sizeof(png_struct) > png_struct_size)
    697    {
    698       png_destroy_struct(png_ptr);
    699       png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
    700       *ptr_ptr = png_ptr;
    701    }
    702 
    703    /* Reset all variables to 0 */
    704    png_memset(png_ptr, 0, png_sizeof(png_struct));
    705 
    706    /* Added at libpng-1.2.6 */
    707 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
    708    png_ptr->user_width_max = PNG_USER_WIDTH_MAX;
    709    png_ptr->user_height_max = PNG_USER_HEIGHT_MAX;
    710 #endif
    711 
    712 #ifdef PNG_SETJMP_SUPPORTED
    713    /* Restore jump buffer */
    714    png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf));
    715 #endif
    716 
    717    png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL,
    718       png_flush_ptr_NULL);
    719 
    720    /* Initialize zbuf - compression buffer */
    721    png_ptr->zbuf_size = PNG_ZBUF_SIZE;
    722    png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
    723      (png_uint_32)png_ptr->zbuf_size);
    724 #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
    725    png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
    726       1, png_doublep_NULL, png_doublep_NULL);
    727 #endif
    728 }
    729 
    730 /* Write a few rows of image data.  If the image is interlaced,
    731  * either you will have to write the 7 sub images, or, if you
    732  * have called png_set_interlace_handling(), you will have to
    733  * "write" the image seven times.
    734  */
    735 void PNGAPI
    736 png_write_rows(png_structp png_ptr, png_bytepp row,
    737    png_uint_32 num_rows)
    738 {
    739    png_uint_32 i; /* row counter */
    740    png_bytepp rp; /* row pointer */
    741 
    742    png_debug(1, "in png_write_rows");
    743 
    744    if (png_ptr == NULL)
    745       return;
    746 
    747    /* Loop through the rows */
    748    for (i = 0, rp = row; i < num_rows; i++, rp++)
    749    {
    750       png_write_row(png_ptr, *rp);
    751    }
    752 }
    753 
    754 /* Write the image.  You only need to call this function once, even
    755  * if you are writing an interlaced image.
    756  */
    757 void PNGAPI
    758 png_write_image(png_structp png_ptr, png_bytepp image)
    759 {
    760    png_uint_32 i; /* row index */
    761    int pass, num_pass; /* pass variables */
    762    png_bytepp rp; /* points to current row */
    763 
    764    if (png_ptr == NULL)
    765       return;
    766 
    767    png_debug(1, "in png_write_image");
    768 
    769 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
    770    /* Initialize interlace handling.  If image is not interlaced,
    771     * this will set pass to 1
    772     */
    773    num_pass = png_set_interlace_handling(png_ptr);
    774 #else
    775    num_pass = 1;
    776 #endif
    777    /* Loop through passes */
    778    for (pass = 0; pass < num_pass; pass++)
    779    {
    780       /* Loop through image */
    781       for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
    782       {
    783          png_write_row(png_ptr, *rp);
    784       }
    785    }
    786 }
    787 
    788 /* Called by user to write a row of image data */
    789 void PNGAPI
    790 png_write_row(png_structp png_ptr, png_bytep row)
    791 {
    792    if (png_ptr == NULL)
    793       return;
    794 
    795    png_debug2(1, "in png_write_row (row %ld, pass %d)",
    796       png_ptr->row_number, png_ptr->pass);
    797 
    798    /* Initialize transformations and other stuff if first time */
    799    if (png_ptr->row_number == 0 && png_ptr->pass == 0)
    800    {
    801       /* Make sure we wrote the header info */
    802       if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
    803          png_error(png_ptr,
    804             "png_write_info was never called before png_write_row.");
    805 
    806       /* Check for transforms that have been set but were defined out */
    807 #if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
    808       if (png_ptr->transformations & PNG_INVERT_MONO)
    809          png_warning(png_ptr,
    810              "PNG_WRITE_INVERT_SUPPORTED is not defined.");
    811 #endif
    812 #if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
    813       if (png_ptr->transformations & PNG_FILLER)
    814          png_warning(png_ptr,
    815              "PNG_WRITE_FILLER_SUPPORTED is not defined.");
    816 #endif
    817 #if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
    818     defined(PNG_READ_PACKSWAP_SUPPORTED)
    819       if (png_ptr->transformations & PNG_PACKSWAP)
    820          png_warning(png_ptr,
    821              "PNG_WRITE_PACKSWAP_SUPPORTED is not defined.");
    822 #endif
    823 #if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
    824       if (png_ptr->transformations & PNG_PACK)
    825          png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined.");
    826 #endif
    827 #if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
    828       if (png_ptr->transformations & PNG_SHIFT)
    829          png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined.");
    830 #endif
    831 #if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
    832       if (png_ptr->transformations & PNG_BGR)
    833          png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined.");
    834 #endif
    835 #if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
    836       if (png_ptr->transformations & PNG_SWAP_BYTES)
    837          png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined.");
    838 #endif
    839 
    840       png_write_start_row(png_ptr);
    841    }
    842 
    843 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
    844    /* If interlaced and not interested in row, return */
    845    if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
    846    {
    847       switch (png_ptr->pass)
    848       {
    849          case 0:
    850             if (png_ptr->row_number & 0x07)
    851             {
    852                png_write_finish_row(png_ptr);
    853                return;
    854             }
    855             break;
    856          case 1:
    857             if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
    858             {
    859                png_write_finish_row(png_ptr);
    860                return;
    861             }
    862             break;
    863          case 2:
    864             if ((png_ptr->row_number & 0x07) != 4)
    865             {
    866                png_write_finish_row(png_ptr);
    867                return;
    868             }
    869             break;
    870          case 3:
    871             if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)
    872             {
    873                png_write_finish_row(png_ptr);
    874                return;
    875             }
    876             break;
    877          case 4:
    878             if ((png_ptr->row_number & 0x03) != 2)
    879             {
    880                png_write_finish_row(png_ptr);
    881                return;
    882             }
    883             break;
    884          case 5:
    885             if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)
    886             {
    887                png_write_finish_row(png_ptr);
    888                return;
    889             }
    890             break;
    891          case 6:
    892             if (!(png_ptr->row_number & 0x01))
    893             {
    894                png_write_finish_row(png_ptr);
    895                return;
    896             }
    897             break;
    898       }
    899    }
    900 #endif
    901 
    902    /* Set up row info for transformations */
    903    png_ptr->row_info.color_type = png_ptr->color_type;
    904    png_ptr->row_info.width = png_ptr->usr_width;
    905    png_ptr->row_info.channels = png_ptr->usr_channels;
    906    png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
    907    png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
    908       png_ptr->row_info.channels);
    909 
    910    png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
    911       png_ptr->row_info.width);
    912 
    913    png_debug1(3, "row_info->color_type = %d", png_ptr->row_info.color_type);
    914    png_debug1(3, "row_info->width = %lu", png_ptr->row_info.width);
    915    png_debug1(3, "row_info->channels = %d", png_ptr->row_info.channels);
    916    png_debug1(3, "row_info->bit_depth = %d", png_ptr->row_info.bit_depth);
    917    png_debug1(3, "row_info->pixel_depth = %d", png_ptr->row_info.pixel_depth);
    918    png_debug1(3, "row_info->rowbytes = %lu", png_ptr->row_info.rowbytes);
    919 
    920    /* Copy user's row into buffer, leaving room for filter byte. */
    921    png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row,
    922       png_ptr->row_info.rowbytes);
    923 
    924 #ifdef PNG_WRITE_INTERLACING_SUPPORTED
    925    /* Handle interlacing */
    926    if (png_ptr->interlaced && png_ptr->pass < 6 &&
    927       (png_ptr->transformations & PNG_INTERLACE))
    928    {
    929       png_do_write_interlace(&(png_ptr->row_info),
    930          png_ptr->row_buf + 1, png_ptr->pass);
    931       /* This should always get caught above, but still ... */
    932       if (!(png_ptr->row_info.width))
    933       {
    934          png_write_finish_row(png_ptr);
    935          return;
    936       }
    937    }
    938 #endif
    939 
    940    /* Handle other transformations */
    941    if (png_ptr->transformations)
    942       png_do_write_transformations(png_ptr);
    943 
    944 #ifdef PNG_MNG_FEATURES_SUPPORTED
    945    /* Write filter_method 64 (intrapixel differencing) only if
    946     * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
    947     * 2. Libpng did not write a PNG signature (this filter_method is only
    948     *    used in PNG datastreams that are embedded in MNG datastreams) and
    949     * 3. The application called png_permit_mng_features with a mask that
    950     *    included PNG_FLAG_MNG_FILTER_64 and
    951     * 4. The filter_method is 64 and
    952     * 5. The color_type is RGB or RGBA
    953     */
    954    if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
    955       (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
    956    {
    957       /* Intrapixel differencing */
    958       png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1);
    959    }
    960 #endif
    961 
    962    /* Find a filter if necessary, filter the row and write it out. */
    963    png_write_find_filter(png_ptr, &(png_ptr->row_info));
    964 
    965    if (png_ptr->write_row_fn != NULL)
    966       (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
    967 }
    968 
    969 #ifdef PNG_WRITE_FLUSH_SUPPORTED
    970 /* Set the automatic flush interval or 0 to turn flushing off */
    971 void PNGAPI
    972 png_set_flush(png_structp png_ptr, int nrows)
    973 {
    974    png_debug(1, "in png_set_flush");
    975 
    976    if (png_ptr == NULL)
    977       return;
    978    png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
    979 }
    980 
    981 /* Flush the current output buffers now */
    982 void PNGAPI
    983 png_write_flush(png_structp png_ptr)
    984 {
    985    int wrote_IDAT;
    986 
    987    png_debug(1, "in png_write_flush");
    988 
    989    if (png_ptr == NULL)
    990       return;
    991    /* We have already written out all of the data */
    992    if (png_ptr->row_number >= png_ptr->num_rows)
    993       return;
    994 
    995    do
    996    {
    997       int ret;
    998 
    999       /* Compress the data */
   1000       ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
   1001       wrote_IDAT = 0;
   1002 
   1003       /* Check for compression errors */
   1004       if (ret != Z_OK)
   1005       {
   1006          if (png_ptr->zstream.msg != NULL)
   1007             png_error(png_ptr, png_ptr->zstream.msg);
   1008          else
   1009             png_error(png_ptr, "zlib error");
   1010       }
   1011 
   1012       if (!(png_ptr->zstream.avail_out))
   1013       {
   1014          /* Write the IDAT and reset the zlib output buffer */
   1015          png_write_IDAT(png_ptr, png_ptr->zbuf,
   1016                         png_ptr->zbuf_size);
   1017          png_ptr->zstream.next_out = png_ptr->zbuf;
   1018          png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
   1019          wrote_IDAT = 1;
   1020       }
   1021    } while(wrote_IDAT == 1);
   1022 
   1023    /* If there is any data left to be output, write it into a new IDAT */
   1024    if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
   1025    {
   1026       /* Write the IDAT and reset the zlib output buffer */
   1027       png_write_IDAT(png_ptr, png_ptr->zbuf,
   1028                      png_ptr->zbuf_size - png_ptr->zstream.avail_out);
   1029       png_ptr->zstream.next_out = png_ptr->zbuf;
   1030       png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
   1031    }
   1032    png_ptr->flush_rows = 0;
   1033    png_flush(png_ptr);
   1034 }
   1035 #endif /* PNG_WRITE_FLUSH_SUPPORTED */
   1036 
   1037 /* Free all memory used by the write */
   1038 void PNGAPI
   1039 png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
   1040 {
   1041    png_structp png_ptr = NULL;
   1042    png_infop info_ptr = NULL;
   1043 #ifdef PNG_USER_MEM_SUPPORTED
   1044    png_free_ptr free_fn = NULL;
   1045    png_voidp mem_ptr = NULL;
   1046 #endif
   1047 
   1048    png_debug(1, "in png_destroy_write_struct");
   1049 
   1050    if (png_ptr_ptr != NULL)
   1051    {
   1052       png_ptr = *png_ptr_ptr;
   1053 #ifdef PNG_USER_MEM_SUPPORTED
   1054       free_fn = png_ptr->free_fn;
   1055       mem_ptr = png_ptr->mem_ptr;
   1056 #endif
   1057    }
   1058 
   1059 #ifdef PNG_USER_MEM_SUPPORTED
   1060    if (png_ptr != NULL)
   1061    {
   1062       free_fn = png_ptr->free_fn;
   1063       mem_ptr = png_ptr->mem_ptr;
   1064    }
   1065 #endif
   1066 
   1067    if (info_ptr_ptr != NULL)
   1068       info_ptr = *info_ptr_ptr;
   1069 
   1070    if (info_ptr != NULL)
   1071    {
   1072       if (png_ptr != NULL)
   1073       {
   1074         png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
   1075 
   1076 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
   1077         if (png_ptr->num_chunk_list)
   1078         {
   1079            png_free(png_ptr, png_ptr->chunk_list);
   1080            png_ptr->chunk_list = NULL;
   1081            png_ptr->num_chunk_list = 0;
   1082         }
   1083 #endif
   1084       }
   1085 
   1086 #ifdef PNG_USER_MEM_SUPPORTED
   1087       png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn,
   1088          (png_voidp)mem_ptr);
   1089 #else
   1090       png_destroy_struct((png_voidp)info_ptr);
   1091 #endif
   1092       *info_ptr_ptr = NULL;
   1093    }
   1094 
   1095    if (png_ptr != NULL)
   1096    {
   1097       png_write_destroy(png_ptr);
   1098 #ifdef PNG_USER_MEM_SUPPORTED
   1099       png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn,
   1100          (png_voidp)mem_ptr);
   1101 #else
   1102       png_destroy_struct((png_voidp)png_ptr);
   1103 #endif
   1104       *png_ptr_ptr = NULL;
   1105    }
   1106 }
   1107 
   1108 
   1109 /* Free any memory used in png_ptr struct (old method) */
   1110 void /* PRIVATE */
   1111 png_write_destroy(png_structp png_ptr)
   1112 {
   1113 #ifdef PNG_SETJMP_SUPPORTED
   1114    jmp_buf tmp_jmp; /* Save jump buffer */
   1115 #endif
   1116    png_error_ptr error_fn;
   1117    png_error_ptr warning_fn;
   1118    png_voidp error_ptr;
   1119 #ifdef PNG_USER_MEM_SUPPORTED
   1120    png_free_ptr free_fn;
   1121 #endif
   1122 
   1123    png_debug(1, "in png_write_destroy");
   1124 
   1125    /* Free any memory zlib uses */
   1126    deflateEnd(&png_ptr->zstream);
   1127 
   1128    /* Free our memory.  png_free checks NULL for us. */
   1129    png_free(png_ptr, png_ptr->zbuf);
   1130    png_free(png_ptr, png_ptr->row_buf);
   1131 #ifdef PNG_WRITE_FILTER_SUPPORTED
   1132    png_free(png_ptr, png_ptr->prev_row);
   1133    png_free(png_ptr, png_ptr->sub_row);
   1134    png_free(png_ptr, png_ptr->up_row);
   1135    png_free(png_ptr, png_ptr->avg_row);
   1136    png_free(png_ptr, png_ptr->paeth_row);
   1137 #endif
   1138 
   1139 #ifdef PNG_TIME_RFC1123_SUPPORTED
   1140    png_free(png_ptr, png_ptr->time_buffer);
   1141 #endif
   1142 
   1143 #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
   1144    png_free(png_ptr, png_ptr->prev_filters);
   1145    png_free(png_ptr, png_ptr->filter_weights);
   1146    png_free(png_ptr, png_ptr->inv_filter_weights);
   1147    png_free(png_ptr, png_ptr->filter_costs);
   1148    png_free(png_ptr, png_ptr->inv_filter_costs);
   1149 #endif
   1150 
   1151 #ifdef PNG_SETJMP_SUPPORTED
   1152    /* Reset structure */
   1153    png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf));
   1154 #endif
   1155 
   1156    error_fn = png_ptr->error_fn;
   1157    warning_fn = png_ptr->warning_fn;
   1158    error_ptr = png_ptr->error_ptr;
   1159 #ifdef PNG_USER_MEM_SUPPORTED
   1160    free_fn = png_ptr->free_fn;
   1161 #endif
   1162 
   1163    png_memset(png_ptr, 0, png_sizeof(png_struct));
   1164 
   1165    png_ptr->error_fn = error_fn;
   1166    png_ptr->warning_fn = warning_fn;
   1167    png_ptr->error_ptr = error_ptr;
   1168 #ifdef PNG_USER_MEM_SUPPORTED
   1169    png_ptr->free_fn = free_fn;
   1170 #endif
   1171 
   1172 #ifdef PNG_SETJMP_SUPPORTED
   1173    png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf));
   1174 #endif
   1175 }
   1176 
   1177 /* Allow the application to select one or more row filters to use. */
   1178 void PNGAPI
   1179 png_set_filter(png_structp png_ptr, int method, int filters)
   1180 {
   1181    png_debug(1, "in png_set_filter");
   1182 
   1183    if (png_ptr == NULL)
   1184       return;
   1185 #ifdef PNG_MNG_FEATURES_SUPPORTED
   1186    if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
   1187       (method == PNG_INTRAPIXEL_DIFFERENCING))
   1188          method = PNG_FILTER_TYPE_BASE;
   1189 #endif
   1190    if (method == PNG_FILTER_TYPE_BASE)
   1191    {
   1192       switch (filters & (PNG_ALL_FILTERS | 0x07))
   1193       {
   1194 #ifdef PNG_WRITE_FILTER_SUPPORTED
   1195          case 5:
   1196          case 6:
   1197          case 7: png_warning(png_ptr, "Unknown row filter for method 0");
   1198 #endif /* PNG_WRITE_FILTER_SUPPORTED */
   1199          case PNG_FILTER_VALUE_NONE:
   1200               png_ptr->do_filter = PNG_FILTER_NONE; break;
   1201 #ifdef PNG_WRITE_FILTER_SUPPORTED
   1202          case PNG_FILTER_VALUE_SUB:
   1203               png_ptr->do_filter = PNG_FILTER_SUB; break;
   1204          case PNG_FILTER_VALUE_UP:
   1205               png_ptr->do_filter = PNG_FILTER_UP; break;
   1206          case PNG_FILTER_VALUE_AVG:
   1207               png_ptr->do_filter = PNG_FILTER_AVG; break;
   1208          case PNG_FILTER_VALUE_PAETH:
   1209               png_ptr->do_filter = PNG_FILTER_PAETH; break;
   1210          default: png_ptr->do_filter = (png_byte)filters; break;
   1211 #else
   1212          default: png_warning(png_ptr, "Unknown row filter for method 0");
   1213 #endif /* PNG_WRITE_FILTER_SUPPORTED */
   1214       }
   1215 
   1216       /* If we have allocated the row_buf, this means we have already started
   1217        * with the image and we should have allocated all of the filter buffers
   1218        * that have been selected.  If prev_row isn't already allocated, then
   1219        * it is too late to start using the filters that need it, since we
   1220        * will be missing the data in the previous row.  If an application
   1221        * wants to start and stop using particular filters during compression,
   1222        * it should start out with all of the filters, and then add and
   1223        * remove them after the start of compression.
   1224        */
   1225       if (png_ptr->row_buf != NULL)
   1226       {
   1227 #ifdef PNG_WRITE_FILTER_SUPPORTED
   1228          if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)
   1229          {
   1230             png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
   1231               (png_ptr->rowbytes + 1));
   1232             png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
   1233          }
   1234 
   1235          if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)
   1236          {
   1237             if (png_ptr->prev_row == NULL)
   1238             {
   1239                png_warning(png_ptr, "Can't add Up filter after starting");
   1240                png_ptr->do_filter &= ~PNG_FILTER_UP;
   1241             }
   1242             else
   1243             {
   1244                png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
   1245                   (png_ptr->rowbytes + 1));
   1246                png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
   1247             }
   1248          }
   1249 
   1250          if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)
   1251          {
   1252             if (png_ptr->prev_row == NULL)
   1253             {
   1254                png_warning(png_ptr, "Can't add Average filter after starting");
   1255                png_ptr->do_filter &= ~PNG_FILTER_AVG;
   1256             }
   1257             else
   1258             {
   1259                png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
   1260                   (png_ptr->rowbytes + 1));
   1261                png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
   1262             }
   1263          }
   1264 
   1265          if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&
   1266              png_ptr->paeth_row == NULL)
   1267          {
   1268             if (png_ptr->prev_row == NULL)
   1269             {
   1270                png_warning(png_ptr, "Can't add Paeth filter after starting");
   1271                png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
   1272             }
   1273             else
   1274             {
   1275                png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
   1276                   (png_ptr->rowbytes + 1));
   1277                png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
   1278             }
   1279          }
   1280 
   1281          if (png_ptr->do_filter == PNG_NO_FILTERS)
   1282 #endif /* PNG_WRITE_FILTER_SUPPORTED */
   1283             png_ptr->do_filter = PNG_FILTER_NONE;
   1284       }
   1285    }
   1286    else
   1287       png_error(png_ptr, "Unknown custom filter method");
   1288 }
   1289 
   1290 /* This allows us to influence the way in which libpng chooses the "best"
   1291  * filter for the current scanline.  While the "minimum-sum-of-absolute-
   1292  * differences metric is relatively fast and effective, there is some
   1293  * question as to whether it can be improved upon by trying to keep the
   1294  * filtered data going to zlib more consistent, hopefully resulting in
   1295  * better compression.
   1296  */
   1297 #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED      /* GRR 970116 */
   1298 void PNGAPI
   1299 png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
   1300    int num_weights, png_doublep filter_weights,
   1301    png_doublep filter_costs)
   1302 {
   1303    int i;
   1304 
   1305    png_debug(1, "in png_set_filter_heuristics");
   1306 
   1307    if (png_ptr == NULL)
   1308       return;
   1309    if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST)
   1310    {
   1311       png_warning(png_ptr, "Unknown filter heuristic method");
   1312       return;
   1313    }
   1314 
   1315    if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT)
   1316    {
   1317       heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
   1318    }
   1319 
   1320    if (num_weights < 0 || filter_weights == NULL ||
   1321       heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
   1322    {
   1323       num_weights = 0;
   1324    }
   1325 
   1326    png_ptr->num_prev_filters = (png_byte)num_weights;
   1327    png_ptr->heuristic_method = (png_byte)heuristic_method;
   1328 
   1329    if (num_weights > 0)
   1330    {
   1331       if (png_ptr->prev_filters == NULL)
   1332       {
   1333          png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
   1334             (png_uint_32)(png_sizeof(png_byte) * num_weights));
   1335 
   1336          /* To make sure that the weighting starts out fairly */
   1337          for (i = 0; i < num_weights; i++)
   1338          {
   1339             png_ptr->prev_filters[i] = 255;
   1340          }
   1341       }
   1342 
   1343       if (png_ptr->filter_weights == NULL)
   1344       {
   1345          png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
   1346             (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
   1347 
   1348          png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
   1349             (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
   1350          for (i = 0; i < num_weights; i++)
   1351          {
   1352             png_ptr->inv_filter_weights[i] =
   1353             png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
   1354          }
   1355       }
   1356 
   1357       for (i = 0; i < num_weights; i++)
   1358       {
   1359          if (filter_weights[i] < 0.0)
   1360          {
   1361             png_ptr->inv_filter_weights[i] =
   1362             png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
   1363          }
   1364          else
   1365          {
   1366             png_ptr->inv_filter_weights[i] =
   1367                (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5);
   1368             png_ptr->filter_weights[i] =
   1369                (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5);
   1370          }
   1371       }
   1372    }
   1373 
   1374    /* If, in the future, there are other filter methods, this would
   1375     * need to be based on png_ptr->filter.
   1376     */
   1377    if (png_ptr->filter_costs == NULL)
   1378    {
   1379       png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
   1380          (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
   1381 
   1382       png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
   1383          (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
   1384 
   1385       for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
   1386       {
   1387          png_ptr->inv_filter_costs[i] =
   1388          png_ptr->filter_costs[i] = PNG_COST_FACTOR;
   1389       }
   1390    }
   1391 
   1392    /* Here is where we set the relative costs of the different filters.  We
   1393     * should take the desired compression level into account when setting
   1394     * the costs, so that Paeth, for instance, has a high relative cost at low
   1395     * compression levels, while it has a lower relative cost at higher
   1396     * compression settings.  The filter types are in order of increasing
   1397     * relative cost, so it would be possible to do this with an algorithm.
   1398     */
   1399    for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
   1400    {
   1401       if (filter_costs == NULL || filter_costs[i] < 0.0)
   1402       {
   1403          png_ptr->inv_filter_costs[i] =
   1404          png_ptr->filter_costs[i] = PNG_COST_FACTOR;
   1405       }
   1406       else if (filter_costs[i] >= 1.0)
   1407       {
   1408          png_ptr->inv_filter_costs[i] =
   1409             (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5);
   1410          png_ptr->filter_costs[i] =
   1411             (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5);
   1412       }
   1413    }
   1414 }
   1415 #endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
   1416 
   1417 void PNGAPI
   1418 png_set_compression_level(png_structp png_ptr, int level)
   1419 {
   1420    png_debug(1, "in png_set_compression_level");
   1421 
   1422    if (png_ptr == NULL)
   1423       return;
   1424    png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
   1425    png_ptr->zlib_level = level;
   1426 }
   1427 
   1428 void PNGAPI
   1429 png_set_compression_mem_level(png_structp png_ptr, int mem_level)
   1430 {
   1431    png_debug(1, "in png_set_compression_mem_level");
   1432 
   1433    if (png_ptr == NULL)
   1434       return;
   1435    png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
   1436    png_ptr->zlib_mem_level = mem_level;
   1437 }
   1438 
   1439 void PNGAPI
   1440 png_set_compression_strategy(png_structp png_ptr, int strategy)
   1441 {
   1442    png_debug(1, "in png_set_compression_strategy");
   1443 
   1444    if (png_ptr == NULL)
   1445       return;
   1446    png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
   1447    png_ptr->zlib_strategy = strategy;
   1448 }
   1449 
   1450 void PNGAPI
   1451 png_set_compression_window_bits(png_structp png_ptr, int window_bits)
   1452 {
   1453    if (png_ptr == NULL)
   1454       return;
   1455    if (window_bits > 15)
   1456       png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
   1457    else if (window_bits < 8)
   1458       png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
   1459 #ifndef WBITS_8_OK
   1460    /* Avoid libpng bug with 256-byte windows */
   1461    if (window_bits == 8)
   1462      {
   1463        png_warning(png_ptr, "Compression window is being reset to 512");
   1464        window_bits = 9;
   1465      }
   1466 #endif
   1467    png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
   1468    png_ptr->zlib_window_bits = window_bits;
   1469 }
   1470 
   1471 void PNGAPI
   1472 png_set_compression_method(png_structp png_ptr, int method)
   1473 {
   1474    png_debug(1, "in png_set_compression_method");
   1475 
   1476    if (png_ptr == NULL)
   1477       return;
   1478    if (method != 8)
   1479       png_warning(png_ptr, "Only compression method 8 is supported by PNG");
   1480    png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
   1481    png_ptr->zlib_method = method;
   1482 }
   1483 
   1484 void PNGAPI
   1485 png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
   1486 {
   1487    if (png_ptr == NULL)
   1488       return;
   1489    png_ptr->write_row_fn = write_row_fn;
   1490 }
   1491 
   1492 #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
   1493 void PNGAPI
   1494 png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
   1495    write_user_transform_fn)
   1496 {
   1497    png_debug(1, "in png_set_write_user_transform_fn");
   1498 
   1499    if (png_ptr == NULL)
   1500       return;
   1501    png_ptr->transformations |= PNG_USER_TRANSFORM;
   1502    png_ptr->write_user_transform_fn = write_user_transform_fn;
   1503 }
   1504 #endif
   1505 
   1506 
   1507 #ifdef PNG_INFO_IMAGE_SUPPORTED
   1508 void PNGAPI
   1509 png_write_png(png_structp png_ptr, png_infop info_ptr,
   1510               int transforms, voidp params)
   1511 {
   1512    if (png_ptr == NULL || info_ptr == NULL)
   1513       return;
   1514 
   1515    /* Write the file header information. */
   1516    png_write_info(png_ptr, info_ptr);
   1517 
   1518    /* ------ these transformations don't touch the info structure ------- */
   1519 
   1520 #ifdef PNG_WRITE_INVERT_SUPPORTED
   1521    /* Invert monochrome pixels */
   1522    if (transforms & PNG_TRANSFORM_INVERT_MONO)
   1523       png_set_invert_mono(png_ptr);
   1524 #endif
   1525 
   1526 #ifdef PNG_WRITE_SHIFT_SUPPORTED
   1527    /* Shift the pixels up to a legal bit depth and fill in
   1528     * as appropriate to correctly scale the image.
   1529     */
   1530    if ((transforms & PNG_TRANSFORM_SHIFT)
   1531                && (info_ptr->valid & PNG_INFO_sBIT))
   1532       png_set_shift(png_ptr, &info_ptr->sig_bit);
   1533 #endif
   1534 
   1535 #ifdef PNG_WRITE_PACK_SUPPORTED
   1536    /* Pack pixels into bytes */
   1537    if (transforms & PNG_TRANSFORM_PACKING)
   1538        png_set_packing(png_ptr);
   1539 #endif
   1540 
   1541 #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
   1542    /* Swap location of alpha bytes from ARGB to RGBA */
   1543    if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
   1544       png_set_swap_alpha(png_ptr);
   1545 #endif
   1546 
   1547 #ifdef PNG_WRITE_FILLER_SUPPORTED
   1548    /* Pack XRGB/RGBX/ARGB/RGBA into * RGB (4 channels -> 3 channels) */
   1549    if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)
   1550       png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
   1551    else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)
   1552       png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
   1553 #endif
   1554 
   1555 #ifdef PNG_WRITE_BGR_SUPPORTED
   1556    /* Flip BGR pixels to RGB */
   1557    if (transforms & PNG_TRANSFORM_BGR)
   1558       png_set_bgr(png_ptr);
   1559 #endif
   1560 
   1561 #ifdef PNG_WRITE_SWAP_SUPPORTED
   1562    /* Swap bytes of 16-bit files to most significant byte first */
   1563    if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
   1564       png_set_swap(png_ptr);
   1565 #endif
   1566 
   1567 #ifdef PNG_WRITE_PACKSWAP_SUPPORTED
   1568    /* Swap bits of 1, 2, 4 bit packed pixel formats */
   1569    if (transforms & PNG_TRANSFORM_PACKSWAP)
   1570       png_set_packswap(png_ptr);
   1571 #endif
   1572 
   1573 #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
   1574    /* Invert the alpha channel from opacity to transparency */
   1575    if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
   1576       png_set_invert_alpha(png_ptr);
   1577 #endif
   1578 
   1579    /* ----------------------- end of transformations ------------------- */
   1580 
   1581    /* Write the bits */
   1582    if (info_ptr->valid & PNG_INFO_IDAT)
   1583        png_write_image(png_ptr, info_ptr->row_pointers);
   1584 
   1585    /* It is REQUIRED to call this to finish writing the rest of the file */
   1586    png_write_end(png_ptr, info_ptr);
   1587 
   1588    transforms = transforms; /* Quiet compiler warnings */
   1589    params = params;
   1590 }
   1591 #endif
   1592 #endif /* PNG_WRITE_SUPPORTED */
   1593