example.c 17.8 KB
Newer Older
G
Guy Schalnat 已提交
1 2 3 4 5 6 7 8
/* example.c - an example of using libpng */

/* this is an example of how to use libpng to read and write
   png files.  The file libpng.txt is much more verbose then
   this.  If you have not read it, do so first.  This was
   designed to be a starting point of an implementation.
   This is not officially part of libpng, and therefore
   does not require a copyright notice.
G
Guy Schalnat 已提交
9 10 11 12

   This file does not currently compile, because it is missing
   certain parts, like allocating memory to hold an image.
   You will have to supply these parts to get it to compile.
G
Guy Schalnat 已提交
13 14 15 16
   */

#include <png.h>

A
Andreas Dilger 已提交
17 18 19 20 21 22 23 24 25 26 27
/* Check to see if a file is a png file using png_check_sig().

   If this call is successful, and you are going to keep the file
   open, you should call png_set_sig_bytes_read(png_ptr, 8);
   once you have created the png_ptr, so that libpng knows it
   doesn't have to read the signature again.  Make sure you don't
   call png_set_sig_bytes_read() with more than 8 bytes read or
   give it an incorrect number of bytes read, or you will either
   have read too many bytes (your fault), or you are telling libpng
   to read the wrong number of magic bytes (also your fault). */
int check_png(char *file_name, FILE **fp)
G
Guy Schalnat 已提交
28 29 30 31
{
   char buf[8];
   int ret;

A
Andreas Dilger 已提交
32
   *fp = fopen(file_name, "rb");
G
Guy Schalnat 已提交
33 34
   if (!fp)
      return 0;
A
Andreas Dilger 已提交
35
   ret = fread(buf, 1, 8, *fp);
G
Guy Schalnat 已提交
36 37 38 39

   if (ret != 8)
      return 0;

A
Andreas Dilger 已提交
40 41
   /* Check the signature starting at byte 0, and check all 8 bytes */
   ret = png_check_sig(buf, 0, 8);
G
Guy Schalnat 已提交
42 43 44 45 46

   return (ret);
}

/* read a png file.  You may want to return an error code if the read
A
Andreas Dilger 已提交
47 48 49 50 51 52 53
   fails (depending upon the failure).  There are two "prototypes" given
   here - one where we are given the filename, and we need to open the
   file, and the other where we are given an open file (possibly with
   some or all of the magic bytes read - see above) and an opened file
   for reading. */
------- prototype 1 ----------
void read_png(char *file_name)  /* We need to open the file */
G
Guy Schalnat 已提交
54
{
G
Guy Schalnat 已提交
55
   png_structp png_ptr;
G
Guy Schalnat 已提交
56
   png_infop info_ptr;
A
Andreas Dilger 已提交
57
   FILE *fp;
G
Guy Schalnat 已提交
58

A
Andreas Dilger 已提交
59
   if ((fp = fopen(file_name, "rb")) == NULL)
G
Guy Schalnat 已提交
60
      return;
A
Andreas Dilger 已提交
61 62 63 64 65 66
------- prototype 2 ----------
void read_png(FILE *fp, unsigned int sig_read)  /* file is already open */
{
   png_structp png_ptr;
   png_infop info_ptr;
------- only use one! --------
G
Guy Schalnat 已提交
67

G
Guy Schalnat 已提交
68 69 70
   /* Create and initialize the png_struct with the desired error handler
      functions.  If you want to use the default stderr and longjump method,
      you can supply NULL for the last three parameters.  We also check that
A
Andreas Dilger 已提交
71
      the header file is compatible with the library version.  */
G
Guy Schalnat 已提交
72 73 74
   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
      (void *)user_error_ptr, user_error_fn, user_warning_fn);

G
Guy Schalnat 已提交
75 76 77 78 79 80
   if (!png_ptr)
   {
      fclose(fp);
      return;
   }

G
Guy Schalnat 已提交
81
   info_ptr = png_create_info_struct();
G
Guy Schalnat 已提交
82 83 84
   if (!info_ptr)
   {
      fclose(fp);
G
Guy Schalnat 已提交
85
      png_destroy_read_struct(&png_ptr,  (png_infopp)NULL, (png_infopp)NULL);
G
Guy Schalnat 已提交
86 87 88
      return;
   }

G
Guy Schalnat 已提交
89
   /* set error handling if you are using the setjmp/longjmp method */
G
Guy Schalnat 已提交
90 91
   if (setjmp(png_ptr->jmpbuf))
   {
G
Guy Schalnat 已提交
92 93
      /* Free all of the memory associated with the png_ptr and info_ptr */
      png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
G
Guy Schalnat 已提交
94 95 96 97 98
      fclose(fp);
      /* If we get here, we had a problem reading the file */
      return;
   }

G
Guy Schalnat 已提交
99
   /* set up the input control if you are using standard C streams */
G
Guy Schalnat 已提交
100 101
   png_init_io(png_ptr, fp);

G
Guy Schalnat 已提交
102 103 104 105
   /* if you are using replacement read functions, instead of calling
      png_init_io() here you would call */
   png_set_read_fn(png_ptr, (void *)user_io_ptr, user_read_fn);
   /* where user_io_ptr is a structure you want available to the callbacks */
G
Guy Schalnat 已提交
106

A
Andreas Dilger 已提交
107 108 109 110 111
   /* if we have already read some of the signature from the beginning call */
   png_set_sig_bytes_read(png_ptr, sig_read);

   /* The call to png_read_info() gives us all of the information
      from the PNG file before the first IDAT (image data chunk). */
G
Guy Schalnat 已提交
112
   png_read_info(png_ptr, info_ptr);
G
Guy Schalnat 已提交
113

G
Guy Schalnat 已提交
114
   /* set up the transformations you want.  Note that these are
G
Guy Schalnat 已提交
115 116
      all optional.  Only call them if you want them */

G
Guy Schalnat 已提交
117
   /* expand paletted colors into true RGB triplets */
G
Guy Schalnat 已提交
118
   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
G
Guy Schalnat 已提交
119 120 121
      png_set_expand(png_ptr);

   /* expand grayscale images to the full 8 bits */
G
Guy Schalnat 已提交
122
   if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY && info_ptr->bit_depth < 8)
G
Guy Schalnat 已提交
123 124
      png_set_expand(png_ptr);

G
Guy Schalnat 已提交
125 126
   /* expand paletted or RGB images with transparency to full alpha channels
    * so the data will be available as RGBA quartets */
G
Guy Schalnat 已提交
127 128 129 130
   if (info_ptr->valid & PNG_INFO_tRNS)
      png_set_expand(png_ptr);

   /* Set the background color to draw transparent and alpha
G
Guy Schalnat 已提交
131 132 133
      images over.  It is possible to set the red, green, and blue
      components directly for paletted images. */

G
Guy Schalnat 已提交
134 135 136 137
   png_color_16 my_background;

   if (info_ptr->valid & PNG_INFO_bKGD)
      png_set_background(png_ptr, &(info_ptr->background),
G
Guy Schalnat 已提交
138
                         PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
G
Guy Schalnat 已提交
139 140
   else
      png_set_background(png_ptr, &my_background,
G
Guy Schalnat 已提交
141
                         PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
G
Guy Schalnat 已提交
142

A
Andreas Dilger 已提交
143 144 145 146
   /* tell libpng to handle the gamma conversion for you.  We only
      need the second call if the screen_gamma isn't the usual 2.2
      or if it is controllable by the user.  It may also be a good
      idea to allow the user to set the file gamma if it is unknown. */
G
Guy Schalnat 已提交
147 148 149 150 151
   if (info_ptr->valid & PNG_INFO_gAMA)
      png_set_gamma(png_ptr, screen_gamma, info_ptr->gamma);
   else
      png_set_gamma(png_ptr, screen_gamma, 0.45);

G
Guy Schalnat 已提交
152
   /* tell libpng to strip 16 bit/color files down to 8 bits/color */
G
Guy Schalnat 已提交
153
   if (info_ptr->bit_depth == 16)
G
Guy Schalnat 已提交
154
      png_set_strip_16(png_ptr);
G
Guy Schalnat 已提交
155

G
Guy Schalnat 已提交
156
   /* dither rgb files down to 8 bit palette & reduce palettes
G
Guy Schalnat 已提交
157 158 159 160
      to the number of colors available on your screen */
   if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
   {
      if (info_ptr->valid & PNG_INFO_PLTE)
G
Guy Schalnat 已提交
161 162
         png_set_dither(png_ptr, info_ptr->palette, info_ptr->num_palette,
                        max_screen_colors, info_ptr->histogram);
G
Guy Schalnat 已提交
163 164 165 166 167 168 169 170
      else
      {
         png_color std_color_cube[MAX_SCREEN_COLORS] =
            {/* ... colors ... */};

         png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS,
            MAX_SCREEN_COLORS, NULL);
      }
G
Guy Schalnat 已提交
171
   }
G
Guy Schalnat 已提交
172

G
Guy Schalnat 已提交
173 174
   /* invert monocrome files to have 0 as white and 1 as black */
   if (info_ptr->bit_depth == 1 && info_ptr->color_type == PNG_COLOR_GRAY)
G
Guy Schalnat 已提交
175 176 177
      png_set_invert(png_ptr);

   /* shift the pixels down to their true bit depth */
G
Guy Schalnat 已提交
178 179
   if (info_ptr->valid & PNG_INFO_sBIT &&
      info_ptr->bit_depth > info_ptr->sig_bit)
G
Guy Schalnat 已提交
180 181
      png_set_shift(png_ptr, &(info_ptr->sig_bit));

G
Guy Schalnat 已提交
182 183
   /* pack multiple pixels with bit depths of 1, 2, and 4 into bytes
      (useful only for paletted and grayscale images) */
G
Guy Schalnat 已提交
184 185 186 187 188
   if (info_ptr->bit_depth < 8)
      png_set_packing(png_ptr);

   /* flip the rgb pixels to bgr */
   if (info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
G
Guy Schalnat 已提交
189
      info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
G
Guy Schalnat 已提交
190 191 192 193 194 195
      png_set_bgr(png_ptr);

   /* swap bytes of 16 bit files to least significant bit first */
   if (info_ptr->bit_depth == 16)
      png_set_swap(png_ptr);

G
Guy Schalnat 已提交
196 197
   /* add a filler byte to RGB files (before or after each RGB triplet) */
   if (info_ptr->bit_depth == 8 && info_ptr->color_type == PNG_COLOR_TYPE_RGB)
G
Guy Schalnat 已提交
198 199 200
      png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);

   /* turn on interlace handling if you are not using png_read_image() */
G
Guy Schalnat 已提交
201
   number_passes = png_set_interlace_handling(png_ptr);
G
Guy Schalnat 已提交
202

G
Guy Schalnat 已提交
203 204
   /* optional call to gamma correct and add the background to the palette
      and update info structure. */
G
Guy Schalnat 已提交
205 206 207 208 209
   png_read_update_info(png_ptr, info_ptr);

   /* allocate the memory to hold the image using the fields
      of png_info. */

G
Guy Schalnat 已提交
210
   /* the easiest way to read the image */
G
Guy Schalnat 已提交
211
   png_bytep row_pointers[height];
G
Guy Schalnat 已提交
212 213 214 215 216 217

   for (row = 0; row < height; row++)
   {
     row_pointers[row] = malloc(info_ptr->rowbytes);
   }

G
Guy Schalnat 已提交
218 219 220 221 222 223 224
   png_read_image(png_ptr, row_pointers);

   /* the other way to read images - deal with interlacing */

   for (pass = 0; pass < number_passes; pass++)
   {
      /* Read the image using the "sparkle" effect. */
G
Guy Schalnat 已提交
225
      png_read_rows(png_ptr, row_pointers, NULL, number_of_rows);
G
Guy Schalnat 已提交
226 227 228 229

      /* If you are only reading on row at a time, this works */
      for (y = 0; y < height; y++)
      {
G
Guy Schalnat 已提交
230
         png_bytep row_pointers = row[y];
G
Guy Schalnat 已提交
231 232 233 234 235 236 237 238 239 240
         png_read_rows(png_ptr, &row_pointers, NULL, 1);
      }

      /* to get the rectangle effect, use the third parameter */
      png_read_rows(png_ptr, NULL, row_pointers, number_of_rows);

      /* if you want to display the image after every pass, do
         so here */
   }

G
Guy Schalnat 已提交
241
   /* read the rest of the file, getting any additional chunks in info_ptr */
G
Guy Schalnat 已提交
242
   png_read_end(png_ptr, info_ptr);
G
Guy Schalnat 已提交
243 244

   /* clean up after the read, and free any memory allocated */
G
Guy Schalnat 已提交
245
   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
G
Guy Schalnat 已提交
246 247 248 249 250 251 252 253

   /* close the file */
   fclose(fp);

   /* that's it */
   return;
}

G
Guy Schalnat 已提交
254 255 256
/* progressively read a file */

int
G
Guy Schalnat 已提交
257
initialize_png_reader(png_structp *png_ptr, png_infop *info_ptr)
G
Guy Schalnat 已提交
258
{
G
Guy Schalnat 已提交
259 260 261 262 263 264 265 266 267 268
   /* Create and initialize the png_struct with the desired error handler
      functions.  If you want to use the default stderr and longjump method,
      you can supply NULL for the last three parameters.  We also check that
      the library version is compatible in case we are using dynamically
      linked libraries.
    */
   *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
       (void *)user_error_ptr, user_error_fn, user_warning_fn);

   if (! *png_ptr)
G
Guy Schalnat 已提交
269
   {
G
Guy Schalnat 已提交
270 271
      *info_ptr = NULL;
      return ERROR;
G
Guy Schalnat 已提交
272 273
   }

G
Guy Schalnat 已提交
274 275 276
   *info_ptr = png_create_info_struct(png_ptr);

   if (! *info_ptr)
G
Guy Schalnat 已提交
277
   {
G
Guy Schalnat 已提交
278 279
      png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)NULL);
      return ERROR;
G
Guy Schalnat 已提交
280 281
   }

G
Guy Schalnat 已提交
282 283 284 285 286
   if (setjmp((*png_ptr)->jmpbuf))
   {
      png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)NULL);
      return ERROR;
   }
G
Guy Schalnat 已提交
287 288 289

   /* this one's new.  You will need to provide all three
      function callbacks, even if you aren't using them all.
G
Guy Schalnat 已提交
290 291 292 293 294 295 296
      These functions shouldn't be dependent on global or
      static variables if you are decoding several images
      simultaneously.  You should store stream specific data
      in a separate struct, given as the second parameter,
      and retrieve the pointer from inside the callbacks using
      the function png_get_progressive_ptr(png_ptr). */
   png_set_progressive_read_fn(*png_ptr, (void *)stream_data,
G
Guy Schalnat 已提交
297 298
      info_callback, row_callback, end_callback);

G
Guy Schalnat 已提交
299
   return OK;
G
Guy Schalnat 已提交
300 301 302
}

int
G
Guy Schalnat 已提交
303 304
process_data(png_structp *png_ptr, png_infop *info_ptr,
   png_bytep buffer, png_uint_32 length)
G
Guy Schalnat 已提交
305
{
G
Guy Schalnat 已提交
306
   if (setjmp((*png_ptr)->jmpbuf))
G
Guy Schalnat 已提交
307
   {
G
Guy Schalnat 已提交
308 309 310
      /* Free the png_ptr and info_ptr memory on error */
      png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)NULL);
      return ERROR;
G
Guy Schalnat 已提交
311 312
   }

G
Guy Schalnat 已提交
313 314 315 316 317 318 319 320 321 322 323
   /* this one's new also.  Simply give it chunks of data as
      they arrive from the data stream (in order, of course).
      On Segmented machines, don't give it any more than 64K.
      The library seems to run fine with sizes of 4K, although
      you can give it much less if necessary (I assume you can
      give it chunks of 1 byte, but I haven't tried with less
      than 256 bytes yet).  When this function returns, you may
      want to display any rows that were generated in the row
      callback, if you aren't already displaying them there. */
   png_process_data(*png_ptr, *info_ptr, buffer, length);
   return OK;
G
Guy Schalnat 已提交
324 325 326 327
}

info_callback(png_structp png_ptr, png_infop info)
{
G
Guy Schalnat 已提交
328 329 330 331 332 333
/* do any setup here, including setting any of the transformations
   mentioned in the Reading PNG files section.  For now, you _must_
   call either png_start_read_image() or png_read_update_info()
   after all the transformations are set (even if you don't set
   any).  You may start getting rows before png_process_data()
   returns, so this is your last chance to prepare for that. */
G
Guy Schalnat 已提交
334 335 336
}

row_callback(png_structp png_ptr, png_bytep new_row,
G
Guy Schalnat 已提交
337
   png_uint_32 row_num, int pass)
G
Guy Schalnat 已提交
338
{
G
Guy Schalnat 已提交
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
/* this function is called for every row in the image.  If the
   image is interlacing, and you turned on the interlace handler,
   this function will be called for every row in every pass.
   Some of these rows will not be changed from the previous pass.
   When the row is not changed, the new_row variable will be NULL.
   The rows and passes are called in order, so you don't really
   need the row_num and pass, but I'm supplying them because it
   may make your life easier.

   For the non-NULL rows of interlaced images, you must call
   png_progressive_combine_row() passing in the row and the
   old row.  You can call this function for NULL rows (it will
   just return) and for non-interlaced images (it just does the
   memcpy for you) if it will make the code easier.  Thus, you
   can just do this for all cases: */

   png_progressive_combine_row(png_ptr, old_row, new_row);

/* where old_row is what was displayed for previous rows.  Note
   that the first pass (pass == 0 really) will completely cover
   the old row, so the rows do not have to be initialized.  After
   the first pass (and only for interlaced images), you will have
   to pass the current row, and the function will combine the
   old row and the new row. */
G
Guy Schalnat 已提交
363 364 365 366
}

end_callback(png_structp png_ptr, png_infop info)
{
G
Guy Schalnat 已提交
367 368 369 370 371 372 373 374
/* this function is called when the whole image has been read,
   including any chunks after the image (up to and including
   the IEND).  You will usually have the same info chunk as you
   had in the header, although some data may have been added
   to the comments and time fields.

   Most people won't do much here, perhaps setting a flag that
   marks the image as finished. */
G
Guy Schalnat 已提交
375 376
}

G
Guy Schalnat 已提交
377 378 379 380
/* write a png file */
void write_png(char *file_name, ... other image information ...)
{
   FILE *fp;
G
Guy Schalnat 已提交
381 382
   png_structp png_ptr;
   png_infop info_ptr;
G
Guy Schalnat 已提交
383 384 385 386 387 388

   /* open the file */
   fp = fopen(file_name, "wb");
   if (!fp)
      return;

G
Guy Schalnat 已提交
389 390 391 392 393 394 395 396 397
   /* Create and initialize the png_struct with the desired error handler
      functions.  If you want to use the default stderr and longjump method,
      you can supply NULL for the last three parameters.  We also check that
      the library version is compatible in case we are using dynamically
      linked libraries.
    */
   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
      (void *)user_error_ptr, user_error_fn, user_warning_fn);

G
Guy Schalnat 已提交
398 399 400 401 402 403
   if (!png_ptr)
   {
      fclose(fp);
      return;
   }

G
Guy Schalnat 已提交
404
   info_ptr = png_create_info_struct(png_ptr);
G
Guy Schalnat 已提交
405 406 407
   if (!info_ptr)
   {
      fclose(fp);
G
Guy Schalnat 已提交
408
      png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
G
Guy Schalnat 已提交
409 410 411 412
      return;
   }

   /* set error handling */
G
Guy Schalnat 已提交
413
   if (setjmp(png_ptr->jmpbuf))
G
Guy Schalnat 已提交
414 415
   {
      /* If we get here, we had a problem reading the file */
G
Guy Schalnat 已提交
416 417
      fclose(fp);
      png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
G
Guy Schalnat 已提交
418 419 420
      return;
   }

G
Guy Schalnat 已提交
421
   /* set up the output control if you are using standard C streams */
G
Guy Schalnat 已提交
422 423
   png_init_io(png_ptr, fp);

G
Guy Schalnat 已提交
424
   /* set the file information here */
G
Guy Schalnat 已提交
425 426 427 428 429 430 431 432 433 434 435 436
   info_ptr->width = ;
   info_ptr->height = ;
   etc.

   /* set the palette if there is one */
   info_ptr->valid |= PNG_INFO_PLTE;
   info_ptr->palette = malloc(256 * sizeof (png_color));
   info_ptr->num_palette = 256;
   ... set palette colors ...

   /* optional significant bit chunk */
   info_ptr->valid |= PNG_INFO_sBIT;
G
Guy Schalnat 已提交
437 438 439 440 441 442 443
   /* if we are dealing with a grayscale image then */
   info_ptr->sig_bit.gray = true_bit_depth;
   /* otherwise, if we are dealing with a color image then */
   info_ptr->sig_bit.red = true_red_bit_depth;
   info_ptr->sig_bit.green = true_green_bit_depth;
   info_ptr->sig_bit.blue = true_blue_bit_depth;
   /* if the image has an alpha channel then */
G
Guy Schalnat 已提交
444 445
   info_ptr->sig_bit.alpha = true_alpha_bit_depth;
  
G
Guy Schalnat 已提交
446 447 448
   /* optional gamma chunk is strongly suggested if you have any guess
      as to the correct gamma of the image */
   info_ptr->valid |= PNG_INFO_gAMA;
G
Guy Schalnat 已提交
449 450
   info_ptr->gamma = gamma;

G
Guy Schalnat 已提交
451
   /* other optional chunks like cHRM, bKGD, tRNS, tEXt, tIME, oFFs, pHYs, */
G
Guy Schalnat 已提交
452

G
Guy Schalnat 已提交
453
   /* write the file header information */
G
Guy Schalnat 已提交
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
   png_write_info(png_ptr, info_ptr);

   /* set up the transformations you want.  Note that these are
      all optional.  Only call them if you want them */

   /* invert monocrome pixels */
   png_set_invert(png_ptr);

   /* shift the pixels up to a legal bit depth and fill in
      as appropriate to correctly scale the image */
   png_set_shift(png_ptr, &(info_ptr->sig_bit));

   /* pack pixels into bytes */
   png_set_packing(png_ptr);

   /* flip bgr pixels to rgb */
   png_set_bgr(png_ptr);

   /* swap bytes of 16 bit files to most significant bit first */
   png_set_swap(png_ptr);

G
Guy Schalnat 已提交
475 476 477
   /* get rid of filler bytes, pack rgb into 3 bytes.  The
      filler number is not used. */
   png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
G
Guy Schalnat 已提交
478

G
Guy Schalnat 已提交
479
   /* turn on interlace handling if you are not using png_write_image() */
G
Guy Schalnat 已提交
480 481 482 483 484
   if (interlacing)
      number_passes = png_set_interlace_handling(png_ptr);
   else
      number_passes = 1;

G
Guy Schalnat 已提交
485 486 487 488
   /* the easiest way to write the image (you may choose to allocate the
      memory differently, however) */
   png_byte row_pointers[height][width];

G
Guy Schalnat 已提交
489 490 491 492
   png_write_image(png_ptr, row_pointers);

   /* the other way to write the image - deal with interlacing */

G
Guy Schalnat 已提交
493 494 495 496 497 498 499 500
   for (pass = 0; pass < number_passes; pass++)
   {
      /* Write a few rows at a time. */
      png_write_rows(png_ptr, row_pointers, number_of_rows);

      /* If you are only writing one row at a time, this works */
      for (y = 0; y < height; y++)
      {
G
Guy Schalnat 已提交
501
         png_bytep row_pointers = row[y];
G
Guy Schalnat 已提交
502 503 504 505
         png_write_rows(png_ptr, &row_pointers, 1);
      }
   }

G
Guy Schalnat 已提交
506 507 508 509 510 511
   /* You can write optional chunks like tEXt, tIME at the end as well.
    * Note that if you wrote tEXt or zTXt chunks before the image, and
    * you aren't writing out more at the end, you have to set
    * info_ptr->num_text = 0 or they will be written out again.
    */

G
Guy Schalnat 已提交
512 513 514 515 516 517 518
   /* write the rest of the file */
   png_write_end(png_ptr, info_ptr);

   /* if you malloced the palette, free it here */
   if (info_ptr->palette)
      free(info_ptr->palette);

G
Guy Schalnat 已提交
519 520 521 522
   /* if you allocated any text comments, free them here */

   /* clean up after the write, and free any memory allocated */
   png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
G
Guy Schalnat 已提交
523 524 525 526 527 528 529 530

   /* close the file */
   fclose(fp);

   /* that's it */
   return;
}