Commit | Line | Data |
---|---|---|
1d7e3124 | 1 | #include "impng.h" |
02d1d628 AMH |
2 | #include "png.h" |
3 | ||
4 | /* Check to see if a file is a PNG file using png_sig_cmp(). png_sig_cmp() | |
5 | * returns zero if the image is a PNG and nonzero if it isn't a PNG. | |
6 | * | |
7 | * The function check_if_png() shown here, but not used, returns nonzero (true) | |
8 | * if the file can be opened and is a PNG, 0 (false) otherwise. | |
9 | * | |
10 | * If this call is successful, and you are going to keep the file open, | |
11 | * you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once | |
12 | * you have created the png_ptr, so that libpng knows your application | |
13 | * has read that many bytes from the start of the file. Make sure you | |
14 | * don't call png_set_sig_bytes() with more than 8 bytes read or give it | |
15 | * an incorrect number of bytes read, or you will either have read too | |
16 | * many bytes (your fault), or you are telling libpng to read the wrong | |
17 | * number of magic bytes (also your fault). | |
18 | * | |
19 | * Many applications already read the first 2 or 4 bytes from the start | |
20 | * of the image to determine the file type, so it would be easiest just | |
21 | * to pass the bytes to png_sig_cmp() or even skip that if you know | |
22 | * you have a PNG file, and call png_set_sig_bytes(). | |
23 | */ | |
24 | ||
25 | /* this is a way to get number of channels from color space | |
26 | * Color code to channel number */ | |
27 | ||
b33c08f8 | 28 | static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA]; |
02d1d628 AMH |
29 | |
30 | #define PNG_BYTES_TO_CHECK 4 | |
31 | ||
32 | ||
33 | ||
02d1d628 | 34 | static void |
790923a4 | 35 | wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { |
f5b4354e | 36 | io_glue *ig = png_get_io_ptr(png_ptr); |
790923a4 AMH |
37 | int rc = ig->readcb(ig, data, length); |
38 | if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source."); | |
02d1d628 AMH |
39 | } |
40 | ||
02d1d628 | 41 | static void |
790923a4 AMH |
42 | wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { |
43 | int rc; | |
f5b4354e | 44 | io_glue *ig = png_get_io_ptr(png_ptr); |
790923a4 AMH |
45 | rc = ig->writecb(ig, data, length); |
46 | if (rc != length) png_error(png_ptr, "Write error on an iolayer source."); | |
02d1d628 AMH |
47 | } |
48 | ||
49 | static void | |
790923a4 AMH |
50 | wiol_flush_data(png_structp png_ptr) { |
51 | /* XXX : This needs to be added to the io layer */ | |
02d1d628 | 52 | } |
02d1d628 AMH |
53 | |
54 | ||
15dc61b6 AMH |
55 | /* Check function demo |
56 | ||
02d1d628 AMH |
57 | int |
58 | check_if_png(char *file_name, FILE **fp) { | |
790923a4 | 59 | char buf[PNG_BYTES_TO_CHECK]; |
790923a4 | 60 | if ((*fp = fopen(file_name, "rb")) != NULL) return 0; |
790923a4 | 61 | if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) return 0; |
790923a4 | 62 | return(!png_sig_cmp((png_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK)); |
02d1d628 | 63 | } |
15dc61b6 | 64 | */ |
02d1d628 | 65 | |
02d1d628 | 66 | undef_int |
790923a4 | 67 | i_writepng_wiol(i_img *im, io_glue *ig) { |
02d1d628 | 68 | png_structp png_ptr; |
a807aae6 | 69 | png_infop info_ptr = NULL; |
02d1d628 AMH |
70 | int width,height,y; |
71 | volatile int cspace,channels; | |
faa9b3e7 TC |
72 | double xres, yres; |
73 | int aspect_only, have_res; | |
02d1d628 | 74 | |
790923a4 | 75 | mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig)); |
02d1d628 | 76 | |
790923a4 AMH |
77 | height = im->ysize; |
78 | width = im->xsize; | |
02d1d628 AMH |
79 | |
80 | channels=im->channels; | |
81 | ||
790923a4 | 82 | if (channels > 2) { cspace = PNG_COLOR_TYPE_RGB; channels-=3; } |
02d1d628 AMH |
83 | else { cspace=PNG_COLOR_TYPE_GRAY; channels--; } |
84 | ||
85 | if (channels) cspace|=PNG_COLOR_MASK_ALPHA; | |
86 | mm_log((1,"cspace=%d\n",cspace)); | |
87 | ||
790923a4 | 88 | channels = im->channels; |
02d1d628 AMH |
89 | |
90 | /* Create and initialize the png_struct with the desired error handler | |
91 | * functions. If you want to use the default stderr and longjump method, | |
92 | * you can supply NULL for the last three parameters. We also check that | |
93 | * the library version is compatible with the one used at compile time, | |
94 | * in case we are using dynamically linked libraries. REQUIRED. | |
95 | */ | |
96 | ||
97 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); | |
98 | ||
790923a4 AMH |
99 | if (png_ptr == NULL) return 0; |
100 | ||
02d1d628 AMH |
101 | |
102 | /* Allocate/initialize the image information data. REQUIRED */ | |
103 | info_ptr = png_create_info_struct(png_ptr); | |
104 | ||
105 | if (info_ptr == NULL) { | |
a807aae6 | 106 | png_destroy_write_struct(&png_ptr, &info_ptr); |
790923a4 | 107 | return 0; |
02d1d628 AMH |
108 | } |
109 | ||
110 | /* Set error handling. REQUIRED if you aren't supplying your own | |
111 | * error hadnling functions in the png_create_write_struct() call. | |
112 | */ | |
f5b4354e | 113 | if (setjmp(png_jmpbuf(png_ptr))) { |
a807aae6 | 114 | png_destroy_write_struct(&png_ptr, &info_ptr); |
02d1d628 AMH |
115 | return(0); |
116 | } | |
117 | ||
790923a4 | 118 | png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data); |
02d1d628 AMH |
119 | |
120 | /* Set the image information here. Width and height are up to 2^31, | |
121 | * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on | |
122 | * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, | |
123 | * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, | |
124 | * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or | |
125 | * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST | |
126 | * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED | |
127 | */ | |
128 | ||
129 | png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace, | |
130 | PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); | |
131 | ||
faa9b3e7 TC |
132 | have_res = 1; |
133 | if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) { | |
134 | if (i_tags_get_float(&im->tags, "i_yres", 0, &yres)) | |
135 | ; /* nothing to do */ | |
136 | else | |
137 | yres = xres; | |
138 | } | |
139 | else { | |
140 | if (i_tags_get_float(&im->tags, "i_yres", 0, &yres)) | |
141 | xres = yres; | |
142 | else | |
143 | have_res = 0; | |
144 | } | |
145 | if (have_res) { | |
146 | aspect_only = 0; | |
147 | i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only); | |
148 | xres /= 0.0254; | |
149 | yres /= 0.0254; | |
150 | png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5, | |
151 | aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER); | |
152 | } | |
153 | ||
02d1d628 | 154 | png_write_info(png_ptr, info_ptr); |
790923a4 | 155 | |
faa9b3e7 TC |
156 | if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) { |
157 | for (y = 0; y < height; y++) | |
158 | png_write_row(png_ptr, (png_bytep) &(im->idata[channels*width*y])); | |
159 | } | |
160 | else { | |
161 | unsigned char *data = mymalloc(im->xsize * im->channels); | |
435e4a7d TC |
162 | for (y = 0; y < height; y++) { |
163 | i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels); | |
164 | png_write_row(png_ptr, (png_bytep)data); | |
faa9b3e7 | 165 | } |
435e4a7d | 166 | myfree(data); |
faa9b3e7 | 167 | } |
790923a4 | 168 | |
02d1d628 | 169 | png_write_end(png_ptr, info_ptr); |
790923a4 | 170 | |
a807aae6 | 171 | png_destroy_write_struct(&png_ptr, &info_ptr); |
02d1d628 | 172 | |
10461f9a TC |
173 | ig->closecb(ig); |
174 | ||
02d1d628 AMH |
175 | return(1); |
176 | } | |
177 | ||
178 | ||
179 | ||
faa9b3e7 | 180 | static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr); |
02d1d628 AMH |
181 | |
182 | i_img* | |
1d7e3124 | 183 | i_readpng_wiol(io_glue *ig) { |
02576e8d | 184 | i_img *im = NULL; |
02d1d628 AMH |
185 | png_structp png_ptr; |
186 | png_infop info_ptr; | |
790923a4 | 187 | png_uint_32 width, height; |
02d1d628 | 188 | int bit_depth, color_type, interlace_type; |
790923a4 AMH |
189 | int number_passes,y; |
190 | int channels,pass; | |
02d1d628 AMH |
191 | unsigned int sig_read; |
192 | ||
790923a4 | 193 | sig_read = 0; |
02d1d628 | 194 | |
1d7e3124 | 195 | mm_log((1,"i_readpng_wiol(ig %p)\n", ig)); |
02d1d628 AMH |
196 | |
197 | png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); | |
790923a4 | 198 | png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data); |
02d1d628 AMH |
199 | |
200 | info_ptr = png_create_info_struct(png_ptr); | |
201 | if (info_ptr == NULL) { | |
202 | png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); | |
203 | return NULL; | |
204 | } | |
205 | ||
f5b4354e | 206 | if (setjmp(png_jmpbuf(png_ptr))) { |
02576e8d | 207 | if (im) i_img_destroy(im); |
790923a4 | 208 | mm_log((1,"i_readpng_wiol: error.\n")); |
02d1d628 AMH |
209 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); |
210 | return NULL; | |
211 | } | |
790923a4 | 212 | |
02d1d628 AMH |
213 | png_set_sig_bytes(png_ptr, sig_read); |
214 | png_read_info(png_ptr, info_ptr); | |
215 | png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); | |
216 | ||
217 | mm_log((1, | |
790923a4 | 218 | "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n", |
02d1d628 AMH |
219 | width,height,bit_depth,color_type,interlace_type)); |
220 | ||
221 | CC2C[PNG_COLOR_TYPE_GRAY]=1; | |
222 | CC2C[PNG_COLOR_TYPE_PALETTE]=3; | |
223 | CC2C[PNG_COLOR_TYPE_RGB]=3; | |
224 | CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4; | |
225 | CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2; | |
790923a4 AMH |
226 | channels = CC2C[color_type]; |
227 | ||
228 | mm_log((1,"i_readpng_wiol: channels %d\n",channels)); | |
229 | ||
77157728 TC |
230 | if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) { |
231 | mm_log((1, "i_readpnm: image size exceeds limits\n")); | |
232 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
233 | return NULL; | |
234 | } | |
235 | ||
02d1d628 AMH |
236 | png_set_strip_16(png_ptr); |
237 | png_set_packing(png_ptr); | |
238 | if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); | |
239 | if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); | |
6829f5f6 AMH |
240 | |
241 | if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | |
242 | channels++; | |
243 | mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels)); | |
244 | png_set_expand(png_ptr); | |
245 | } | |
790923a4 | 246 | |
02d1d628 AMH |
247 | number_passes = png_set_interlace_handling(png_ptr); |
248 | mm_log((1,"number of passes=%d\n",number_passes)); | |
249 | png_read_update_info(png_ptr, info_ptr); | |
790923a4 | 250 | |
1d7e3124 | 251 | im = i_img_8_new(width,height,channels); |
352c64ed TC |
252 | if (!im) { |
253 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
254 | return NULL; | |
255 | } | |
790923a4 | 256 | |
02d1d628 | 257 | for (pass = 0; pass < number_passes; pass++) |
faa9b3e7 | 258 | for (y = 0; y < height; y++) { png_read_row(png_ptr,(png_bytep) &(im->idata[channels*width*y]), NULL); } |
02d1d628 | 259 | |
790923a4 | 260 | png_read_end(png_ptr, info_ptr); |
02d1d628 | 261 | |
faa9b3e7 TC |
262 | get_png_tags(im, png_ptr, info_ptr); |
263 | ||
790923a4 | 264 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); |
02d1d628 | 265 | |
790923a4 | 266 | mm_log((1,"(0x%08X) <- i_readpng_scalar\n", im)); |
02d1d628 | 267 | |
790923a4 AMH |
268 | return im; |
269 | } | |
faa9b3e7 TC |
270 | |
271 | static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) { | |
272 | png_uint_32 xres, yres; | |
273 | int unit_type; | |
352c64ed | 274 | |
1d7e3124 | 275 | i_tags_set(&im->tags, "i_format", "png", -1); |
faa9b3e7 TC |
276 | if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) { |
277 | mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type)); | |
278 | if (unit_type == PNG_RESOLUTION_METER) { | |
2e41e30b TC |
279 | i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5); |
280 | i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5); | |
faa9b3e7 TC |
281 | } |
282 | else { | |
1d7e3124 TC |
283 | i_tags_setn(&im->tags, "i_xres", xres); |
284 | i_tags_setn(&im->tags, "i_yres", yres); | |
285 | i_tags_setn(&im->tags, "i_aspect_only", 1); | |
faa9b3e7 TC |
286 | } |
287 | } | |
288 | } |