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