]>
Commit | Line | Data |
---|---|---|
92bda632 | 1 | #include "imager.h" |
02d1d628 | 2 | #include "log.h" |
067d6bdc | 3 | #include "iolayer.h" |
92bda632 | 4 | #include "imageri.h" |
02d1d628 AMH |
5 | |
6 | #include <stdlib.h> | |
f0e7b647 | 7 | #include <errno.h> |
02d1d628 AMH |
8 | |
9 | ||
10 | /* | |
11 | =head1 NAME | |
12 | ||
13 | pnm.c - implements reading and writing ppm/pnm/pbm files, uses io layer. | |
14 | ||
15 | =head1 SYNOPSIS | |
16 | ||
17 | io_glue *ig = io_new_fd( fd ); | |
9c106321 | 18 | i_img *im = i_readpnm_wiol(ig, 0); // no limit on how much is read |
02d1d628 AMH |
19 | // or |
20 | io_glue *ig = io_new_fd( fd ); | |
21 | return_code = i_writepnm_wiol(im, ig); | |
22 | ||
23 | =head1 DESCRIPTION | |
24 | ||
25 | pnm.c implements the basic functions to read and write portable | |
26 | anymap files. It uses the iolayer and needs either a seekable source | |
27 | or an entire memory mapped buffer. | |
28 | ||
29 | =head1 FUNCTION REFERENCE | |
30 | ||
31 | Some of these functions are internal. | |
32 | ||
b8c2033e | 33 | =over |
02d1d628 AMH |
34 | |
35 | =cut | |
36 | */ | |
37 | ||
38 | ||
02d1d628 AMH |
39 | #define misspace(x) (x==' ' || x=='\n' || x=='\r' || x=='\t' || x=='\f' || x=='\v') |
40 | #define misnumber(x) (x <= '9' && x>='0') | |
41 | ||
42 | static char *typenames[]={"ascii pbm", "ascii pgm", "ascii ppm", "binary pbm", "binary pgm", "binary ppm"}; | |
43 | ||
44 | /* | |
6d5c85a2 | 45 | =item skip_spaces(ig) |
02d1d628 AMH |
46 | |
47 | Advances in stream until it is positioned at a | |
48 | non white space character. (internal) | |
49 | ||
6d5c85a2 | 50 | ig - io_glue |
02d1d628 AMH |
51 | |
52 | =cut | |
53 | */ | |
54 | ||
55 | static | |
56 | int | |
6d5c85a2 TC |
57 | skip_spaces(io_glue *ig) { |
58 | int c; | |
59 | while( (c = i_io_peekc(ig)) != EOF && misspace(c) ) { | |
60 | if ( i_io_getc(ig) == EOF ) | |
61 | break; | |
62 | } | |
63 | if (c == EOF) | |
64 | return 0; | |
65 | ||
02d1d628 AMH |
66 | return 1; |
67 | } | |
68 | ||
69 | ||
70 | /* | |
6d5c85a2 | 71 | =item skip_comment(ig) |
02d1d628 AMH |
72 | |
73 | Advances in stream over whitespace and a comment if one is found. (internal) | |
74 | ||
6d5c85a2 | 75 | ig - io_glue object |
02d1d628 AMH |
76 | |
77 | =cut | |
78 | */ | |
79 | ||
80 | static | |
81 | int | |
6d5c85a2 TC |
82 | skip_comment(io_glue *ig) { |
83 | int c; | |
02d1d628 | 84 | |
6d5c85a2 TC |
85 | if (!skip_spaces(ig)) |
86 | return 0; | |
02d1d628 | 87 | |
6d5c85a2 TC |
88 | if ((c = i_io_peekc(ig)) == EOF) |
89 | return 0; | |
90 | ||
91 | if (c == '#') { | |
92 | while( (c = i_io_peekc(ig)) != EOF && (c != '\n' && c != '\r') ) { | |
93 | if ( i_io_getc(ig) == EOF ) | |
94 | break; | |
02d1d628 AMH |
95 | } |
96 | } | |
6d5c85a2 TC |
97 | if (c == EOF) |
98 | return 0; | |
02d1d628 AMH |
99 | |
100 | return 1; | |
101 | } | |
102 | ||
103 | ||
104 | /* | |
105 | =item gnum(mb, i) | |
106 | ||
107 | Fetches the next number from stream and stores in i, returns true | |
108 | on success else false. | |
109 | ||
110 | mb - buffer object | |
111 | i - integer to store result in | |
112 | ||
113 | =cut | |
114 | */ | |
115 | ||
116 | static | |
117 | int | |
6d5c85a2 TC |
118 | gnum(io_glue *ig, int *i) { |
119 | int c; | |
02d1d628 AMH |
120 | *i = 0; |
121 | ||
6d5c85a2 | 122 | if (!skip_spaces(ig)) return 0; |
02d1d628 | 123 | |
6d5c85a2 | 124 | if ((c = i_io_peekc(ig)) == EOF) |
9c106321 | 125 | return 0; |
6d5c85a2 | 126 | if (!misnumber(c)) |
9c106321 | 127 | return 0; |
6d5c85a2 TC |
128 | while( (c = i_io_peekc(ig)) != EOF && misnumber(c) ) { |
129 | int work = *i * 10 + (c - '0'); | |
8d14daab TC |
130 | if (work < *i) { |
131 | /* overflow */ | |
132 | i_push_error(0, "integer overflow"); | |
133 | return 0; | |
134 | } | |
135 | *i = work; | |
215d9ead | 136 | i_io_nextc(ig); |
02d1d628 | 137 | } |
6d5c85a2 | 138 | |
02d1d628 AMH |
139 | return 1; |
140 | } | |
141 | ||
9c106321 TC |
142 | static |
143 | i_img * | |
6d5c85a2 | 144 | read_pgm_ppm_bin8(io_glue *ig, i_img *im, int width, int height, |
d87dc9a4 | 145 | int channels, int maxval, int allow_incomplete) { |
9c106321 TC |
146 | i_color *line, *linep; |
147 | int read_size; | |
148 | unsigned char *read_buf, *readp; | |
149 | int x, y, ch; | |
150 | int rounder = maxval / 2; | |
151 | ||
152 | line = mymalloc(width * sizeof(i_color)); | |
153 | read_size = channels * width; | |
154 | read_buf = mymalloc(read_size); | |
155 | for(y=0;y<height;y++) { | |
156 | linep = line; | |
157 | readp = read_buf; | |
6d5c85a2 | 158 | if (i_io_read(ig, read_buf, read_size) != read_size) { |
9c106321 TC |
159 | myfree(line); |
160 | myfree(read_buf); | |
d87dc9a4 | 161 | if (allow_incomplete) { |
9c106321 TC |
162 | i_tags_setn(&im->tags, "i_incomplete", 1); |
163 | i_tags_setn(&im->tags, "i_lines_read", y); | |
164 | return im; | |
165 | } | |
166 | else { | |
167 | i_push_error(0, "short read - file truncated?"); | |
168 | i_img_destroy(im); | |
169 | return NULL; | |
170 | } | |
171 | } | |
172 | if (maxval == 255) { | |
173 | for(x=0; x<width; x++) { | |
174 | for(ch=0; ch<channels; ch++) { | |
175 | linep->channel[ch] = *readp++; | |
176 | } | |
177 | ++linep; | |
178 | } | |
179 | } | |
180 | else { | |
181 | for(x=0; x<width; x++) { | |
182 | for(ch=0; ch<channels; ch++) { | |
183 | /* we just clamp samples to the correct range */ | |
184 | unsigned sample = *readp++; | |
185 | if (sample > maxval) | |
186 | sample = maxval; | |
187 | linep->channel[ch] = (sample * 255 + rounder) / maxval; | |
188 | } | |
189 | ++linep; | |
190 | } | |
191 | } | |
192 | i_plin(im, 0, width, y, line); | |
193 | } | |
194 | myfree(read_buf); | |
195 | myfree(line); | |
196 | ||
197 | return im; | |
198 | } | |
199 | ||
200 | static | |
201 | i_img * | |
6d5c85a2 | 202 | read_pgm_ppm_bin16(io_glue *ig, i_img *im, int width, int height, |
d87dc9a4 | 203 | int channels, int maxval, int allow_incomplete) { |
9c106321 TC |
204 | i_fcolor *line, *linep; |
205 | int read_size; | |
206 | unsigned char *read_buf, *readp; | |
207 | int x, y, ch; | |
208 | double maxvalf = maxval; | |
209 | ||
210 | line = mymalloc(width * sizeof(i_fcolor)); | |
211 | read_size = channels * width * 2; | |
212 | read_buf = mymalloc(read_size); | |
213 | for(y=0;y<height;y++) { | |
214 | linep = line; | |
215 | readp = read_buf; | |
6d5c85a2 | 216 | if (i_io_read(ig, read_buf, read_size) != read_size) { |
9c106321 TC |
217 | myfree(line); |
218 | myfree(read_buf); | |
d87dc9a4 | 219 | if (allow_incomplete) { |
9c106321 TC |
220 | i_tags_setn(&im->tags, "i_incomplete", 1); |
221 | i_tags_setn(&im->tags, "i_lines_read", y); | |
222 | return im; | |
223 | } | |
224 | else { | |
225 | i_push_error(0, "short read - file truncated?"); | |
226 | i_img_destroy(im); | |
227 | return NULL; | |
228 | } | |
229 | } | |
230 | for(x=0; x<width; x++) { | |
231 | for(ch=0; ch<channels; ch++) { | |
232 | unsigned sample = (readp[0] << 8) + readp[1]; | |
233 | if (sample > maxval) | |
234 | sample = maxval; | |
235 | readp += 2; | |
236 | linep->channel[ch] = sample / maxvalf; | |
237 | } | |
238 | ++linep; | |
239 | } | |
240 | i_plinf(im, 0, width, y, line); | |
241 | } | |
242 | myfree(read_buf); | |
243 | myfree(line); | |
244 | ||
245 | return im; | |
246 | } | |
247 | ||
248 | static | |
249 | i_img * | |
6d5c85a2 | 250 | read_pbm_bin(io_glue *ig, i_img *im, int width, int height, int allow_incomplete) { |
9c106321 TC |
251 | i_palidx *line, *linep; |
252 | int read_size; | |
253 | unsigned char *read_buf, *readp; | |
254 | int x, y; | |
255 | unsigned mask; | |
256 | ||
257 | line = mymalloc(width * sizeof(i_palidx)); | |
258 | read_size = (width + 7) / 8; | |
259 | read_buf = mymalloc(read_size); | |
260 | for(y = 0; y < height; y++) { | |
6d5c85a2 | 261 | if (i_io_read(ig, read_buf, read_size) != read_size) { |
9c106321 TC |
262 | myfree(line); |
263 | myfree(read_buf); | |
d87dc9a4 | 264 | if (allow_incomplete) { |
9c106321 TC |
265 | i_tags_setn(&im->tags, "i_incomplete", 1); |
266 | i_tags_setn(&im->tags, "i_lines_read", y); | |
267 | return im; | |
268 | } | |
269 | else { | |
270 | i_push_error(0, "short read - file truncated?"); | |
271 | i_img_destroy(im); | |
272 | return NULL; | |
273 | } | |
274 | } | |
275 | linep = line; | |
276 | readp = read_buf; | |
277 | mask = 0x80; | |
278 | for(x = 0; x < width; ++x) { | |
279 | *linep++ = *readp & mask ? 1 : 0; | |
280 | mask >>= 1; | |
281 | if (mask == 0) { | |
282 | ++readp; | |
283 | mask = 0x80; | |
284 | } | |
285 | } | |
286 | i_ppal(im, 0, width, y, line); | |
287 | } | |
288 | myfree(read_buf); | |
289 | myfree(line); | |
290 | ||
291 | return im; | |
292 | } | |
293 | ||
294 | /* unlike pgm/ppm pbm: | |
295 | - doesn't require spaces between samples (bits) | |
296 | - 1 (maxval) is black instead of white | |
297 | */ | |
298 | static | |
299 | i_img * | |
6d5c85a2 | 300 | read_pbm_ascii(io_glue *ig, i_img *im, int width, int height, int allow_incomplete) { |
9c106321 TC |
301 | i_palidx *line, *linep; |
302 | int x, y; | |
303 | ||
304 | line = mymalloc(width * sizeof(i_palidx)); | |
305 | for(y = 0; y < height; y++) { | |
306 | linep = line; | |
307 | for(x = 0; x < width; ++x) { | |
6d5c85a2 TC |
308 | int c; |
309 | skip_spaces(ig); | |
310 | if ((c = i_io_getc(ig)) == EOF || (c != '0' && c != '1')) { | |
9c106321 | 311 | myfree(line); |
d87dc9a4 | 312 | if (allow_incomplete) { |
9c106321 TC |
313 | i_tags_setn(&im->tags, "i_incomplete", 1); |
314 | i_tags_setn(&im->tags, "i_lines_read", y); | |
315 | return im; | |
316 | } | |
317 | else { | |
6d5c85a2 | 318 | if (c != EOF) |
9c106321 TC |
319 | i_push_error(0, "invalid data for ascii pnm"); |
320 | else | |
321 | i_push_error(0, "short read - file truncated?"); | |
322 | i_img_destroy(im); | |
323 | return NULL; | |
324 | } | |
325 | } | |
6d5c85a2 | 326 | *linep++ = c == '0' ? 0 : 1; |
9c106321 TC |
327 | } |
328 | i_ppal(im, 0, width, y, line); | |
329 | } | |
330 | myfree(line); | |
331 | ||
332 | return im; | |
333 | } | |
334 | ||
335 | static | |
336 | i_img * | |
6d5c85a2 | 337 | read_pgm_ppm_ascii(io_glue *ig, i_img *im, int width, int height, int channels, |
d87dc9a4 | 338 | int maxval, int allow_incomplete) { |
9c106321 TC |
339 | i_color *line, *linep; |
340 | int x, y, ch; | |
341 | int rounder = maxval / 2; | |
342 | ||
343 | line = mymalloc(width * sizeof(i_color)); | |
344 | for(y=0;y<height;y++) { | |
345 | linep = line; | |
346 | for(x=0; x<width; x++) { | |
347 | for(ch=0; ch<channels; ch++) { | |
348 | int sample; | |
349 | ||
6d5c85a2 | 350 | if (!gnum(ig, &sample)) { |
9c106321 | 351 | myfree(line); |
d87dc9a4 | 352 | if (allow_incomplete) { |
9c106321 TC |
353 | i_tags_setn(&im->tags, "i_incomplete", 1); |
354 | i_tags_setn(&im->tags, "i_lines_read", 1); | |
355 | return im; | |
356 | } | |
357 | else { | |
6d5c85a2 | 358 | if (i_io_peekc(ig) != EOF) |
9c106321 TC |
359 | i_push_error(0, "invalid data for ascii pnm"); |
360 | else | |
361 | i_push_error(0, "short read - file truncated?"); | |
362 | i_img_destroy(im); | |
363 | return NULL; | |
364 | } | |
365 | } | |
366 | if (sample > maxval) | |
367 | sample = maxval; | |
368 | linep->channel[ch] = (sample * 255 + rounder) / maxval; | |
369 | } | |
370 | ++linep; | |
371 | } | |
372 | i_plin(im, 0, width, y, line); | |
373 | } | |
374 | myfree(line); | |
375 | ||
376 | return im; | |
377 | } | |
378 | ||
379 | static | |
380 | i_img * | |
6d5c85a2 | 381 | read_pgm_ppm_ascii_16(io_glue *ig, i_img *im, int width, int height, |
d87dc9a4 | 382 | int channels, int maxval, int allow_incomplete) { |
9c106321 TC |
383 | i_fcolor *line, *linep; |
384 | int x, y, ch; | |
385 | double maxvalf = maxval; | |
386 | ||
387 | line = mymalloc(width * sizeof(i_fcolor)); | |
388 | for(y=0;y<height;y++) { | |
389 | linep = line; | |
390 | for(x=0; x<width; x++) { | |
391 | for(ch=0; ch<channels; ch++) { | |
392 | int sample; | |
393 | ||
6d5c85a2 | 394 | if (!gnum(ig, &sample)) { |
9c106321 | 395 | myfree(line); |
d87dc9a4 TC |
396 | if (allow_incomplete) { |
397 | i_tags_setn(&im->tags, "i_incomplete", 1); | |
398 | i_tags_setn(&im->tags, "i_lines_read", y); | |
399 | return im; | |
9c106321 TC |
400 | } |
401 | else { | |
6d5c85a2 | 402 | if (i_io_peekc(ig) != EOF) |
9c106321 TC |
403 | i_push_error(0, "invalid data for ascii pnm"); |
404 | else | |
405 | i_push_error(0, "short read - file truncated?"); | |
406 | i_img_destroy(im); | |
407 | return NULL; | |
408 | } | |
409 | } | |
410 | if (sample > maxval) | |
411 | sample = maxval; | |
412 | linep->channel[ch] = sample / maxvalf; | |
413 | } | |
414 | ++linep; | |
415 | } | |
416 | i_plinf(im, 0, width, y, line); | |
417 | } | |
418 | myfree(line); | |
419 | ||
420 | return im; | |
421 | } | |
02d1d628 AMH |
422 | |
423 | /* | |
d87dc9a4 | 424 | =item i_readpnm_wiol(ig, allow_incomplete) |
02d1d628 AMH |
425 | |
426 | Retrieve an image and stores in the iolayer object. Returns NULL on fatal error. | |
427 | ||
428 | ig - io_glue object | |
d87dc9a4 | 429 | allow_incomplete - allows a partial file to be read successfully |
02d1d628 AMH |
430 | |
431 | =cut | |
432 | */ | |
02d1d628 AMH |
433 | |
434 | i_img * | |
6d5c85a2 | 435 | i_readpnm_wiol( io_glue *ig, int allow_incomplete) { |
02d1d628 AMH |
436 | i_img* im; |
437 | int type; | |
8d14daab | 438 | int width, height, maxval, channels; |
6d5c85a2 | 439 | int c; |
02d1d628 | 440 | |
fac8664e | 441 | i_clear_error(); |
6d5c85a2 | 442 | mm_log((1,"i_readpnm(ig %p, allow_incomplete %d)\n", ig, allow_incomplete)); |
02d1d628 | 443 | |
6d5c85a2 | 444 | c = i_io_getc(ig); |
02d1d628 | 445 | |
6d5c85a2 | 446 | if (c != 'P') { |
fac8664e | 447 | i_push_error(0, "bad header magic, not a PNM file"); |
02d1d628 AMH |
448 | mm_log((1, "i_readpnm: Could not read header of file\n")); |
449 | return NULL; | |
450 | } | |
451 | ||
6d5c85a2 | 452 | if ((c = i_io_getc(ig)) == EOF ) { |
02d1d628 AMH |
453 | mm_log((1, "i_readpnm: Could not read header of file\n")); |
454 | return NULL; | |
455 | } | |
456 | ||
6d5c85a2 | 457 | type = c - '0'; |
02d1d628 AMH |
458 | |
459 | if (type < 1 || type > 6) { | |
fac8664e | 460 | i_push_error(0, "unknown PNM file type, not a PNM file"); |
02d1d628 AMH |
461 | mm_log((1, "i_readpnm: Not a pnm file\n")); |
462 | return NULL; | |
463 | } | |
464 | ||
6d5c85a2 | 465 | if ( (c = i_io_getc(ig)) == EOF ) { |
02d1d628 AMH |
466 | mm_log((1, "i_readpnm: Could not read header of file\n")); |
467 | return NULL; | |
468 | } | |
469 | ||
6d5c85a2 | 470 | if ( !misspace(c) ) { |
fac8664e | 471 | i_push_error(0, "unexpected character, not a PNM file"); |
02d1d628 AMH |
472 | mm_log((1, "i_readpnm: Not a pnm file\n")); |
473 | return NULL; | |
474 | } | |
475 | ||
476 | mm_log((1, "i_readpnm: image is a %s\n", typenames[type-1] )); | |
477 | ||
478 | ||
479 | /* Read sizes and such */ | |
480 | ||
6d5c85a2 | 481 | if (!skip_comment(ig)) { |
fac8664e | 482 | i_push_error(0, "while skipping to width"); |
02d1d628 AMH |
483 | mm_log((1, "i_readpnm: error reading before width\n")); |
484 | return NULL; | |
485 | } | |
486 | ||
6d5c85a2 | 487 | if (!gnum(ig, &width)) { |
fac8664e | 488 | i_push_error(0, "could not read image width"); |
02d1d628 AMH |
489 | mm_log((1, "i_readpnm: error reading width\n")); |
490 | return NULL; | |
491 | } | |
492 | ||
6d5c85a2 | 493 | if (!skip_comment(ig)) { |
fac8664e | 494 | i_push_error(0, "while skipping to height"); |
02d1d628 AMH |
495 | mm_log((1, "i_readpnm: error reading before height\n")); |
496 | return NULL; | |
497 | } | |
498 | ||
6d5c85a2 | 499 | if (!gnum(ig, &height)) { |
fac8664e | 500 | i_push_error(0, "could not read image height"); |
02d1d628 AMH |
501 | mm_log((1, "i_readpnm: error reading height\n")); |
502 | return NULL; | |
503 | } | |
504 | ||
505 | if (!(type == 1 || type == 4)) { | |
6d5c85a2 | 506 | if (!skip_comment(ig)) { |
fac8664e | 507 | i_push_error(0, "while skipping to maxval"); |
02d1d628 AMH |
508 | mm_log((1, "i_readpnm: error reading before maxval\n")); |
509 | return NULL; | |
510 | } | |
511 | ||
6d5c85a2 | 512 | if (!gnum(ig, &maxval)) { |
fac8664e | 513 | i_push_error(0, "could not read maxval"); |
02d1d628 AMH |
514 | mm_log((1, "i_readpnm: error reading maxval\n")); |
515 | return NULL; | |
516 | } | |
8b695554 TC |
517 | |
518 | if (maxval == 0) { | |
519 | i_push_error(0, "maxval is zero - invalid pnm file"); | |
520 | mm_log((1, "i_readpnm: maxval is zero, invalid pnm file\n")); | |
521 | return NULL; | |
522 | } | |
523 | else if (maxval > 65535) { | |
524 | i_push_errorf(0, "maxval of %d is over 65535 - invalid pnm file", | |
525 | maxval); | |
8d14daab | 526 | mm_log((1, "i_readpnm: maxval of %d is over 65535 - invalid pnm file\n", maxval)); |
8b695554 TC |
527 | return NULL; |
528 | } | |
02d1d628 AMH |
529 | } else maxval=1; |
530 | ||
6d5c85a2 | 531 | if ((c = i_io_getc(ig)) == EOF || !misspace(c)) { |
fac8664e | 532 | i_push_error(0, "garbage in header, invalid PNM file"); |
02d1d628 AMH |
533 | mm_log((1, "i_readpnm: garbage in header\n")); |
534 | return NULL; | |
535 | } | |
536 | ||
537 | channels = (type == 3 || type == 6) ? 3:1; | |
02d1d628 | 538 | |
77157728 TC |
539 | if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) { |
540 | mm_log((1, "i_readpnm: image size exceeds limits\n")); | |
541 | return NULL; | |
542 | } | |
543 | ||
02d1d628 | 544 | mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval)); |
02d1d628 | 545 | |
9c106321 TC |
546 | if (type == 1 || type == 4) { |
547 | i_color pbm_pal[2]; | |
548 | pbm_pal[0].channel[0] = 255; | |
549 | pbm_pal[1].channel[0] = 0; | |
550 | ||
551 | im = i_img_pal_new(width, height, 1, 256); | |
552 | i_addcolors(im, pbm_pal, 2); | |
553 | } | |
554 | else { | |
555 | if (maxval > 255) | |
556 | im = i_img_16_new(width, height, channels); | |
557 | else | |
558 | im = i_img_8_new(width, height, channels); | |
559 | } | |
642a675b | 560 | |
02d1d628 AMH |
561 | switch (type) { |
562 | case 1: /* Ascii types */ | |
6d5c85a2 | 563 | im = read_pbm_ascii(ig, im, width, height, allow_incomplete); |
9c106321 TC |
564 | break; |
565 | ||
02d1d628 AMH |
566 | case 2: |
567 | case 3: | |
9c106321 | 568 | if (maxval > 255) |
6d5c85a2 | 569 | im = read_pgm_ppm_ascii_16(ig, im, width, height, channels, maxval, allow_incomplete); |
9c106321 | 570 | else |
6d5c85a2 | 571 | im = read_pgm_ppm_ascii(ig, im, width, height, channels, maxval, allow_incomplete); |
02d1d628 AMH |
572 | break; |
573 | ||
574 | case 4: /* binary pbm */ | |
6d5c85a2 | 575 | im = read_pbm_bin(ig, im, width, height, allow_incomplete); |
02d1d628 AMH |
576 | break; |
577 | ||
578 | case 5: /* binary pgm */ | |
579 | case 6: /* binary ppm */ | |
9c106321 | 580 | if (maxval > 255) |
6d5c85a2 | 581 | im = read_pgm_ppm_bin16(ig, im, width, height, channels, maxval, allow_incomplete); |
9c106321 | 582 | else |
6d5c85a2 | 583 | im = read_pgm_ppm_bin8(ig, im, width, height, channels, maxval, allow_incomplete); |
02d1d628 | 584 | break; |
9c106321 | 585 | |
02d1d628 AMH |
586 | default: |
587 | mm_log((1, "type %s [P%d] unsupported\n", typenames[type-1], type)); | |
588 | return NULL; | |
589 | } | |
9c106321 TC |
590 | |
591 | if (!im) | |
592 | return NULL; | |
593 | ||
594 | i_tags_add(&im->tags, "i_format", 0, "pnm", -1, 0); | |
595 | i_tags_setn(&im->tags, "pnm_maxval", maxval); | |
596 | i_tags_setn(&im->tags, "pnm_type", type); | |
597 | ||
02d1d628 AMH |
598 | return im; |
599 | } | |
600 | ||
2086be61 TC |
601 | static void free_images(i_img **imgs, int count) { |
602 | int i; | |
603 | ||
604 | if (count) { | |
605 | for (i = 0; i < count; ++i) | |
606 | i_img_destroy(imgs[i]); | |
607 | myfree(imgs); | |
608 | } | |
609 | } | |
610 | ||
611 | i_img **i_readpnm_multi_wiol(io_glue *ig, int *count, int allow_incomplete) { | |
612 | i_img **results = NULL; | |
613 | i_img *img = NULL; | |
6d5c85a2 | 614 | char c = EOF; |
2086be61 TC |
615 | int result_alloc = 0, |
616 | value = 0, | |
617 | eof = 0; | |
618 | *count=0; | |
6d5c85a2 | 619 | |
2086be61 TC |
620 | do { |
621 | mm_log((1, "read image %i\n", 1+*count)); | |
6d5c85a2 | 622 | img = i_readpnm_wiol( ig, allow_incomplete ); |
2086be61 TC |
623 | if( !img ) { |
624 | free_images( results, *count ); | |
625 | return NULL; | |
626 | } | |
627 | ++*count; | |
628 | if (*count > result_alloc) { | |
629 | if (result_alloc == 0) { | |
630 | result_alloc = 5; | |
631 | results = mymalloc(result_alloc * sizeof(i_img *)); | |
632 | } | |
633 | else { | |
634 | /* myrealloc never fails (it just dies if it can't allocate) */ | |
635 | result_alloc *= 2; | |
636 | results = myrealloc(results, result_alloc * sizeof(i_img *)); | |
637 | } | |
638 | } | |
639 | results[*count-1] = img; | |
640 | ||
641 | ||
642 | if( i_tags_get_int(&img->tags, "i_incomplete", 0, &value ) && value) { | |
643 | eof = 1; | |
644 | } | |
6d5c85a2 | 645 | else if( skip_spaces( ig ) && ( c=i_io_peekc( ig ) ) != EOF && c == 'P' ) { |
2086be61 TC |
646 | eof = 0; |
647 | } | |
648 | else { | |
649 | eof = 1; | |
650 | } | |
651 | } while(!eof); | |
652 | return results; | |
653 | } | |
654 | ||
655 | ||
656 | ||
9c106321 TC |
657 | static |
658 | int | |
659 | write_pbm(i_img *im, io_glue *ig, int zero_is_white) { | |
660 | int x, y; | |
661 | i_palidx *line; | |
8d14daab | 662 | i_img_dim write_size; |
9c106321 TC |
663 | unsigned char *write_buf; |
664 | unsigned char *writep; | |
665 | char header[255]; | |
666 | unsigned mask; | |
667 | ||
8d14daab TC |
668 | sprintf(header, "P4\012# CREATOR: Imager\012%" i_DF " %" i_DF "\012", |
669 | i_DFc(im->xsize), i_DFc(im->ysize)); | |
9c106321 TC |
670 | if (i_io_write(ig, header, strlen(header)) < 0) { |
671 | i_push_error(0, "could not write pbm header"); | |
672 | return 0; | |
673 | } | |
674 | write_size = (im->xsize + 7) / 8; | |
675 | line = mymalloc(sizeof(i_palidx) * im->xsize); | |
676 | write_buf = mymalloc(write_size); | |
677 | for (y = 0; y < im->ysize; ++y) { | |
678 | i_gpal(im, 0, im->xsize, y, line); | |
679 | mask = 0x80; | |
680 | writep = write_buf; | |
681 | memset(write_buf, 0, write_size); | |
682 | for (x = 0; x < im->xsize; ++x) { | |
683 | if (zero_is_white ? line[x] : !line[x]) | |
684 | *writep |= mask; | |
685 | mask >>= 1; | |
686 | if (!mask) { | |
687 | ++writep; | |
688 | mask = 0x80; | |
689 | } | |
690 | } | |
691 | if (i_io_write(ig, write_buf, write_size) != write_size) { | |
692 | i_push_error(0, "write failure"); | |
693 | myfree(write_buf); | |
694 | myfree(line); | |
695 | return 0; | |
696 | } | |
697 | } | |
698 | myfree(write_buf); | |
699 | myfree(line); | |
700 | ||
701 | return 1; | |
702 | } | |
703 | ||
704 | static | |
705 | int | |
fa90de94 | 706 | write_ppm_data_8(i_img *im, io_glue *ig, int want_channels) { |
8d14daab TC |
707 | size_t write_size = im->xsize * want_channels; |
708 | size_t buf_size = im->xsize * im->channels; | |
2a31a4b4 | 709 | unsigned char *data = mymalloc(buf_size); |
8d14daab | 710 | i_img_dim y = 0; |
9c106321 | 711 | int rc = 1; |
fa90de94 | 712 | i_color bg; |
9c106321 | 713 | |
fa90de94 | 714 | i_get_file_background(im, &bg); |
9c106321 | 715 | while (y < im->ysize && rc >= 0) { |
2a31a4b4 | 716 | i_gsamp_bg(im, 0, im->xsize, y, data, want_channels, &bg); |
9c106321 TC |
717 | if (i_io_write(ig, data, write_size) != write_size) { |
718 | i_push_error(errno, "could not write ppm data"); | |
719 | rc = 0; | |
720 | break; | |
721 | } | |
722 | ++y; | |
723 | } | |
724 | myfree(data); | |
725 | ||
726 | return rc; | |
727 | } | |
728 | ||
729 | static | |
730 | int | |
fa90de94 | 731 | write_ppm_data_16(i_img *im, io_glue *ig, int want_channels) { |
8d14daab TC |
732 | size_t line_size = im->channels * im->xsize * sizeof(i_fsample_t); |
733 | size_t sample_count = want_channels * im->xsize; | |
734 | size_t write_size = sample_count * 2; | |
2a31a4b4 TC |
735 | i_fsample_t *line_buf = mymalloc(line_size); |
736 | i_fsample_t *samplep; | |
9c106321 | 737 | unsigned char *write_buf = mymalloc(write_size); |
2a31a4b4 | 738 | unsigned char *writep; |
8d14daab TC |
739 | size_t sample_num; |
740 | i_img_dim y = 0; | |
9c106321 | 741 | int rc = 1; |
fa90de94 TC |
742 | i_fcolor bg; |
743 | ||
744 | i_get_file_backgroundf(im, &bg); | |
9c106321 TC |
745 | |
746 | while (y < im->ysize) { | |
2a31a4b4 TC |
747 | i_gsampf_bg(im, 0, im->xsize, y, line_buf, want_channels, &bg); |
748 | samplep = line_buf; | |
749 | writep = write_buf; | |
750 | for (sample_num = 0; sample_num < sample_count; ++sample_num) { | |
751 | unsigned sample16 = SampleFTo16(*samplep++); | |
752 | *writep++ = sample16 >> 8; | |
753 | *writep++ = sample16 & 0xFF; | |
9c106321 TC |
754 | } |
755 | if (i_io_write(ig, write_buf, write_size) != write_size) { | |
756 | i_push_error(errno, "could not write ppm data"); | |
757 | rc = 0; | |
758 | break; | |
759 | } | |
760 | ++y; | |
761 | } | |
762 | myfree(line_buf); | |
763 | myfree(write_buf); | |
764 | ||
765 | return rc; | |
766 | } | |
02d1d628 | 767 | |
067d6bdc AMH |
768 | undef_int |
769 | i_writeppm_wiol(i_img *im, io_glue *ig) { | |
770 | char header[255]; | |
9c106321 TC |
771 | int zero_is_white; |
772 | int wide_data; | |
02d1d628 | 773 | |
067d6bdc AMH |
774 | mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig)); |
775 | i_clear_error(); | |
02d1d628 | 776 | |
067d6bdc AMH |
777 | /* Add code to get the filename info from the iolayer */ |
778 | /* Also add code to check for mmapped code */ | |
02d1d628 | 779 | |
9c106321 | 780 | if (i_img_is_monochrome(im, &zero_is_white)) { |
6d5c85a2 TC |
781 | if (!write_pbm(im, ig, zero_is_white)) |
782 | return 0; | |
9c106321 TC |
783 | } |
784 | else { | |
785 | int type; | |
786 | int maxval; | |
fa90de94 TC |
787 | int want_channels = im->channels; |
788 | ||
789 | if (want_channels == 2 || want_channels == 4) | |
790 | --want_channels; | |
faa9b3e7 | 791 | |
9c106321 TC |
792 | if (!i_tags_get_int(&im->tags, "pnm_write_wide_data", 0, &wide_data)) |
793 | wide_data = 0; | |
794 | ||
fa90de94 | 795 | if (want_channels == 3) { |
9c106321 | 796 | type = 6; |
faa9b3e7 | 797 | } |
fa90de94 | 798 | else if (want_channels == 1) { |
9c106321 | 799 | type = 5; |
faa9b3e7 | 800 | } |
9c106321 TC |
801 | else { |
802 | i_push_error(0, "can only save 1 or 3 channel images to pnm"); | |
803 | mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels)); | |
067d6bdc AMH |
804 | return(0); |
805 | } | |
9c106321 TC |
806 | if (im->bits <= 8 || !wide_data) |
807 | maxval = 255; | |
808 | else | |
809 | maxval = 65535; | |
810 | ||
8d14daab TC |
811 | sprintf(header,"P%d\n#CREATOR: Imager\n%" i_DF " %" i_DF"\n%d\n", |
812 | type, i_DFc(im->xsize), i_DFc(im->ysize), maxval); | |
9c106321 | 813 | |
6d5c85a2 | 814 | if (i_io_write(ig,header,strlen(header)) != strlen(header)) { |
9c106321 TC |
815 | i_push_error(errno, "could not write ppm header"); |
816 | mm_log((1,"i_writeppm: unable to write ppm header.\n")); | |
067d6bdc AMH |
817 | return(0); |
818 | } | |
faa9b3e7 | 819 | |
fa90de94 TC |
820 | if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type |
821 | && im->channels == want_channels) { | |
6d5c85a2 | 822 | if (i_io_write(ig,im->idata,im->bytes) != im->bytes) { |
9c106321 | 823 | i_push_error(errno, "could not write ppm data"); |
faa9b3e7 TC |
824 | return 0; |
825 | } | |
826 | } | |
9c106321 | 827 | else if (maxval == 255) { |
fa90de94 | 828 | if (!write_ppm_data_8(im, ig, want_channels)) |
9c106321 TC |
829 | return 0; |
830 | } | |
831 | else { | |
fa90de94 | 832 | if (!write_ppm_data_16(im, ig, want_channels)) |
9c106321 | 833 | return 0; |
067d6bdc | 834 | } |
067d6bdc | 835 | } |
6d5c85a2 TC |
836 | if (i_io_close(ig)) { |
837 | i_push_errorf(i_io_error(ig), "Error closing stream: %d", i_io_error(ig)); | |
838 | return 0; | |
839 | } | |
faa9b3e7 | 840 | |
067d6bdc AMH |
841 | return(1); |
842 | } | |
b8c2033e AMH |
843 | |
844 | /* | |
845 | =back | |
846 | ||
847 | =head1 AUTHOR | |
848 | ||
5b480b14 | 849 | Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook <tonyc@cpan.org>, |
2086be61 | 850 | Philip Gwyn <gwyn@cpan.org>. |
b8c2033e AMH |
851 | |
852 | =head1 SEE ALSO | |
853 | ||
854 | Imager(3) | |
855 | ||
856 | =cut | |
857 | */ |