- start of external Imager API access:
[imager.git] / png.c
CommitLineData
790923a4 1#include "iolayer.h"
92bda632 2#include "imageri.h"
02d1d628
AMH
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
b33c08f8 29static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA];
02d1d628
AMH
30
31#define PNG_BYTES_TO_CHECK 4
32
33
34
02d1d628 35static void
790923a4
AMH
36wiol_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.");
02d1d628
AMH
40}
41
02d1d628 42static void
790923a4
AMH
43wiol_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.");
02d1d628
AMH
48}
49
50static void
790923a4
AMH
51wiol_flush_data(png_structp png_ptr) {
52 /* XXX : This needs to be added to the io layer */
02d1d628 53}
02d1d628
AMH
54
55
15dc61b6
AMH
56/* Check function demo
57
02d1d628
AMH
58int
59check_if_png(char *file_name, FILE **fp) {
790923a4 60 char buf[PNG_BYTES_TO_CHECK];
790923a4 61 if ((*fp = fopen(file_name, "rb")) != NULL) return 0;
790923a4 62 if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) return 0;
790923a4 63 return(!png_sig_cmp((png_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
02d1d628 64}
15dc61b6 65*/
02d1d628 66
02d1d628 67undef_int
790923a4 68i_writepng_wiol(i_img *im, io_glue *ig) {
02d1d628
AMH
69 png_structp png_ptr;
70 png_infop info_ptr;
71 int width,height,y;
72 volatile int cspace,channels;
faa9b3e7
TC
73 double xres, yres;
74 int aspect_only, have_res;
02d1d628 75
790923a4
AMH
76 io_glue_commit_types(ig);
77 mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
02d1d628 78
790923a4
AMH
79 height = im->ysize;
80 width = im->xsize;
02d1d628
AMH
81
82 channels=im->channels;
83
790923a4 84 if (channels > 2) { cspace = PNG_COLOR_TYPE_RGB; channels-=3; }
02d1d628
AMH
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
790923a4 90 channels = im->channels;
02d1d628
AMH
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
790923a4
AMH
101 if (png_ptr == NULL) return 0;
102
02d1d628
AMH
103
104 /* Allocate/initialize the image information data. REQUIRED */
105 info_ptr = png_create_info_struct(png_ptr);
106
107 if (info_ptr == NULL) {
02d1d628 108 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
790923a4 109 return 0;
02d1d628
AMH
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)) {
02d1d628
AMH
116 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
117 return(0);
118 }
119
790923a4
AMH
120 png_set_write_fn(png_ptr, (png_voidp) (ig), wiol_write_data, wiol_flush_data);
121 png_ptr->io_ptr = (png_voidp) ig;
02d1d628
AMH
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
faa9b3e7
TC
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
02d1d628 157 png_write_info(png_ptr, info_ptr);
790923a4 158
faa9b3e7
TC
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 {
faa9b3e7
TC
173 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
174 return 0;
175 }
176 }
790923a4 177
02d1d628 178 png_write_end(png_ptr, info_ptr);
790923a4 179
02d1d628
AMH
180 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
181
10461f9a
TC
182 ig->closecb(ig);
183
02d1d628
AMH
184 return(1);
185}
186
187
188
faa9b3e7 189static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
02d1d628
AMH
190
191i_img*
790923a4 192i_readpng_wiol(io_glue *ig, int length) {
02d1d628
AMH
193 i_img *im;
194 png_structp png_ptr;
195 png_infop info_ptr;
790923a4 196 png_uint_32 width, height;
02d1d628 197 int bit_depth, color_type, interlace_type;
790923a4
AMH
198 int number_passes,y;
199 int channels,pass;
02d1d628
AMH
200 unsigned int sig_read;
201
790923a4 202 sig_read = 0;
02d1d628 203
790923a4
AMH
204 io_glue_commit_types(ig);
205 mm_log((1,"i_readpng_wiol(ig %p, length %d)\n", ig, length));
02d1d628
AMH
206
207 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
790923a4 208 png_set_read_fn(png_ptr, (png_voidp) (ig), wiol_read_data);
02d1d628
AMH
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)) {
790923a4 217 mm_log((1,"i_readpng_wiol: error.\n"));
02d1d628
AMH
218 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
219 return NULL;
220 }
790923a4
AMH
221
222 png_ptr->io_ptr = (png_voidp) ig;
02d1d628
AMH
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,
790923a4 228 "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n",
02d1d628
AMH
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;
790923a4
AMH
236 channels = CC2C[color_type];
237
238 mm_log((1,"i_readpng_wiol: channels %d\n",channels));
239
77157728
TC
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
02d1d628
AMH
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);
6829f5f6
AMH
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 }
790923a4 256
02d1d628
AMH
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);
790923a4
AMH
260
261 im = i_img_empty_ch(NULL,width,height,channels);
352c64ed
TC
262 if (!im) {
263 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
264 return NULL;
265 }
790923a4 266
02d1d628 267 for (pass = 0; pass < number_passes; pass++)
faa9b3e7 268 for (y = 0; y < height; y++) { png_read_row(png_ptr,(png_bytep) &(im->idata[channels*width*y]), NULL); }
02d1d628 269
790923a4 270 png_read_end(png_ptr, info_ptr);
02d1d628 271
faa9b3e7
TC
272 get_png_tags(im, png_ptr, info_ptr);
273
790923a4 274 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
02d1d628 275
790923a4 276 mm_log((1,"(0x%08X) <- i_readpng_scalar\n", im));
02d1d628 277
790923a4
AMH
278 return im;
279}
faa9b3e7
TC
280
281static 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;
352c64ed
TC
284
285 i_tags_add(&im->tags, "i_format", 0, "png", -1, 0);
faa9b3e7
TC
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) {
2e41e30b
TC
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);
faa9b3e7
TC
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}