- start of external Imager API access:
[imager.git] / png.c
1 #include "iolayer.h"
2 #include "imageri.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 static 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 static void
36 wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
37   io_glue *ig = (io_glue *)png_ptr->io_ptr;
38   int rc = ig->readcb(ig, data, length);
39   if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
40 }
41
42 static void
43 wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
44   int rc;
45   io_glue *ig = (io_glue *)png_ptr->io_ptr;
46   rc = ig->writecb(ig, data, length);
47   if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
48 }
49
50 static void
51 wiol_flush_data(png_structp png_ptr) {
52   /* XXX : This needs to be added to the io layer */
53 }
54
55
56 /* Check function demo 
57
58 int
59 check_if_png(char *file_name, FILE **fp) {
60   char buf[PNG_BYTES_TO_CHECK];
61   if ((*fp = fopen(file_name, "rb")) != NULL) return 0;
62   if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) return 0;
63   return(!png_sig_cmp((png_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
64 }
65 */
66
67 undef_int
68 i_writepng_wiol(i_img *im, io_glue *ig) {
69   png_structp png_ptr;
70   png_infop info_ptr;
71   int width,height,y;
72   volatile int cspace,channels;
73   double xres, yres;
74   int aspect_only, have_res;
75
76   io_glue_commit_types(ig);
77   mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
78   
79   height = im->ysize;
80   width  = im->xsize;
81
82   channels=im->channels;
83
84   if (channels > 2) { cspace = PNG_COLOR_TYPE_RGB; channels-=3; }
85   else { cspace=PNG_COLOR_TYPE_GRAY; channels--; }
86   
87   if (channels) cspace|=PNG_COLOR_MASK_ALPHA;
88   mm_log((1,"cspace=%d\n",cspace));
89
90   channels = im->channels;
91
92   /* Create and initialize the png_struct with the desired error handler
93    * functions.  If you want to use the default stderr and longjump method,
94    * you can supply NULL for the last three parameters.  We also check that
95    * the library version is compatible with the one used at compile time,
96    * in case we are using dynamically linked libraries.  REQUIRED.
97    */
98   
99   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
100   
101   if (png_ptr == NULL) return 0;
102
103   
104   /* Allocate/initialize the image information data.  REQUIRED */
105   info_ptr = png_create_info_struct(png_ptr);
106
107   if (info_ptr == NULL) {
108     png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
109     return 0;
110   }
111   
112   /* Set error handling.  REQUIRED if you aren't supplying your own
113    * error hadnling functions in the png_create_write_struct() call.
114    */
115   if (setjmp(png_ptr->jmpbuf)) {
116     png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
117     return(0);
118   }
119   
120   png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
121   png_ptr->io_ptr = (png_voidp) ig;
122
123   /* Set the image information here.  Width and height are up to 2^31,
124    * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
125    * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
126    * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
127    * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
128    * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
129    * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
130    */
131
132   png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace,
133                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
134
135   have_res = 1;
136   if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
137     if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
138       ; /* nothing to do */
139     else
140       yres = xres;
141   }
142   else {
143     if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
144       xres = yres;
145     else
146       have_res = 0;
147   }
148   if (have_res) {
149     aspect_only = 0;
150     i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
151     xres /= 0.0254;
152     yres /= 0.0254;
153     png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5, 
154                  aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
155   }
156
157   png_write_info(png_ptr, info_ptr);
158
159   if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) {
160     for (y = 0; y < height; y++) 
161       png_write_row(png_ptr, (png_bytep) &(im->idata[channels*width*y]));
162   }
163   else {
164     unsigned char *data = mymalloc(im->xsize * im->channels);
165     if (data) {
166       for (y = 0; y < height; y++) {
167         i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
168         png_write_row(png_ptr, (png_bytep)data);
169       }
170       myfree(data);
171     }
172     else {
173       png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
174       return 0;
175     }
176   }
177
178   png_write_end(png_ptr, info_ptr);
179
180   png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
181
182   ig->closecb(ig);
183
184   return(1);
185 }
186
187
188
189 static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
190
191 i_img*
192 i_readpng_wiol(io_glue *ig, int length) {
193   i_img *im;
194   png_structp png_ptr;
195   png_infop info_ptr;
196   png_uint_32 width, height;
197   int bit_depth, color_type, interlace_type;
198   int number_passes,y;
199   int channels,pass;
200   unsigned int sig_read;
201
202   sig_read  = 0;
203
204   io_glue_commit_types(ig);
205   mm_log((1,"i_readpng_wiol(ig %p, length %d)\n", ig, length));
206
207   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
208   png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
209   
210   info_ptr = png_create_info_struct(png_ptr);
211   if (info_ptr == NULL) {
212     png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
213     return NULL;
214   }
215   
216   if (setjmp(png_ptr->jmpbuf)) {
217     mm_log((1,"i_readpng_wiol: error.\n"));
218     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
219     return NULL;
220   }
221
222   png_ptr->io_ptr = (png_voidp) ig;
223   png_set_sig_bytes(png_ptr, sig_read);
224   png_read_info(png_ptr, info_ptr);
225   png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
226   
227   mm_log((1,
228           "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n",
229           width,height,bit_depth,color_type,interlace_type));
230   
231   CC2C[PNG_COLOR_TYPE_GRAY]=1;
232   CC2C[PNG_COLOR_TYPE_PALETTE]=3;
233   CC2C[PNG_COLOR_TYPE_RGB]=3;
234   CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
235   CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
236   channels = CC2C[color_type];
237
238   mm_log((1,"i_readpng_wiol: channels %d\n",channels));
239
240   if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
241     mm_log((1, "i_readpnm: image size exceeds limits\n"));
242     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
243     return NULL;
244   }
245
246   png_set_strip_16(png_ptr);
247   png_set_packing(png_ptr);
248   if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr);
249   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr);
250
251   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
252     channels++;
253     mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
254     png_set_expand(png_ptr);
255   }
256   
257   number_passes = png_set_interlace_handling(png_ptr);
258   mm_log((1,"number of passes=%d\n",number_passes));
259   png_read_update_info(png_ptr, info_ptr);
260   
261   im = i_img_empty_ch(NULL,width,height,channels);
262   if (!im) {
263     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
264     return NULL;
265   }
266
267   for (pass = 0; pass < number_passes; pass++)
268     for (y = 0; y < height; y++) { png_read_row(png_ptr,(png_bytep) &(im->idata[channels*width*y]), NULL); }
269   
270   png_read_end(png_ptr, info_ptr); 
271   
272   get_png_tags(im, png_ptr, info_ptr);
273
274   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
275   
276   mm_log((1,"(0x%08X) <- i_readpng_scalar\n", im));  
277   
278   return im;
279 }
280
281 static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
282   png_uint_32 xres, yres;
283   int unit_type;
284
285   i_tags_add(&im->tags, "i_format", 0, "png", -1, 0);
286   if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
287     mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type));
288     if (unit_type == PNG_RESOLUTION_METER) {
289       i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
290       i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
291     }
292     else {
293       i_tags_addn(&im->tags, "i_xres", 0, xres);
294       i_tags_addn(&im->tags, "i_yres", 0, yres);
295       i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
296     }
297   }
298 }