]> git.imager.perl.org - imager.git/blame - PNG/impng.c
sub-module version bumps and Changes notes in prep for dev release
[imager.git] / PNG / impng.c
CommitLineData
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 28static 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 34static void
790923a4 35wiol_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 41static void
790923a4
AMH
42wiol_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
49static void
790923a4
AMH
50wiol_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
57int
58check_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 66undef_int
790923a4 67i_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 187static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
02d1d628
AMH
188
189i_img*
1d7e3124 190i_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
278static 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}