]> git.imager.perl.org - imager.git/blame - pnm.c
add new comparison method rgb_difference that resembles arithmetical difference per...
[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;
215d9ead 136 i_io_nextc(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;
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
601static 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
611i_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
657static
658int
659write_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
704static
705int
fa90de94 706write_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
729static
730int
fa90de94 731write_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
768undef_int
769i_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 849Arnar M. Hrafnkelsson <addi@umich.edu>, Tony Cook <tonyc@cpan.org>,
2086be61 850Philip Gwyn <gwyn@cpan.org>.
b8c2033e
AMH
851
852=head1 SEE ALSO
853
854Imager(3)
855
856=cut
857*/