]> git.imager.perl.org - imager.git/blob - png.c
tests missing from MANIFEST
[imager.git] / png.c
1 #include "iolayer.h"
2 #include "image.h"
3 #include "png.h"
4
5 /* Check to see if a file is a PNG file using png_sig_cmp().  png_sig_cmp()
6  * returns zero if the image is a PNG and nonzero if it isn't a PNG.
7  *
8  * The function check_if_png() shown here, but not used, returns nonzero (true)
9  * if the file can be opened and is a PNG, 0 (false) otherwise.
10  *
11  * If this call is successful, and you are going to keep the file open,
12  * you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once
13  * you have created the png_ptr, so that libpng knows your application
14  * has read that many bytes from the start of the file.  Make sure you
15  * don't call png_set_sig_bytes() with more than 8 bytes read or give it
16  * an incorrect number of bytes read, or you will either have read too
17  * many bytes (your fault), or you are telling libpng to read the wrong
18  * number of magic bytes (also your fault).
19  *
20  * Many applications already read the first 2 or 4 bytes from the start
21  * of the image to determine the file type, so it would be easiest just
22  * to pass the bytes to png_sig_cmp() or even skip that if you know
23  * you have a PNG file, and call png_set_sig_bytes().
24  */
25
26 /* this is a way to get number of channels from color space 
27  * Color code to channel number */
28
29 int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
30
31 #define PNG_BYTES_TO_CHECK 4
32  
33
34
35
36
37
38
39
40
41 /*
42   png_set_read_fn(png_structp read_ptr, voidp read_io_ptr, png_rw_ptr read_data_fn)
43   png_set_write_fn(png_structp write_ptr, voidp write_io_ptr, png_rw_ptr write_data_fn,
44   png_flush_ptr output_flush_fn);
45   voidp read_io_ptr = png_get_io_ptr(read_ptr);
46   voidp write_io_ptr = png_get_io_ptr(write_ptr);
47 */
48
49
50
51
52 static void
53 wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
54   io_glue *ig = (io_glue *)png_ptr->io_ptr;
55   int rc = ig->readcb(ig, data, length);
56   if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
57 }
58
59 static void
60 wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
61   int rc;
62   io_glue *ig = (io_glue *)png_ptr->io_ptr;
63   rc = ig->writecb(ig, data, length);
64   if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
65 }
66
67 static void
68 wiol_flush_data(png_structp png_ptr) {
69   /* XXX : This needs to be added to the io layer */
70 }
71
72
73 int
74 check_if_png(char *file_name, FILE **fp) {
75   char buf[PNG_BYTES_TO_CHECK];
76    
77   /* Open the prospective PNG file. */
78   if ((*fp = fopen(file_name, "rb")) != NULL) return 0;
79    
80   /* Read in some of the signature bytes */
81   if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) return 0;
82
83   /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.
84      Return nonzero (true) if they match */
85
86   return(!png_sig_cmp((png_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
87 }
88
89
90
91 undef_int
92 i_writepng(i_img *im, int fd) {
93   FILE *fp;
94   png_structp png_ptr;
95   png_infop info_ptr;
96   int width,height,y;
97   volatile int cspace,channels;
98   double xres, yres;
99   int aspect_only, have_res;
100   double offx, offy;
101   char offunit[20] = "pixel";
102
103   mm_log((1,"i_writepng(0x%x,fd %d)\n",im,fd));
104   
105   if ((fp = fdopen(fd,"w")) == NULL) {
106     mm_log((1,"can't fdopen.\n"));
107     exit(1);
108   }
109
110   height=im->ysize;
111   width=im->xsize;
112
113   channels=im->channels;
114
115   if (channels>2) { cspace=PNG_COLOR_TYPE_RGB; channels-=3; }
116   else { cspace=PNG_COLOR_TYPE_GRAY; channels--; }
117   
118   if (channels) cspace|=PNG_COLOR_MASK_ALPHA;
119   mm_log((1,"cspace=%d\n",cspace));
120
121   channels=im->channels;
122
123   /* Create and initialize the png_struct with the desired error handler
124    * functions.  If you want to use the default stderr and longjump method,
125    * you can supply NULL for the last three parameters.  We also check that
126    * the library version is compatible with the one used at compile time,
127    * in case we are using dynamically linked libraries.  REQUIRED.
128    */
129   
130   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
131   
132   if (png_ptr == NULL) {
133     fclose(fp);
134     return 0;
135   }
136   
137   /* Allocate/initialize the image information data.  REQUIRED */
138   info_ptr = png_create_info_struct(png_ptr);
139
140   if (info_ptr == NULL) {
141     fclose(fp);
142     png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
143     return(0);
144   }
145   
146   /* Set error handling.  REQUIRED if you aren't supplying your own
147    * error hadnling functions in the png_create_write_struct() call.
148    */
149   if (setjmp(png_ptr->jmpbuf)) {
150     /* If we get here, we had a problem reading the file */
151     fclose(fp);
152     png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
153     return(0);
154   }
155   
156   png_init_io(png_ptr, fp);
157
158   /* Set the image information here.  Width and height are up to 2^31,
159    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
160    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
161    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
162    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
163    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
164    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
165    */
166
167   png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace,
168                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
169
170   have_res = 1;
171   if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
172     if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
173       ; /* nothing to do */
174     else
175       yres = xres;
176   }
177   else {
178     if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
179       xres = yres;
180     else
181       have_res = 0;
182   }
183   if (have_res) {
184     aspect_only = 0;
185     i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
186     xres /= 0.0254;
187     yres /= 0.0254;
188     png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5, 
189                  aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
190   }
191
192   png_write_info(png_ptr, info_ptr);
193   if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) {
194     for (y = 0; y < height; y++) 
195       png_write_row(png_ptr, (png_bytep) &(im->idata[channels*width*y]));
196   }
197   else {
198     unsigned char *data = mymalloc(im->xsize * im->channels);
199     if (data) {
200       for (y = 0; y < height; y++) {
201         i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
202         png_write_row(png_ptr, (png_bytep)data);
203       }
204       myfree(data);
205     }
206     else {
207       fclose(fp);
208       png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
209       return 0;
210     }
211   }
212   png_write_end(png_ptr, info_ptr);
213   png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
214
215   fclose(fp);
216   return(1);
217 }
218
219
220
221 undef_int
222 i_writepng_wiol(i_img *im, io_glue *ig) {
223   FILE *fp;
224   png_structp png_ptr;
225   png_infop info_ptr;
226   int width,height,y;
227   volatile int cspace,channels;
228   double xres, yres;
229   int aspect_only, have_res;
230   double offx, offy;
231   char offunit[20] = "pixel";
232
233   io_glue_commit_types(ig);
234   mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
235   
236   height = im->ysize;
237   width  = im->xsize;
238
239   channels=im->channels;
240
241   if (channels > 2) { cspace = PNG_COLOR_TYPE_RGB; channels-=3; }
242   else { cspace=PNG_COLOR_TYPE_GRAY; channels--; }
243   
244   if (channels) cspace|=PNG_COLOR_MASK_ALPHA;
245   mm_log((1,"cspace=%d\n",cspace));
246
247   channels = im->channels;
248
249   /* Create and initialize the png_struct with the desired error handler
250    * functions.  If you want to use the default stderr and longjump method,
251    * you can supply NULL for the last three parameters.  We also check that
252    * the library version is compatible with the one used at compile time,
253    * in case we are using dynamically linked libraries.  REQUIRED.
254    */
255   
256   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
257   
258   if (png_ptr == NULL) return 0;
259
260   
261   /* Allocate/initialize the image information data.  REQUIRED */
262   info_ptr = png_create_info_struct(png_ptr);
263
264   if (info_ptr == NULL) {
265     png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
266     return 0;
267   }
268   
269   /* Set error handling.  REQUIRED if you aren't supplying your own
270    * error hadnling functions in the png_create_write_struct() call.
271    */
272   if (setjmp(png_ptr->jmpbuf)) {
273     png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
274     return(0);
275   }
276   
277   png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
278   png_ptr->io_ptr = (png_voidp) ig;
279
280   /* Set the image information here.  Width and height are up to 2^31,
281    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
282    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
283    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
284    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
285    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
286    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
287    */
288
289   png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace,
290                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
291
292   have_res = 1;
293   if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
294     if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
295       ; /* nothing to do */
296     else
297       yres = xres;
298   }
299   else {
300     if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
301       xres = yres;
302     else
303       have_res = 0;
304   }
305   if (have_res) {
306     aspect_only = 0;
307     i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
308     xres /= 0.0254;
309     yres /= 0.0254;
310     png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5, 
311                  aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
312   }
313
314   png_write_info(png_ptr, info_ptr);
315
316   if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) {
317     for (y = 0; y < height; y++) 
318       png_write_row(png_ptr, (png_bytep) &(im->idata[channels*width*y]));
319   }
320   else {
321     unsigned char *data = mymalloc(im->xsize * im->channels);
322     if (data) {
323       for (y = 0; y < height; y++) {
324         i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
325         png_write_row(png_ptr, (png_bytep)data);
326       }
327       myfree(data);
328     }
329     else {
330       fclose(fp);
331       png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
332       return 0;
333     }
334   }
335
336   png_write_end(png_ptr, info_ptr);
337
338   png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
339
340   return(1);
341 }
342
343
344
345 static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
346
347 i_img*
348 i_readpng_wiol(io_glue *ig, int length) {
349   i_img *im;
350   png_structp png_ptr;
351   png_infop info_ptr;
352   png_uint_32 width, height;
353   int bit_depth, color_type, interlace_type;
354   int number_passes,y;
355   int channels,pass;
356   unsigned int sig_read;
357
358   sig_read  = 0;
359
360   io_glue_commit_types(ig);
361   mm_log((1,"i_readpng_wiol(ig %p, length %d)\n", ig, length));
362
363   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
364   png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
365   
366   info_ptr = png_create_info_struct(png_ptr);
367   if (info_ptr == NULL) {
368     png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
369     return NULL;
370   }
371   
372   if (setjmp(png_ptr->jmpbuf)) {
373     mm_log((1,"i_readpng_wiol: error.\n"));
374     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
375     return NULL;
376   }
377
378   png_ptr->io_ptr = (png_voidp) ig;
379   png_set_sig_bytes(png_ptr, sig_read);
380   png_read_info(png_ptr, info_ptr);
381   png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
382   
383   mm_log((1,
384           "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n",
385           width,height,bit_depth,color_type,interlace_type));
386   
387   CC2C[PNG_COLOR_TYPE_GRAY]=1;
388   CC2C[PNG_COLOR_TYPE_PALETTE]=3;
389   CC2C[PNG_COLOR_TYPE_RGB]=3;
390   CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
391   CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
392   channels = CC2C[color_type];
393
394   mm_log((1,"i_readpng_wiol: channels %d\n",channels));
395
396   png_set_strip_16(png_ptr);
397   png_set_packing(png_ptr);
398   if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr);
399   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr);
400
401   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
402     channels++;
403     mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
404     png_set_expand(png_ptr);
405   }
406   
407   number_passes = png_set_interlace_handling(png_ptr);
408   mm_log((1,"number of passes=%d\n",number_passes));
409   png_read_update_info(png_ptr, info_ptr);
410   
411   im = i_img_empty_ch(NULL,width,height,channels);
412
413   for (pass = 0; pass < number_passes; pass++)
414     for (y = 0; y < height; y++) { png_read_row(png_ptr,(png_bytep) &(im->idata[channels*width*y]), NULL); }
415   
416   png_read_end(png_ptr, info_ptr); 
417   
418   get_png_tags(im, png_ptr, info_ptr);
419
420   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
421   
422   mm_log((1,"(0x%08X) <- i_readpng_scalar\n", im));  
423   
424   return im;
425 }
426
427 static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
428   png_uint_32 xres, yres;
429   int unit_type;
430   if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
431     mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type));
432     if (unit_type == PNG_RESOLUTION_METER) {
433       i_tags_set_float(&im->tags, "i_xres", 0, xres * 0.0254);
434       i_tags_set_float(&im->tags, "i_yres", 0, xres * 0.0254);
435     }
436     else {
437       i_tags_addn(&im->tags, "i_xres", 0, xres);
438       i_tags_addn(&im->tags, "i_yres", 0, yres);
439       i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
440     }
441   }
442 }