example.c 14.7 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 17
   */

#include <png.h>

/* check to see if a file is a png file using png_check_sig() */
G
Guy Schalnat 已提交
18
int check_png(char * file_name)
G
Guy Schalnat 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
{
   FILE *fp;
   char buf[8];
   int ret;

   fp = fopen(file_name, "rb");
   if (!fp)
      return 0;
   ret = fread(buf, 1, 8, fp);
   fclose(fp);

   if (ret != 8)
      return 0;

   ret = png_check_sig(buf, 8);

   return (ret);
}

/* read a png file.  You may want to return an error code if the read
   fails (depending upon the failure). */
void read_png(char *file_name)
{
   FILE *fp;
G
Guy Schalnat 已提交
43 44
	png_structp png_ptr;
   png_infop info_ptr;
G
Guy Schalnat 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81

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

   /* allocate the necessary structures */
   png_ptr = malloc(sizeof (png_struct));
   if (!png_ptr)
   {
      fclose(fp);
      return;
   }

   info_ptr = malloc(sizeof (png_info));
   if (!info_ptr)
   {
      fclose(fp);
      free(png_ptr);
      return;
   }

   /* set error handling */
   if (setjmp(png_ptr->jmpbuf))
   {
      png_read_destroy(png_ptr, info_ptr, (png_info *)0);
      fclose(fp);
      free(png_ptr);
      free(info_ptr);
      /* If we get here, we had a problem reading the file */
      return;
   }

   /* initialize the structures, info first for error handling */
   png_info_init(info_ptr);
   png_read_init(png_ptr);

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

G
Guy Schalnat 已提交
85 86 87
	/* if you are using replacement read functions, here you would call */
	png_set_read_fn(png_ptr, (void *)io_ptr, user_read_fn);
	/* where io_ptr is a structure you want available to the callbacks */
G
Guy Schalnat 已提交
88

G
Guy Schalnat 已提交
89 90 91 92 93 94 95 96
	/* if you are using replacement message functions, here you would call */
	png_set_message_fn(png_ptr, (void *)msg_ptr, user_error_fn, user_warning_fn);
	/* where msg_ptr is a structure you want available to the callbacks */

	/* read the file information */
	png_read_info(png_ptr, info_ptr);

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

   /* expand paletted colors into true rgb */
G
Guy Schalnat 已提交
100
   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
G
Guy Schalnat 已提交
101 102 103 104 105 106 107 108 109 110 111 112
      png_set_expand(png_ptr);

   /* expand grayscale images to the full 8 bits */
   if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&
      info_ptr->bit_depth < 8)
      png_set_expand(png_ptr);

   /* expand images with transparency to full alpha channels */
   if (info_ptr->valid & PNG_INFO_tRNS)
      png_set_expand(png_ptr);

   /* Set the background color to draw transparent and alpha
G
Guy Schalnat 已提交
113
		images over */
G
Guy Schalnat 已提交
114 115 116 117
   png_color_16 my_background;

   if (info_ptr->valid & PNG_INFO_bKGD)
      png_set_background(png_ptr, &(info_ptr->background),
G
Guy Schalnat 已提交
118 119 120 121
			PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
	else
		png_set_background(png_ptr, &my_background,
         PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
G
Guy Schalnat 已提交
122 123 124 125 126 127 128 129 130

   /* tell libpng to handle the gamma conversion for you */
   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);

   /* tell libpng to strip 16 bit depth files down to 8 bits */
   if (info_ptr->bit_depth == 16)
G
Guy Schalnat 已提交
131
		png_set_strip_16(png_ptr);
G
Guy Schalnat 已提交
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148

   /* dither rgb files down to 8 bit palettes & reduce palettes
      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)
         png_set_dither(png_ptr, info_ptr->palette,
            info_ptr->num_palette, max_screen_colors,
               info_ptr->histogram);
      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 已提交
149
	}
G
Guy Schalnat 已提交
150 151 152 153 154 155 156

   /* invert monocrome files */
   if (info_ptr->bit_depth == 1 &&
      info_ptr->color_type == PNG_COLOR_GRAY)
      png_set_invert(png_ptr);

   /* shift the pixels down to their true bit depth */
G
Guy Schalnat 已提交
157 158
   if (info_ptr->valid & PNG_INFO_sBIT &&
      info_ptr->bit_depth > info_ptr->sig_bit)
G
Guy Schalnat 已提交
159 160 161 162 163 164 165 166
      png_set_shift(png_ptr, &(info_ptr->sig_bit));

   /* pack pixels into bytes */
   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 已提交
167
		info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
G
Guy Schalnat 已提交
168 169 170 171 172 173
      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 已提交
174
   /* add a filler byte to rgb files */
G
Guy Schalnat 已提交
175 176
   if (info_ptr->bit_depth == 8 &&
      info_ptr->color_type == PNG_COLOR_TYPE_RGB)
G
Guy Schalnat 已提交
177 178 179 180 181 182 183
      png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);

   /* turn on interlace handling if you are not using png_read_image() */
   if (info_ptr->interlace_type)
      number_passes = png_set_interlace_handling(png_ptr);
   else
      number_passes = 1;
G
Guy Schalnat 已提交
184

G
Guy Schalnat 已提交
185
	/* optional call to update palette with transformations */
G
Guy Schalnat 已提交
186 187
   png_start_read_image(png_ptr);

G
Guy Schalnat 已提交
188 189 190 191 192 193
   /* optional call to update the info structure */
   png_read_update_info(png_ptr, info_ptr);

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

G
Guy Schalnat 已提交
194
   /* the easiest way to read the image */
G
Guy Schalnat 已提交
195
   png_bytep row_pointers[height];
G
Guy Schalnat 已提交
196 197 198 199 200 201 202
   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 已提交
203
		png_read_rows(png_ptr, row_pointers, NULL, number_of_rows);
G
Guy Schalnat 已提交
204 205 206 207

      /* If you are only reading on row at a time, this works */
      for (y = 0; y < height; y++)
      {
G
Guy Schalnat 已提交
208
         png_bytep row_pointers = row[y];
G
Guy Schalnat 已提交
209 210 211 212 213 214 215 216 217 218 219 220
         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 */
   }

   /* read the rest of the file, getting any additional chunks
      in info_ptr */
G
Guy Schalnat 已提交
221
	png_read_end(png_ptr, info_ptr);
G
Guy Schalnat 已提交
222 223

   /* clean up after the read, and free any memory allocated */
G
Guy Schalnat 已提交
224
   png_read_destroy(png_ptr, info_ptr, (png_infop)0);
G
Guy Schalnat 已提交
225 226 227 228 229 230 231 232 233 234 235 236

   /* free the structures */
   free(png_ptr);
   free(info_ptr);

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

   /* that's it */
   return;
}

G
Guy Schalnat 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
/* progressively read a file */

/* these will normally not be global unless you are only
	reading in one image at a time */
png_structp png_ptr;
png_infop info_ptr;

int
initialize_png_reader()
{
	png_ptr = malloc(sizeof (png_struct));
	if (!png_ptr)
		return -1;
	info_ptr = malloc(sizeof (png_info));
	if (!info_ptr)
	{
		free(png_ptr);
		return -1;
	}

	if (setjmp(png_ptr->jmpbuf))
	{
		png_read_destroy(png_ptr, info_ptr, (png_info *)0);
		/* free pointers before returning, if necessary */
		free(png_ptr);
		free(info_ptr);
		return -1;
	}

	png_info_init(info_ptr);
	png_read_init(png_ptr);

	/* this one's new.  You will need to provide all three
		function callbacks, even if you aren't using them all.
		You can put a void pointer in place of the NULL, and
		retrieve the pointer from inside the callbacks using
G
Guy Schalnat 已提交
273
		the function png_get_progressive_ptr(png_ptr); */
G
Guy Schalnat 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
	png_set_progressive_read_fn(png_ptr, NULL,
		info_callback, row_callback, end_callback);

	return 0;
}

int
process_data(png_bytep buffer, png_uint_32 length)
{
	if (setjmp(png_ptr->jmpbuf))
	{
		png_read_destroy(png_ptr, info_ptr, (png_info *)0);
		free(png_ptr);
		free(info_ptr);
		return -1;
	}

	/* this one's new also.  Simply give it a chunk of data
		from the file stream (in order, of course).  On Segmented
		machines, don't give it any more then 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 less then 256 bytes yet).
		When this function returns, you may want to display any
		rows that were generated in the row callback. */
	png_process_data(png_ptr, info_ptr, buffer, length);
	return 0;
}

info_callback(png_structp png_ptr, png_infop info)
{
/*	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. */
}

row_callback(png_structp png_ptr, png_bytep new_row,
	png_uint_32 row_num, int pass)
{
/*	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. */
}

end_callback(png_structp png_ptr, png_infop info)
{
/*	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 已提交
354 355 356 357
/* write a png file */
void write_png(char *file_name, ... other image information ...)
{
   FILE *fp;
G
Guy Schalnat 已提交
358 359
	png_structp png_ptr;
	png_infop info_ptr;
G
Guy Schalnat 已提交
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382

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

   /* allocate the necessary structures */
   png_ptr = malloc(sizeof (png_struct));
   if (!png_ptr)
   {
      fclose(fp);
      return;
   }

   info_ptr = malloc(sizeof (png_info));
   if (!info_ptr)
   {
      fclose(fp);
      free(png_ptr);
      return;
   }

   /* set error handling */
G
Guy Schalnat 已提交
383
	if (setjmp(png_ptr->jmpbuf))
G
Guy Schalnat 已提交
384 385 386 387 388 389 390 391 392 393 394 395 396
   {
      png_write_destroy(png_ptr);
      fclose(fp);
      free(png_ptr);
      free(info_ptr);
      /* If we get here, we had a problem reading the file */
      return;
   }

   /* initialize the structures */
   png_info_init(info_ptr);
   png_write_init(png_ptr);

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

G
Guy Schalnat 已提交
400 401 402 403 404 405 406 407 408
	/* if you are using replacement write functions, here you would call */
	png_set_write_fn(png_ptr, (void *)io_ptr, user_write_fn, user_flush_fn);
	/* where io_ptr is a structure you want available to the callbacks */

	/* if you are using replacement message functions, here you would call */
	png_set_message_fn(png_ptr, (void *)msg_ptr, user_error_fn, user_warning_fn);
	/* where msg_ptr is a structure you want available to the callbacks */

	/* set the file information here */
G
Guy Schalnat 已提交
409 410 411 412 413 414 415 416 417 418 419 420
   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 已提交
421 422 423 424 425 426 427 428 429 430 431 432
	/* 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 */
   info_ptr->sig_bit.alpha = true_alpha_bit_depth;
  
	/* 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 已提交
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
   info_ptr->gamma = gamma;

   /* other optional chunks */

   /* write the file information */
   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 已提交
459 460 461
   /* 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 已提交
462

G
Guy Schalnat 已提交
463
   /* turn on interlace handling if you are not using png_write_image() */
G
Guy Schalnat 已提交
464 465 466 467 468
   if (interlacing)
      number_passes = png_set_interlace_handling(png_ptr);
   else
      number_passes = 1;

G
Guy Schalnat 已提交
469
   /* the easiest way to write the image */
G
Guy Schalnat 已提交
470
   png_bytep row_pointers[height];
G
Guy Schalnat 已提交
471 472 473 474
   png_write_image(png_ptr, row_pointers);

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

G
Guy Schalnat 已提交
475 476 477 478 479 480 481 482
   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 已提交
483
         png_bytep row_pointers = row[y];
G
Guy Schalnat 已提交
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
         png_write_rows(png_ptr, &row_pointers, 1);
      }
   }

   /* write the rest of the file */
   png_write_end(png_ptr, info_ptr);

   /* clean up after the write, and free any memory allocated */
   png_write_destroy(png_ptr);

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

   /* free the structures */
   free(png_ptr);
   free(info_ptr);

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

   /* that's it */
   return;
}