]>
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)); |
8d14daab TC |
76 | |
77 | i_clear_error(); | |
78 | ||
79 | if (im->xsize > PNG_UINT_31_MAX || im->ysize > PNG_UINT_31_MAX) { | |
80 | i_push_error(0, "image too large for PNG"); | |
81 | return 0; | |
82 | } | |
83 | ||
790923a4 AMH |
84 | height = im->ysize; |
85 | width = im->xsize; | |
02d1d628 AMH |
86 | |
87 | channels=im->channels; | |
88 | ||
790923a4 | 89 | if (channels > 2) { cspace = PNG_COLOR_TYPE_RGB; channels-=3; } |
02d1d628 AMH |
90 | else { cspace=PNG_COLOR_TYPE_GRAY; channels--; } |
91 | ||
92 | if (channels) cspace|=PNG_COLOR_MASK_ALPHA; | |
93 | mm_log((1,"cspace=%d\n",cspace)); | |
94 | ||
790923a4 | 95 | channels = im->channels; |
02d1d628 AMH |
96 | |
97 | /* Create and initialize the png_struct with the desired error handler | |
98 | * functions. If you want to use the default stderr and longjump method, | |
99 | * you can supply NULL for the last three parameters. We also check that | |
100 | * the library version is compatible with the one used at compile time, | |
101 | * in case we are using dynamically linked libraries. REQUIRED. | |
102 | */ | |
103 | ||
104 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); | |
105 | ||
790923a4 AMH |
106 | if (png_ptr == NULL) return 0; |
107 | ||
02d1d628 AMH |
108 | |
109 | /* Allocate/initialize the image information data. REQUIRED */ | |
110 | info_ptr = png_create_info_struct(png_ptr); | |
111 | ||
112 | if (info_ptr == NULL) { | |
a807aae6 | 113 | png_destroy_write_struct(&png_ptr, &info_ptr); |
790923a4 | 114 | return 0; |
02d1d628 AMH |
115 | } |
116 | ||
117 | /* Set error handling. REQUIRED if you aren't supplying your own | |
118 | * error hadnling functions in the png_create_write_struct() call. | |
119 | */ | |
f5b4354e | 120 | if (setjmp(png_jmpbuf(png_ptr))) { |
a807aae6 | 121 | png_destroy_write_struct(&png_ptr, &info_ptr); |
02d1d628 AMH |
122 | return(0); |
123 | } | |
124 | ||
790923a4 | 125 | png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data); |
02d1d628 AMH |
126 | |
127 | /* Set the image information here. Width and height are up to 2^31, | |
128 | * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on | |
129 | * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, | |
130 | * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, | |
131 | * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or | |
132 | * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST | |
133 | * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED | |
134 | */ | |
135 | ||
136 | png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace, | |
137 | PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); | |
138 | ||
faa9b3e7 TC |
139 | have_res = 1; |
140 | if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) { | |
141 | if (i_tags_get_float(&im->tags, "i_yres", 0, &yres)) | |
142 | ; /* nothing to do */ | |
143 | else | |
144 | yres = xres; | |
145 | } | |
146 | else { | |
147 | if (i_tags_get_float(&im->tags, "i_yres", 0, &yres)) | |
148 | xres = yres; | |
149 | else | |
150 | have_res = 0; | |
151 | } | |
152 | if (have_res) { | |
153 | aspect_only = 0; | |
154 | i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only); | |
155 | xres /= 0.0254; | |
156 | yres /= 0.0254; | |
157 | png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5, | |
158 | aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER); | |
159 | } | |
160 | ||
02d1d628 | 161 | png_write_info(png_ptr, info_ptr); |
790923a4 | 162 | |
faa9b3e7 TC |
163 | if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) { |
164 | for (y = 0; y < height; y++) | |
165 | png_write_row(png_ptr, (png_bytep) &(im->idata[channels*width*y])); | |
166 | } | |
167 | else { | |
168 | unsigned char *data = mymalloc(im->xsize * im->channels); | |
435e4a7d TC |
169 | for (y = 0; y < height; y++) { |
170 | i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels); | |
171 | png_write_row(png_ptr, (png_bytep)data); | |
faa9b3e7 | 172 | } |
435e4a7d | 173 | myfree(data); |
faa9b3e7 | 174 | } |
790923a4 | 175 | |
02d1d628 | 176 | png_write_end(png_ptr, info_ptr); |
790923a4 | 177 | |
a807aae6 | 178 | png_destroy_write_struct(&png_ptr, &info_ptr); |
02d1d628 | 179 | |
10461f9a TC |
180 | ig->closecb(ig); |
181 | ||
02d1d628 AMH |
182 | return(1); |
183 | } | |
184 | ||
185 | ||
186 | ||
faa9b3e7 | 187 | static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr); |
02d1d628 AMH |
188 | |
189 | i_img* | |
1d7e3124 | 190 | i_readpng_wiol(io_glue *ig) { |
02576e8d | 191 | i_img *im = NULL; |
02d1d628 AMH |
192 | png_structp png_ptr; |
193 | png_infop info_ptr; | |
790923a4 | 194 | png_uint_32 width, height; |
02d1d628 | 195 | int bit_depth, color_type, interlace_type; |
790923a4 AMH |
196 | int number_passes,y; |
197 | int channels,pass; | |
02d1d628 AMH |
198 | unsigned int sig_read; |
199 | ||
790923a4 | 200 | sig_read = 0; |
02d1d628 | 201 | |
1d7e3124 | 202 | mm_log((1,"i_readpng_wiol(ig %p)\n", ig)); |
02d1d628 AMH |
203 | |
204 | png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); | |
790923a4 | 205 | png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data); |
02d1d628 AMH |
206 | |
207 | info_ptr = png_create_info_struct(png_ptr); | |
208 | if (info_ptr == NULL) { | |
209 | png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); | |
210 | return NULL; | |
211 | } | |
212 | ||
f5b4354e | 213 | if (setjmp(png_jmpbuf(png_ptr))) { |
02576e8d | 214 | if (im) i_img_destroy(im); |
790923a4 | 215 | mm_log((1,"i_readpng_wiol: error.\n")); |
02d1d628 AMH |
216 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); |
217 | return NULL; | |
218 | } | |
790923a4 | 219 | |
02d1d628 AMH |
220 | png_set_sig_bytes(png_ptr, sig_read); |
221 | png_read_info(png_ptr, info_ptr); | |
222 | png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); | |
223 | ||
224 | mm_log((1, | |
790923a4 | 225 | "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n", |
02d1d628 AMH |
226 | width,height,bit_depth,color_type,interlace_type)); |
227 | ||
228 | CC2C[PNG_COLOR_TYPE_GRAY]=1; | |
229 | CC2C[PNG_COLOR_TYPE_PALETTE]=3; | |
230 | CC2C[PNG_COLOR_TYPE_RGB]=3; | |
231 | CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4; | |
232 | CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2; | |
790923a4 AMH |
233 | channels = CC2C[color_type]; |
234 | ||
235 | mm_log((1,"i_readpng_wiol: channels %d\n",channels)); | |
236 | ||
77157728 TC |
237 | if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) { |
238 | mm_log((1, "i_readpnm: image size exceeds limits\n")); | |
239 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
240 | return NULL; | |
241 | } | |
242 | ||
02d1d628 AMH |
243 | png_set_strip_16(png_ptr); |
244 | png_set_packing(png_ptr); | |
245 | if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); | |
246 | if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); | |
6829f5f6 AMH |
247 | |
248 | if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | |
249 | channels++; | |
250 | mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels)); | |
251 | png_set_expand(png_ptr); | |
252 | } | |
790923a4 | 253 | |
02d1d628 AMH |
254 | number_passes = png_set_interlace_handling(png_ptr); |
255 | mm_log((1,"number of passes=%d\n",number_passes)); | |
256 | png_read_update_info(png_ptr, info_ptr); | |
790923a4 | 257 | |
1d7e3124 | 258 | im = i_img_8_new(width,height,channels); |
352c64ed TC |
259 | if (!im) { |
260 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); | |
261 | return NULL; | |
262 | } | |
790923a4 | 263 | |
02d1d628 | 264 | for (pass = 0; pass < number_passes; pass++) |
faa9b3e7 | 265 | for (y = 0; y < height; y++) { png_read_row(png_ptr,(png_bytep) &(im->idata[channels*width*y]), NULL); } |
02d1d628 | 266 | |
790923a4 | 267 | png_read_end(png_ptr, info_ptr); |
02d1d628 | 268 | |
faa9b3e7 TC |
269 | get_png_tags(im, png_ptr, info_ptr); |
270 | ||
790923a4 | 271 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); |
02d1d628 | 272 | |
790923a4 | 273 | mm_log((1,"(0x%08X) <- i_readpng_scalar\n", im)); |
02d1d628 | 274 | |
790923a4 AMH |
275 | return im; |
276 | } | |
faa9b3e7 TC |
277 | |
278 | static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) { | |
279 | png_uint_32 xres, yres; | |
280 | int unit_type; | |
352c64ed | 281 | |
1d7e3124 | 282 | i_tags_set(&im->tags, "i_format", "png", -1); |
faa9b3e7 TC |
283 | if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) { |
284 | mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type)); | |
285 | if (unit_type == PNG_RESOLUTION_METER) { | |
2e41e30b TC |
286 | i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5); |
287 | i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5); | |
faa9b3e7 TC |
288 | } |
289 | else { | |
1d7e3124 TC |
290 | i_tags_setn(&im->tags, "i_xres", xres); |
291 | i_tags_setn(&im->tags, "i_yres", yres); | |
292 | i_tags_setn(&im->tags, "i_aspect_only", 1); | |
faa9b3e7 TC |
293 | } |
294 | } | |
295 | } |