]> git.imager.perl.org - imager.git/blame - pnm.c
hide the i_push_errorf() prototype for IMAGER_NO_CONTEXT
[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
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
42static 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
47Advances in stream until it is positioned at a
48non white space character. (internal)
49
6d5c85a2 50 ig - io_glue
02d1d628
AMH
51
52=cut
53*/
54
55static
56int
6d5c85a2
TC
57skip_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
73Advances 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
80static
81int
6d5c85a2
TC
82skip_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
107Fetches the next number from stream and stores in i, returns true
108on success else false.
109
110 mb - buffer object
111 i - integer to store result in
112
113=cut
114*/
115
116static
117int
6d5c85a2
TC
118gnum(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;
6d5c85a2 136 i_io_getc(ig);
02d1d628 137 }
6d5c85a2 138
02d1d628
AMH
139 return 1;
140}
141
9c106321
TC
142static
143i_img *
6d5c85a2 144read_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
200static
201i_img *
6d5c85a2 202read_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
248static
249i_img *
6d5c85a2 250read_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*/
298static
299i_img *
6d5c85a2 300read_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
335static
336i_img *
6d5c85a2 337read_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
379static
380i_img *
6d5c85a2 381read_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
426Retrieve 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
434i_img *
6d5c85a2 435i_readpnm_wiol( io_glue *ig, int allow_incomplete) {
02d1d628
AMH
436 i_img* im;
437 int type;
8d14daab 438 int width, height, maxval, channels;
8b695554 439 int rounder;
6d5c85a2 440 int c;
02d1d628 441
fac8664e 442 i_clear_error();
6d5c85a2 443 mm_log((1,"i_readpnm(ig %p, allow_incomplete %d)\n", ig, allow_incomplete));
02d1d628 444
6d5c85a2 445 c = i_io_getc(ig);
02d1d628 446
6d5c85a2 447 if (c != 'P') {
fac8664e 448 i_push_error(0, "bad header magic, not a PNM file");
02d1d628
AMH
449 mm_log((1, "i_readpnm: Could not read header of file\n"));
450 return NULL;
451 }
452
6d5c85a2 453 if ((c = i_io_getc(ig)) == EOF ) {
02d1d628
AMH
454 mm_log((1, "i_readpnm: Could not read header of file\n"));
455 return NULL;
456 }
457
6d5c85a2 458 type = c - '0';
02d1d628
AMH
459
460 if (type < 1 || type > 6) {
fac8664e 461 i_push_error(0, "unknown PNM file type, not a PNM file");
02d1d628
AMH
462 mm_log((1, "i_readpnm: Not a pnm file\n"));
463 return NULL;
464 }
465
6d5c85a2 466 if ( (c = i_io_getc(ig)) == EOF ) {
02d1d628
AMH
467 mm_log((1, "i_readpnm: Could not read header of file\n"));
468 return NULL;
469 }
470
6d5c85a2 471 if ( !misspace(c) ) {
fac8664e 472 i_push_error(0, "unexpected character, not a PNM file");
02d1d628
AMH
473 mm_log((1, "i_readpnm: Not a pnm file\n"));
474 return NULL;
475 }
476
477 mm_log((1, "i_readpnm: image is a %s\n", typenames[type-1] ));
478
479
480 /* Read sizes and such */
481
6d5c85a2 482 if (!skip_comment(ig)) {
fac8664e 483 i_push_error(0, "while skipping to width");
02d1d628
AMH
484 mm_log((1, "i_readpnm: error reading before width\n"));
485 return NULL;
486 }
487
6d5c85a2 488 if (!gnum(ig, &width)) {
fac8664e 489 i_push_error(0, "could not read image width");
02d1d628
AMH
490 mm_log((1, "i_readpnm: error reading width\n"));
491 return NULL;
492 }
493
6d5c85a2 494 if (!skip_comment(ig)) {
fac8664e 495 i_push_error(0, "while skipping to height");
02d1d628
AMH
496 mm_log((1, "i_readpnm: error reading before height\n"));
497 return NULL;
498 }
499
6d5c85a2 500 if (!gnum(ig, &height)) {
fac8664e 501 i_push_error(0, "could not read image height");
02d1d628
AMH
502 mm_log((1, "i_readpnm: error reading height\n"));
503 return NULL;
504 }
505
506 if (!(type == 1 || type == 4)) {
6d5c85a2 507 if (!skip_comment(ig)) {
fac8664e 508 i_push_error(0, "while skipping to maxval");
02d1d628
AMH
509 mm_log((1, "i_readpnm: error reading before maxval\n"));
510 return NULL;
511 }
512
6d5c85a2 513 if (!gnum(ig, &maxval)) {
fac8664e 514 i_push_error(0, "could not read maxval");
02d1d628
AMH
515 mm_log((1, "i_readpnm: error reading maxval\n"));
516 return NULL;
517 }
8b695554
TC
518
519 if (maxval == 0) {
520 i_push_error(0, "maxval is zero - invalid pnm file");
521 mm_log((1, "i_readpnm: maxval is zero, invalid pnm file\n"));
522 return NULL;
523 }
524 else if (maxval > 65535) {
525 i_push_errorf(0, "maxval of %d is over 65535 - invalid pnm file",
526 maxval);
8d14daab 527 mm_log((1, "i_readpnm: maxval of %d is over 65535 - invalid pnm file\n", maxval));
8b695554
TC
528 return NULL;
529 }
02d1d628 530 } else maxval=1;
8b695554 531 rounder = maxval / 2;
02d1d628 532
6d5c85a2 533 if ((c = i_io_getc(ig)) == EOF || !misspace(c)) {
fac8664e 534 i_push_error(0, "garbage in header, invalid PNM file");
02d1d628
AMH
535 mm_log((1, "i_readpnm: garbage in header\n"));
536 return NULL;
537 }
538
539 channels = (type == 3 || type == 6) ? 3:1;
02d1d628 540
77157728
TC
541 if (!i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))) {
542 mm_log((1, "i_readpnm: image size exceeds limits\n"));
543 return NULL;
544 }
545
02d1d628 546 mm_log((1, "i_readpnm: (%d x %d), channels = %d, maxval = %d\n", width, height, channels, maxval));
02d1d628 547
9c106321
TC
548 if (type == 1 || type == 4) {
549 i_color pbm_pal[2];
550 pbm_pal[0].channel[0] = 255;
551 pbm_pal[1].channel[0] = 0;
552
553 im = i_img_pal_new(width, height, 1, 256);
554 i_addcolors(im, pbm_pal, 2);
555 }
556 else {
557 if (maxval > 255)
558 im = i_img_16_new(width, height, channels);
559 else
560 im = i_img_8_new(width, height, channels);
561 }
642a675b 562
02d1d628
AMH
563 switch (type) {
564 case 1: /* Ascii types */
6d5c85a2 565 im = read_pbm_ascii(ig, im, width, height, allow_incomplete);
9c106321
TC
566 break;
567
02d1d628
AMH
568 case 2:
569 case 3:
9c106321 570 if (maxval > 255)
6d5c85a2 571 im = read_pgm_ppm_ascii_16(ig, im, width, height, channels, maxval, allow_incomplete);
9c106321 572 else
6d5c85a2 573 im = read_pgm_ppm_ascii(ig, im, width, height, channels, maxval, allow_incomplete);
02d1d628
AMH
574 break;
575
576 case 4: /* binary pbm */
6d5c85a2 577 im = read_pbm_bin(ig, im, width, height, allow_incomplete);
02d1d628
AMH
578 break;
579
580 case 5: /* binary pgm */
581 case 6: /* binary ppm */
9c106321 582 if (maxval > 255)
6d5c85a2 583 im = read_pgm_ppm_bin16(ig, im, width, height, channels, maxval, allow_incomplete);
9c106321 584 else
6d5c85a2 585 im = read_pgm_ppm_bin8(ig, im, width, height, channels, maxval, allow_incomplete);
02d1d628 586 break;
9c106321 587
02d1d628
AMH
588 default:
589 mm_log((1, "type %s [P%d] unsupported\n", typenames[type-1], type));
590 return NULL;
591 }
9c106321
TC
592
593 if (!im)
594 return NULL;
595
596 i_tags_add(&im->tags, "i_format", 0, "pnm", -1, 0);
597 i_tags_setn(&im->tags, "pnm_maxval", maxval);
598 i_tags_setn(&im->tags, "pnm_type", type);
599
02d1d628
AMH
600 return im;
601}
602
2086be61
TC
603static void free_images(i_img **imgs, int count) {
604 int i;
605
606 if (count) {
607 for (i = 0; i < count; ++i)
608 i_img_destroy(imgs[i]);
609 myfree(imgs);
610 }
611}
612
613i_img **i_readpnm_multi_wiol(io_glue *ig, int *count, int allow_incomplete) {
614 i_img **results = NULL;
615 i_img *img = NULL;
6d5c85a2 616 char c = EOF;
2086be61
TC
617 int result_alloc = 0,
618 value = 0,
619 eof = 0;
620 *count=0;
6d5c85a2 621
2086be61
TC
622 do {
623 mm_log((1, "read image %i\n", 1+*count));
6d5c85a2 624 img = i_readpnm_wiol( ig, allow_incomplete );
2086be61
TC
625 if( !img ) {
626 free_images( results, *count );
627 return NULL;
628 }
629 ++*count;
630 if (*count > result_alloc) {
631 if (result_alloc == 0) {
632 result_alloc = 5;
633 results = mymalloc(result_alloc * sizeof(i_img *));
634 }
635 else {
636 /* myrealloc never fails (it just dies if it can't allocate) */
637 result_alloc *= 2;
638 results = myrealloc(results, result_alloc * sizeof(i_img *));
639 }
640 }
641 results[*count-1] = img;
642
643
644 if( i_tags_get_int(&img->tags, "i_incomplete", 0, &value ) && value) {
645 eof = 1;
646 }
6d5c85a2 647 else if( skip_spaces( ig ) && ( c=i_io_peekc( ig ) ) != EOF && c == 'P' ) {
2086be61
TC
648 eof = 0;
649 }
650 else {
651 eof = 1;
652 }
653 } while(!eof);
654 return results;
655}
656
657
658
9c106321
TC
659static
660int
661write_pbm(i_img *im, io_glue *ig, int zero_is_white) {
662 int x, y;
663 i_palidx *line;
8d14daab 664 i_img_dim write_size;
9c106321
TC
665 unsigned char *write_buf;
666 unsigned char *writep;
667 char header[255];
668 unsigned mask;
669
8d14daab
TC
670 sprintf(header, "P4\012# CREATOR: Imager\012%" i_DF " %" i_DF "\012",
671 i_DFc(im->xsize), i_DFc(im->ysize));
9c106321
TC
672 if (i_io_write(ig, header, strlen(header)) < 0) {
673 i_push_error(0, "could not write pbm header");
674 return 0;
675 }
676 write_size = (im->xsize + 7) / 8;
677 line = mymalloc(sizeof(i_palidx) * im->xsize);
678 write_buf = mymalloc(write_size);
679 for (y = 0; y < im->ysize; ++y) {
680 i_gpal(im, 0, im->xsize, y, line);
681 mask = 0x80;
682 writep = write_buf;
683 memset(write_buf, 0, write_size);
684 for (x = 0; x < im->xsize; ++x) {
685 if (zero_is_white ? line[x] : !line[x])
686 *writep |= mask;
687 mask >>= 1;
688 if (!mask) {
689 ++writep;
690 mask = 0x80;
691 }
692 }
693 if (i_io_write(ig, write_buf, write_size) != write_size) {
694 i_push_error(0, "write failure");
695 myfree(write_buf);
696 myfree(line);
697 return 0;
698 }
699 }
700 myfree(write_buf);
701 myfree(line);
702
703 return 1;
704}
705
706static
707int
fa90de94 708write_ppm_data_8(i_img *im, io_glue *ig, int want_channels) {
8d14daab
TC
709 size_t write_size = im->xsize * want_channels;
710 size_t buf_size = im->xsize * im->channels;
2a31a4b4 711 unsigned char *data = mymalloc(buf_size);
8d14daab 712 i_img_dim y = 0;
9c106321 713 int rc = 1;
fa90de94 714 i_color bg;
9c106321 715
fa90de94 716 i_get_file_background(im, &bg);
9c106321 717 while (y < im->ysize && rc >= 0) {
2a31a4b4 718 i_gsamp_bg(im, 0, im->xsize, y, data, want_channels, &bg);
9c106321
TC
719 if (i_io_write(ig, data, write_size) != write_size) {
720 i_push_error(errno, "could not write ppm data");
721 rc = 0;
722 break;
723 }
724 ++y;
725 }
726 myfree(data);
727
728 return rc;
729}
730
731static
732int
fa90de94 733write_ppm_data_16(i_img *im, io_glue *ig, int want_channels) {
8d14daab
TC
734 size_t line_size = im->channels * im->xsize * sizeof(i_fsample_t);
735 size_t sample_count = want_channels * im->xsize;
736 size_t write_size = sample_count * 2;
2a31a4b4
TC
737 i_fsample_t *line_buf = mymalloc(line_size);
738 i_fsample_t *samplep;
9c106321 739 unsigned char *write_buf = mymalloc(write_size);
2a31a4b4 740 unsigned char *writep;
8d14daab
TC
741 size_t sample_num;
742 i_img_dim y = 0;
9c106321 743 int rc = 1;
fa90de94
TC
744 i_fcolor bg;
745
746 i_get_file_backgroundf(im, &bg);
9c106321
TC
747
748 while (y < im->ysize) {
2a31a4b4
TC
749 i_gsampf_bg(im, 0, im->xsize, y, line_buf, want_channels, &bg);
750 samplep = line_buf;
751 writep = write_buf;
752 for (sample_num = 0; sample_num < sample_count; ++sample_num) {
753 unsigned sample16 = SampleFTo16(*samplep++);
754 *writep++ = sample16 >> 8;
755 *writep++ = sample16 & 0xFF;
9c106321
TC
756 }
757 if (i_io_write(ig, write_buf, write_size) != write_size) {
758 i_push_error(errno, "could not write ppm data");
759 rc = 0;
760 break;
761 }
762 ++y;
763 }
764 myfree(line_buf);
765 myfree(write_buf);
766
767 return rc;
768}
02d1d628 769
067d6bdc
AMH
770undef_int
771i_writeppm_wiol(i_img *im, io_glue *ig) {
772 char header[255];
9c106321
TC
773 int zero_is_white;
774 int wide_data;
02d1d628 775
067d6bdc
AMH
776 mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig));
777 i_clear_error();
02d1d628 778
067d6bdc
AMH
779 /* Add code to get the filename info from the iolayer */
780 /* Also add code to check for mmapped code */
02d1d628 781
9c106321 782 if (i_img_is_monochrome(im, &zero_is_white)) {
6d5c85a2
TC
783 if (!write_pbm(im, ig, zero_is_white))
784 return 0;
9c106321
TC
785 }
786 else {
787 int type;
788 int maxval;
fa90de94
TC
789 int want_channels = im->channels;
790
791 if (want_channels == 2 || want_channels == 4)
792 --want_channels;
faa9b3e7 793
9c106321
TC
794 if (!i_tags_get_int(&im->tags, "pnm_write_wide_data", 0, &wide_data))
795 wide_data = 0;
796
fa90de94 797 if (want_channels == 3) {
9c106321 798 type = 6;
faa9b3e7 799 }
fa90de94 800 else if (want_channels == 1) {
9c106321 801 type = 5;
faa9b3e7 802 }
9c106321
TC
803 else {
804 i_push_error(0, "can only save 1 or 3 channel images to pnm");
805 mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
067d6bdc
AMH
806 return(0);
807 }
9c106321
TC
808 if (im->bits <= 8 || !wide_data)
809 maxval = 255;
810 else
811 maxval = 65535;
812
8d14daab
TC
813 sprintf(header,"P%d\n#CREATOR: Imager\n%" i_DF " %" i_DF"\n%d\n",
814 type, i_DFc(im->xsize), i_DFc(im->ysize), maxval);
9c106321 815
6d5c85a2 816 if (i_io_write(ig,header,strlen(header)) != strlen(header)) {
9c106321
TC
817 i_push_error(errno, "could not write ppm header");
818 mm_log((1,"i_writeppm: unable to write ppm header.\n"));
067d6bdc
AMH
819 return(0);
820 }
faa9b3e7 821
fa90de94
TC
822 if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type
823 && im->channels == want_channels) {
6d5c85a2 824 if (i_io_write(ig,im->idata,im->bytes) != im->bytes) {
9c106321 825 i_push_error(errno, "could not write ppm data");
faa9b3e7
TC
826 return 0;
827 }
828 }
9c106321 829 else if (maxval == 255) {
fa90de94 830 if (!write_ppm_data_8(im, ig, want_channels))
9c106321
TC
831 return 0;
832 }
833 else {
fa90de94 834 if (!write_ppm_data_16(im, ig, want_channels))
9c106321 835 return 0;
067d6bdc 836 }
067d6bdc 837 }
6d5c85a2
TC
838 if (i_io_close(ig)) {
839 i_push_errorf(i_io_error(ig), "Error closing stream: %d", i_io_error(ig));
840 return 0;
841 }
faa9b3e7 842
067d6bdc
AMH
843 return(1);
844}
b8c2033e
AMH
845
846/*
847=back
848
849=head1 AUTHOR
850
5b480b14 851Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook <tonyc@cpan.org>,
2086be61 852Philip Gwyn <gwyn@cpan.org>.
b8c2033e
AMH
853
854=head1 SEE ALSO
855
856Imager(3)
857
858=cut
859*/