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