]> git.imager.perl.org - imager.git/blob - PNG/impng.c
[rt #70388] debug mymalloc() no longer builds a string, just stores the pointer
[imager.git] / PNG / impng.c
1 #include "impng.h"
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
28 static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
29
30 #define PNG_BYTES_TO_CHECK 4
31  
32
33
34 static void
35 wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
36   io_glue *ig = png_get_io_ptr(png_ptr);
37   int rc = i_io_read(ig, data, length);
38   if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source.");
39 }
40
41 static void
42 wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
43   int rc;
44   io_glue *ig = png_get_io_ptr(png_ptr);
45   rc = i_io_write(ig, data, length);
46   if (rc != length) png_error(png_ptr, "Write error on an iolayer source.");
47 }
48
49 static void
50 wiol_flush_data(png_structp png_ptr) {
51   /* XXX : This needs to be added to the io layer */
52 }
53
54
55 /* Check function demo 
56
57 int
58 check_if_png(char *file_name, FILE **fp) {
59   char buf[PNG_BYTES_TO_CHECK];
60   if ((*fp = fopen(file_name, "rb")) != NULL) return 0;
61   if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) return 0;
62   return(!png_sig_cmp((png_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
63 }
64 */
65
66 undef_int
67 i_writepng_wiol(i_img *im, io_glue *ig) {
68   png_structp png_ptr;
69   png_infop info_ptr = NULL;
70   int width,height,y;
71   volatile int cspace,channels;
72   double xres, yres;
73   int aspect_only, have_res;
74
75   mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
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
84   height = im->ysize;
85   width  = im->xsize;
86
87   channels=im->channels;
88
89   if (channels > 2) { cspace = PNG_COLOR_TYPE_RGB; channels-=3; }
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
95   channels = im->channels;
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   
106   if (png_ptr == NULL) return 0;
107
108   
109   /* Allocate/initialize the image information data.  REQUIRED */
110   info_ptr = png_create_info_struct(png_ptr);
111
112   if (info_ptr == NULL) {
113     png_destroy_write_struct(&png_ptr, &info_ptr);
114     return 0;
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    */
120   if (setjmp(png_jmpbuf(png_ptr))) {
121     png_destroy_write_struct(&png_ptr, &info_ptr);
122     return(0);
123   }
124   
125   png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
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
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
161   png_write_info(png_ptr, info_ptr);
162
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);
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);
172     }
173     myfree(data);
174   }
175
176   png_write_end(png_ptr, info_ptr);
177
178   png_destroy_write_struct(&png_ptr, &info_ptr);
179
180   if (i_io_close(ig))
181     return 0;
182
183   return(1);
184 }
185
186
187
188 static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
189
190 i_img*
191 i_readpng_wiol(io_glue *ig) {
192   i_img *im = NULL;
193   png_structp png_ptr;
194   png_infop info_ptr;
195   png_uint_32 width, height;
196   int bit_depth, color_type, interlace_type;
197   int number_passes,y;
198   int channels,pass;
199   unsigned int sig_read;
200
201   sig_read  = 0;
202
203   mm_log((1,"i_readpng_wiol(ig %p)\n", ig));
204
205   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
206   png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
207   
208   info_ptr = png_create_info_struct(png_ptr);
209   if (info_ptr == NULL) {
210     png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
211     return NULL;
212   }
213   
214   if (setjmp(png_jmpbuf(png_ptr))) {
215     if (im) i_img_destroy(im);
216     mm_log((1,"i_readpng_wiol: error.\n"));
217     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
218     return NULL;
219   }
220
221   png_set_sig_bytes(png_ptr, sig_read);
222   png_read_info(png_ptr, info_ptr);
223   png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
224   
225   mm_log((1,
226           "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n",
227           width,height,bit_depth,color_type,interlace_type));
228   
229   CC2C[PNG_COLOR_TYPE_GRAY]=1;
230   CC2C[PNG_COLOR_TYPE_PALETTE]=3;
231   CC2C[PNG_COLOR_TYPE_RGB]=3;
232   CC2C[PNG_COLOR_TYPE_RGB_ALPHA]=4;
233   CC2C[PNG_COLOR_TYPE_GRAY_ALPHA]=2;
234   channels = CC2C[color_type];
235
236   mm_log((1,"i_readpng_wiol: channels %d\n",channels));
237
238   if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
239     mm_log((1, "i_readpnm: image size exceeds limits\n"));
240     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
241     return NULL;
242   }
243
244   png_set_strip_16(png_ptr);
245   png_set_packing(png_ptr);
246   if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr);
247   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr);
248
249   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
250     channels++;
251     mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
252     png_set_expand(png_ptr);
253   }
254   
255   number_passes = png_set_interlace_handling(png_ptr);
256   mm_log((1,"number of passes=%d\n",number_passes));
257   png_read_update_info(png_ptr, info_ptr);
258   
259   im = i_img_8_new(width,height,channels);
260   if (!im) {
261     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
262     return NULL;
263   }
264
265   for (pass = 0; pass < number_passes; pass++)
266     for (y = 0; y < height; y++) { png_read_row(png_ptr,(png_bytep) &(im->idata[channels*width*y]), NULL); }
267   
268   png_read_end(png_ptr, info_ptr); 
269   
270   get_png_tags(im, png_ptr, info_ptr);
271
272   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
273   
274   mm_log((1,"(0x%08X) <- i_readpng_scalar\n", im));  
275   
276   return im;
277 }
278
279 static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
280   png_uint_32 xres, yres;
281   int unit_type;
282
283   i_tags_set(&im->tags, "i_format", "png", -1);
284   if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
285     mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type));
286     if (unit_type == PNG_RESOLUTION_METER) {
287       i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5);
288       i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5);
289     }
290     else {
291       i_tags_setn(&im->tags, "i_xres", xres);
292       i_tags_setn(&im->tags, "i_yres", yres);
293       i_tags_setn(&im->tags, "i_aspect_only", 1);
294     }
295   }
296 }