Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 1 | /* example.c - an example of using libpng */ |
| 2 | |
| 3 | /* this is an example of how to use libpng to read and write |
| 4 | png files. The file libpng.txt is much more verbose then |
| 5 | this. If you have not read it, do so first. This was |
| 6 | designed to be a starting point of an implementation. |
| 7 | This is not officially part of libpng, and therefore |
| 8 | does not require a copyright notice. |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 9 | |
| 10 | This file does not currently compile, because it is missing |
| 11 | certain parts, like allocating memory to hold an image. |
| 12 | You will have to supply these parts to get it to compile. |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 13 | */ |
| 14 | |
| 15 | #include <png.h> |
| 16 | |
| 17 | /* check to see if a file is a png file using png_check_sig() */ |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 18 | int check_png(char * file_name) |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 19 | { |
| 20 | FILE *fp; |
| 21 | char buf[8]; |
| 22 | int ret; |
| 23 | |
| 24 | fp = fopen(file_name, "rb"); |
| 25 | if (!fp) |
| 26 | return 0; |
| 27 | ret = fread(buf, 1, 8, fp); |
| 28 | fclose(fp); |
| 29 | |
| 30 | if (ret != 8) |
| 31 | return 0; |
| 32 | |
| 33 | ret = png_check_sig(buf, 8); |
| 34 | |
| 35 | return (ret); |
| 36 | } |
| 37 | |
| 38 | /* read a png file. You may want to return an error code if the read |
| 39 | fails (depending upon the failure). */ |
| 40 | void read_png(char *file_name) |
| 41 | { |
| 42 | FILE *fp; |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 43 | png_structp png_ptr; |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 44 | png_infop info_ptr; |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 45 | |
| 46 | /* open the file */ |
| 47 | fp = fopen(file_name, "rb"); |
| 48 | if (!fp) |
| 49 | return; |
| 50 | |
| 51 | /* allocate the necessary structures */ |
| 52 | png_ptr = malloc(sizeof (png_struct)); |
| 53 | if (!png_ptr) |
| 54 | { |
| 55 | fclose(fp); |
| 56 | return; |
| 57 | } |
| 58 | |
| 59 | info_ptr = malloc(sizeof (png_info)); |
| 60 | if (!info_ptr) |
| 61 | { |
| 62 | fclose(fp); |
| 63 | free(png_ptr); |
| 64 | return; |
| 65 | } |
| 66 | |
| 67 | /* set error handling */ |
| 68 | if (setjmp(png_ptr->jmpbuf)) |
| 69 | { |
| 70 | png_read_destroy(png_ptr, info_ptr, (png_info *)0); |
| 71 | fclose(fp); |
| 72 | free(png_ptr); |
| 73 | free(info_ptr); |
| 74 | /* If we get here, we had a problem reading the file */ |
| 75 | return; |
| 76 | } |
| 77 | |
| 78 | /* initialize the structures, info first for error handling */ |
| 79 | png_info_init(info_ptr); |
| 80 | png_read_init(png_ptr); |
| 81 | |
Guy Schalnat | 69b1448 | 1996-01-10 02:56:49 -0600 | [diff] [blame] | 82 | /* set up the input control if you are using standard C streams */ |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 83 | png_init_io(png_ptr, fp); |
| 84 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 85 | /* if you are using replacement read functions, here you would call */ |
| 86 | png_set_read_fn(png_ptr, (void *)io_ptr, user_read_fn); |
| 87 | /* where io_ptr is a structure you want available to the callbacks */ |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 88 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 89 | /* if you are using replacement message functions, here you would call */ |
| 90 | png_set_message_fn(png_ptr, (void *)msg_ptr, user_error_fn, user_warning_fn); |
| 91 | /* where msg_ptr is a structure you want available to the callbacks */ |
Guy Schalnat | 69b1448 | 1996-01-10 02:56:49 -0600 | [diff] [blame] | 92 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 93 | /* read the file information */ |
| 94 | png_read_info(png_ptr, info_ptr); |
Guy Schalnat | 69b1448 | 1996-01-10 02:56:49 -0600 | [diff] [blame] | 95 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 96 | /* set up the transformations you want. Note that these are |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 97 | all optional. Only call them if you want them */ |
| 98 | |
| 99 | /* expand paletted colors into true rgb */ |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 100 | if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 101 | png_set_expand(png_ptr); |
| 102 | |
| 103 | /* expand grayscale images to the full 8 bits */ |
| 104 | if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY && |
| 105 | info_ptr->bit_depth < 8) |
| 106 | png_set_expand(png_ptr); |
| 107 | |
| 108 | /* expand images with transparency to full alpha channels */ |
| 109 | if (info_ptr->valid & PNG_INFO_tRNS) |
| 110 | png_set_expand(png_ptr); |
| 111 | |
| 112 | /* Set the background color to draw transparent and alpha |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 113 | images over */ |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 114 | png_color_16 my_background; |
| 115 | |
| 116 | if (info_ptr->valid & PNG_INFO_bKGD) |
| 117 | png_set_background(png_ptr, &(info_ptr->background), |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 118 | PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); |
| 119 | else |
| 120 | png_set_background(png_ptr, &my_background, |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 121 | PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 122 | |
| 123 | /* tell libpng to handle the gamma conversion for you */ |
| 124 | if (info_ptr->valid & PNG_INFO_gAMA) |
| 125 | png_set_gamma(png_ptr, screen_gamma, info_ptr->gamma); |
| 126 | else |
| 127 | png_set_gamma(png_ptr, screen_gamma, 0.45); |
| 128 | |
| 129 | /* tell libpng to strip 16 bit depth files down to 8 bits */ |
| 130 | if (info_ptr->bit_depth == 16) |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 131 | png_set_strip_16(png_ptr); |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 132 | |
| 133 | /* dither rgb files down to 8 bit palettes & reduce palettes |
| 134 | to the number of colors available on your screen */ |
| 135 | if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) |
| 136 | { |
| 137 | if (info_ptr->valid & PNG_INFO_PLTE) |
| 138 | png_set_dither(png_ptr, info_ptr->palette, |
| 139 | info_ptr->num_palette, max_screen_colors, |
| 140 | info_ptr->histogram); |
| 141 | else |
| 142 | { |
| 143 | png_color std_color_cube[MAX_SCREEN_COLORS] = |
| 144 | {/* ... colors ... */}; |
| 145 | |
| 146 | png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS, |
| 147 | MAX_SCREEN_COLORS, NULL); |
| 148 | } |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 149 | } |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 150 | |
| 151 | /* invert monocrome files */ |
| 152 | if (info_ptr->bit_depth == 1 && |
| 153 | info_ptr->color_type == PNG_COLOR_GRAY) |
| 154 | png_set_invert(png_ptr); |
| 155 | |
| 156 | /* shift the pixels down to their true bit depth */ |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 157 | if (info_ptr->valid & PNG_INFO_sBIT && |
| 158 | info_ptr->bit_depth > info_ptr->sig_bit) |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 159 | png_set_shift(png_ptr, &(info_ptr->sig_bit)); |
| 160 | |
| 161 | /* pack pixels into bytes */ |
| 162 | if (info_ptr->bit_depth < 8) |
| 163 | png_set_packing(png_ptr); |
| 164 | |
| 165 | /* flip the rgb pixels to bgr */ |
| 166 | if (info_ptr->color_type == PNG_COLOR_TYPE_RGB || |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 167 | info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 168 | png_set_bgr(png_ptr); |
| 169 | |
| 170 | /* swap bytes of 16 bit files to least significant bit first */ |
| 171 | if (info_ptr->bit_depth == 16) |
| 172 | png_set_swap(png_ptr); |
| 173 | |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 174 | /* add a filler byte to rgb files */ |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 175 | if (info_ptr->bit_depth == 8 && |
| 176 | info_ptr->color_type == PNG_COLOR_TYPE_RGB) |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 177 | png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 178 | |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 179 | /* turn on interlace handling if you are not using png_read_image() */ |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 180 | if (info_ptr->interlace_type) |
| 181 | number_passes = png_set_interlace_handling(png_ptr); |
| 182 | else |
| 183 | number_passes = 1; |
| 184 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 185 | /* optional call to update palette with transformations */ |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 186 | png_start_read_image(png_ptr); |
| 187 | |
| 188 | /* optional call to update the info structure */ |
| 189 | png_read_update_info(png_ptr, info_ptr); |
| 190 | |
| 191 | /* allocate the memory to hold the image using the fields |
| 192 | of png_info. */ |
| 193 | |
| 194 | /* the easiest way to read the image */ |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 195 | png_bytep row_pointers[height]; |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 196 | png_read_image(png_ptr, row_pointers); |
| 197 | |
| 198 | /* the other way to read images - deal with interlacing */ |
| 199 | |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 200 | for (pass = 0; pass < number_passes; pass++) |
| 201 | { |
| 202 | /* Read the image using the "sparkle" effect. */ |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 203 | png_read_rows(png_ptr, row_pointers, NULL, number_of_rows); |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 204 | |
| 205 | /* If you are only reading on row at a time, this works */ |
| 206 | for (y = 0; y < height; y++) |
| 207 | { |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 208 | png_bytep row_pointers = row[y]; |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 209 | png_read_rows(png_ptr, &row_pointers, NULL, 1); |
| 210 | } |
| 211 | |
| 212 | /* to get the rectangle effect, use the third parameter */ |
| 213 | png_read_rows(png_ptr, NULL, row_pointers, number_of_rows); |
| 214 | |
| 215 | /* if you want to display the image after every pass, do |
| 216 | so here */ |
| 217 | } |
| 218 | |
| 219 | /* read the rest of the file, getting any additional chunks |
| 220 | in info_ptr */ |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 221 | png_read_end(png_ptr, info_ptr); |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 222 | |
| 223 | /* clean up after the read, and free any memory allocated */ |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 224 | png_read_destroy(png_ptr, info_ptr, (png_infop)0); |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 225 | |
| 226 | /* free the structures */ |
| 227 | free(png_ptr); |
| 228 | free(info_ptr); |
| 229 | |
| 230 | /* close the file */ |
| 231 | fclose(fp); |
| 232 | |
| 233 | /* that's it */ |
| 234 | return; |
| 235 | } |
| 236 | |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 237 | /* progressively read a file */ |
| 238 | |
| 239 | /* these will normally not be global unless you are only |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 240 | reading in one image at a time */ |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 241 | png_structp png_ptr; |
| 242 | png_infop info_ptr; |
| 243 | |
| 244 | int |
| 245 | initialize_png_reader() |
| 246 | { |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 247 | png_ptr = malloc(sizeof (png_struct)); |
| 248 | if (!png_ptr) |
| 249 | return -1; |
| 250 | info_ptr = malloc(sizeof (png_info)); |
| 251 | if (!info_ptr) |
| 252 | { |
| 253 | free(png_ptr); |
| 254 | return -1; |
| 255 | } |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 256 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 257 | if (setjmp(png_ptr->jmpbuf)) |
| 258 | { |
| 259 | png_read_destroy(png_ptr, info_ptr, (png_info *)0); |
| 260 | /* free pointers before returning, if necessary */ |
| 261 | free(png_ptr); |
| 262 | free(info_ptr); |
| 263 | return -1; |
| 264 | } |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 265 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 266 | png_info_init(info_ptr); |
| 267 | png_read_init(png_ptr); |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 268 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 269 | /* this one's new. You will need to provide all three |
| 270 | function callbacks, even if you aren't using them all. |
| 271 | You can put a void pointer in place of the NULL, and |
| 272 | retrieve the pointer from inside the callbacks using |
| 273 | the function png_get_progressive_ptr(png_ptr); */ |
| 274 | png_set_progressive_read_fn(png_ptr, NULL, |
| 275 | info_callback, row_callback, end_callback); |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 276 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 277 | return 0; |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 278 | } |
| 279 | |
| 280 | int |
| 281 | process_data(png_bytep buffer, png_uint_32 length) |
| 282 | { |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 283 | if (setjmp(png_ptr->jmpbuf)) |
| 284 | { |
| 285 | png_read_destroy(png_ptr, info_ptr, (png_info *)0); |
| 286 | free(png_ptr); |
| 287 | free(info_ptr); |
| 288 | return -1; |
| 289 | } |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 290 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 291 | /* this one's new also. Simply give it a chunk of data |
| 292 | from the file stream (in order, of course). On Segmented |
| 293 | machines, don't give it any more then 64K. The library |
| 294 | seems to run fine with sizes of 4K, although you can give |
| 295 | it much less if necessary (I assume you can give it chunks |
| 296 | of 1 byte, but I haven't tried less then 256 bytes yet). |
| 297 | When this function returns, you may want to display any |
| 298 | rows that were generated in the row callback. */ |
| 299 | png_process_data(png_ptr, info_ptr, buffer, length); |
| 300 | return 0; |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 301 | } |
| 302 | |
| 303 | info_callback(png_structp png_ptr, png_infop info) |
| 304 | { |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 305 | /* do any setup here, including setting any of the transformations |
| 306 | mentioned in the Reading PNG files section. For now, you _must_ |
| 307 | call either png_start_read_image() or png_read_update_info() |
| 308 | after all the transformations are set (even if you don't set |
| 309 | any). You may start getting rows before png_process_data() |
| 310 | returns, so this is your last chance to prepare for that. */ |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 311 | } |
| 312 | |
| 313 | row_callback(png_structp png_ptr, png_bytep new_row, |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 314 | png_uint_32 row_num, int pass) |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 315 | { |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 316 | /* this function is called for every row in the image. If the |
| 317 | image is interlacing, and you turned on the interlace handler, |
| 318 | this function will be called for every row in every pass. |
| 319 | Some of these rows will not be changed from the previous pass. |
| 320 | When the row is not changed, the new_row variable will be NULL. |
| 321 | The rows and passes are called in order, so you don't really |
| 322 | need the row_num and pass, but I'm supplying them because it |
| 323 | may make your life easier. |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 324 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 325 | For the non-NULL rows of interlaced images, you must call |
| 326 | png_progressive_combine_row() passing in the row and the |
| 327 | old row. You can call this function for NULL rows (it will |
| 328 | just return) and for non-interlaced images (it just does the |
| 329 | memcpy for you) if it will make the code easier. Thus, you |
| 330 | can just do this for all cases: */ |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 331 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 332 | png_progressive_combine_row(png_ptr, old_row, new_row); |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 333 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 334 | /* where old_row is what was displayed for previous rows. Note |
| 335 | that the first pass (pass == 0 really) will completely cover |
| 336 | the old row, so the rows do not have to be initialized. After |
| 337 | the first pass (and only for interlaced images), you will have |
| 338 | to pass the current row, and the function will combine the |
| 339 | old row and the new row. */ |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 340 | } |
| 341 | |
| 342 | end_callback(png_structp png_ptr, png_infop info) |
| 343 | { |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 344 | /* this function is called when the whole image has been read, |
| 345 | including any chunks after the image (up to and including |
| 346 | the IEND). You will usually have the same info chunk as you |
| 347 | had in the header, although some data may have been added |
| 348 | to the comments and time fields. |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 349 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 350 | Most people won't do much here, perhaps setting a flag that |
| 351 | marks the image as finished. */ |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 352 | } |
| 353 | |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 354 | /* write a png file */ |
| 355 | void write_png(char *file_name, ... other image information ...) |
| 356 | { |
| 357 | FILE *fp; |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 358 | png_structp png_ptr; |
| 359 | png_infop info_ptr; |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 360 | |
| 361 | /* open the file */ |
| 362 | fp = fopen(file_name, "wb"); |
| 363 | if (!fp) |
| 364 | return; |
| 365 | |
| 366 | /* allocate the necessary structures */ |
| 367 | png_ptr = malloc(sizeof (png_struct)); |
| 368 | if (!png_ptr) |
| 369 | { |
| 370 | fclose(fp); |
| 371 | return; |
| 372 | } |
| 373 | |
| 374 | info_ptr = malloc(sizeof (png_info)); |
| 375 | if (!info_ptr) |
| 376 | { |
| 377 | fclose(fp); |
| 378 | free(png_ptr); |
| 379 | return; |
| 380 | } |
| 381 | |
| 382 | /* set error handling */ |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 383 | if (setjmp(png_ptr->jmpbuf)) |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 384 | { |
| 385 | png_write_destroy(png_ptr); |
| 386 | fclose(fp); |
| 387 | free(png_ptr); |
| 388 | free(info_ptr); |
| 389 | /* If we get here, we had a problem reading the file */ |
| 390 | return; |
| 391 | } |
| 392 | |
| 393 | /* initialize the structures */ |
| 394 | png_info_init(info_ptr); |
| 395 | png_write_init(png_ptr); |
| 396 | |
Guy Schalnat | 69b1448 | 1996-01-10 02:56:49 -0600 | [diff] [blame] | 397 | /* set up the output control if you are using standard C streams */ |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 398 | png_init_io(png_ptr, fp); |
| 399 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 400 | /* if you are using replacement write functions, here you would call */ |
| 401 | png_set_write_fn(png_ptr, (void *)io_ptr, user_write_fn, user_flush_fn); |
| 402 | /* where io_ptr is a structure you want available to the callbacks */ |
Guy Schalnat | 69b1448 | 1996-01-10 02:56:49 -0600 | [diff] [blame] | 403 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 404 | /* if you are using replacement message functions, here you would call */ |
| 405 | png_set_message_fn(png_ptr, (void *)msg_ptr, user_error_fn, user_warning_fn); |
| 406 | /* where msg_ptr is a structure you want available to the callbacks */ |
Guy Schalnat | 69b1448 | 1996-01-10 02:56:49 -0600 | [diff] [blame] | 407 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 408 | /* set the file information here */ |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 409 | info_ptr->width = ; |
| 410 | info_ptr->height = ; |
| 411 | etc. |
| 412 | |
| 413 | /* set the palette if there is one */ |
| 414 | info_ptr->valid |= PNG_INFO_PLTE; |
| 415 | info_ptr->palette = malloc(256 * sizeof (png_color)); |
| 416 | info_ptr->num_palette = 256; |
| 417 | ... set palette colors ... |
| 418 | |
| 419 | /* optional significant bit chunk */ |
| 420 | info_ptr->valid |= PNG_INFO_sBIT; |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 421 | /* if we are dealing with a grayscale image then */ |
| 422 | info_ptr->sig_bit.gray = true_bit_depth; |
| 423 | /* otherwise, if we are dealing with a color image then */ |
| 424 | info_ptr->sig_bit.red = true_red_bit_depth; |
| 425 | info_ptr->sig_bit.green = true_green_bit_depth; |
| 426 | info_ptr->sig_bit.blue = true_blue_bit_depth; |
| 427 | /* if the image has an alpha channel then */ |
Guy Schalnat | 69b1448 | 1996-01-10 02:56:49 -0600 | [diff] [blame] | 428 | info_ptr->sig_bit.alpha = true_alpha_bit_depth; |
| 429 | |
Guy Schalnat | b2e01bd | 1996-01-26 01:38:47 -0600 | [diff] [blame^] | 430 | /* optional gamma chunk is strongly suggested if you have any guess |
| 431 | as to the correct gamma of the image */ |
| 432 | info_ptr->valid |= PNG_INFO_gAMA; |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 433 | info_ptr->gamma = gamma; |
| 434 | |
| 435 | /* other optional chunks */ |
| 436 | |
| 437 | /* write the file information */ |
| 438 | png_write_info(png_ptr, info_ptr); |
| 439 | |
| 440 | /* set up the transformations you want. Note that these are |
| 441 | all optional. Only call them if you want them */ |
| 442 | |
| 443 | /* invert monocrome pixels */ |
| 444 | png_set_invert(png_ptr); |
| 445 | |
| 446 | /* shift the pixels up to a legal bit depth and fill in |
| 447 | as appropriate to correctly scale the image */ |
| 448 | png_set_shift(png_ptr, &(info_ptr->sig_bit)); |
| 449 | |
| 450 | /* pack pixels into bytes */ |
| 451 | png_set_packing(png_ptr); |
| 452 | |
| 453 | /* flip bgr pixels to rgb */ |
| 454 | png_set_bgr(png_ptr); |
| 455 | |
| 456 | /* swap bytes of 16 bit files to most significant bit first */ |
| 457 | png_set_swap(png_ptr); |
| 458 | |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 459 | /* get rid of filler bytes, pack rgb into 3 bytes. The |
| 460 | filler number is not used. */ |
| 461 | png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 462 | |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 463 | /* turn on interlace handling if you are not using png_write_image() */ |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 464 | if (interlacing) |
| 465 | number_passes = png_set_interlace_handling(png_ptr); |
| 466 | else |
| 467 | number_passes = 1; |
| 468 | |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 469 | /* the easiest way to write the image */ |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 470 | png_bytep row_pointers[height]; |
Guy Schalnat | 51f0eb4 | 1995-09-26 05:22:39 -0500 | [diff] [blame] | 471 | png_write_image(png_ptr, row_pointers); |
| 472 | |
| 473 | /* the other way to write the image - deal with interlacing */ |
| 474 | |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 475 | for (pass = 0; pass < number_passes; pass++) |
| 476 | { |
| 477 | /* Write a few rows at a time. */ |
| 478 | png_write_rows(png_ptr, row_pointers, number_of_rows); |
| 479 | |
| 480 | /* If you are only writing one row at a time, this works */ |
| 481 | for (y = 0; y < height; y++) |
| 482 | { |
Guy Schalnat | 6d76471 | 1995-12-19 03:22:19 -0600 | [diff] [blame] | 483 | png_bytep row_pointers = row[y]; |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 484 | png_write_rows(png_ptr, &row_pointers, 1); |
| 485 | } |
| 486 | } |
| 487 | |
| 488 | /* write the rest of the file */ |
| 489 | png_write_end(png_ptr, info_ptr); |
| 490 | |
Guy Schalnat | 0d58058 | 1995-07-20 02:43:20 -0500 | [diff] [blame] | 491 | /* clean up after the write, and free any memory allocated */ |
| 492 | png_write_destroy(png_ptr); |
| 493 | |
| 494 | /* if you malloced the palette, free it here */ |
| 495 | if (info_ptr->palette) |
| 496 | free(info_ptr->palette); |
| 497 | |
| 498 | /* free the structures */ |
| 499 | free(png_ptr); |
| 500 | free(info_ptr); |
| 501 | |
| 502 | /* close the file */ |
| 503 | fclose(fp); |
| 504 | |
| 505 | /* that's it */ |
| 506 | return; |
| 507 | } |
| 508 | |