1 /*=============================================================================
2 libpam.c
3 ===============================================================================
4 These are the library functions, which belong in the libnetpbm library,
5 that deal with the PAM (Portable Arbitrary Format) image format.
6
7 This file was originally written by Bryan Henderson and is contributed
8 to the public domain by him and subsequent authors.
9 =============================================================================*/
10
11 /* See pmfileio.c for the complicated explanation of this 32/64 bit file
12 offset stuff.
13 */
14 #define _FILE_OFFSET_BITS 64
15 #define _LARGE_FILES
16 #define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
17 #define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */
18 #define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */
19
20 #include <string.h>
21 #include <limits.h>
22 #include <assert.h>
23 #include <stdio.h>
24
25 #include <math.h>
26
27 #include "netpbm/pm_c_util.h"
28 #include "netpbm/mallocvar.h"
29 #include "netpbm/nstring.h"
30
31 #include "pam.h"
32 #include "ppm.h"
33 #include "libpbm.h"
34 #include "libpgm.h"
35 #include "libppm.h"
36 #include "colorname.h"
37 #include "fileio.h"
38
39 #include "libpam.h"
40
41
42
43 static unsigned int
allocationDepth(const struct pam * const pamP)44 allocationDepth(const struct pam * const pamP) {
45
46 unsigned int retval;
47
48 if (pamP->len >= PAM_STRUCT_SIZE(allocation_depth)) {
49 if (pamP->allocation_depth == 0)
50 retval = pamP->depth;
51 else {
52 if (pamP->depth > pamP->allocation_depth)
53 pm_error("'allocationDepth' (%u) is smaller than 'depth' (%u)",
54 pamP->allocation_depth, pamP->depth);
55 retval = pamP->allocation_depth;
56 }
57 } else
58 retval = pamP->depth;
59 return retval;
60 }
61
62
63
64 static const char **
pamCommentP(const struct pam * const pamP)65 pamCommentP(const struct pam * const pamP) {
66
67 const char ** retval;
68
69 if (pamP->len >= PAM_STRUCT_SIZE(comment_p))
70 retval = pamP->comment_p;
71 else
72 retval = NULL;
73
74 return retval;
75 }
76
77
78
79 static void
validateComputableSize(struct pam * const pamP)80 validateComputableSize(struct pam * const pamP) {
81 /*----------------------------------------------------------------------------
82 Validate that the dimensions of the image are such that it can be
83 processed in typical ways on this machine without worrying about
84 overflows. Note that in C, arithmetic is always modulus arithmetic,
85 so if your values are too big, the result is not what you expect.
86 That failed expectation can be disastrous if you use it to allocate
87 memory.
88
89 It is very normal to allocate space for a tuplerow, so we make sure
90 the size of a tuple row, in bytes, can be represented by an 'int'.
91
92 Another common operation is adding 1 or 2 to the highest row, column,
93 or plane number in the image, so we make sure that's possible.
94 -----------------------------------------------------------------------------*/
95 if (pamP->width == 0)
96 pm_error("Width is zero. Image must be at least one pixel wide");
97 else if (pamP->height == 0)
98 pm_error("Height is zero. Image must be at least one pixel high");
99 else {
100 unsigned int const depth = allocationDepth(pamP);
101
102 if (depth > INT_MAX/sizeof(sample))
103 pm_error("image depth (%u) too large to be processed", depth);
104 else if (depth * sizeof(sample) > INT_MAX/pamP->width)
105 pm_error("image width and depth (%u, %u) too large "
106 "to be processed.", pamP->width, depth);
107 else if (pamP->width * (depth * sizeof(sample)) >
108 INT_MAX - depth * sizeof(tuple *))
109 pm_error("image width and depth (%u, %u) too large "
110 "to be processed.", pamP->width, depth);
111
112 if (depth > INT_MAX - 2)
113 pm_error("image depth (%u) too large to be processed", depth);
114 if (pamP->width > INT_MAX - 2)
115 pm_error("image width (%u) too large to be processed",
116 pamP->width);
117 if (pamP->height > INT_MAX - 2)
118 pm_error("image height (%u) too large to be processed",
119 pamP->height);
120 }
121 }
122
123
124
125 tuple
pnm_allocpamtuple(const struct pam * const pamP)126 pnm_allocpamtuple(const struct pam * const pamP) {
127
128 tuple retval;
129
130 retval = malloc(allocationDepth(pamP) * sizeof(retval[0]));
131
132 if (retval == NULL)
133 pm_error("Out of memory allocating %u-plane tuple",
134 allocationDepth(pamP));
135
136 return retval;
137 }
138
139
140
141 int
pnm_tupleequal(const struct pam * const pamP,tuple const comparand,tuple const comparator)142 pnm_tupleequal(const struct pam * const pamP,
143 tuple const comparand,
144 tuple const comparator) {
145
146 unsigned int plane;
147 bool equal;
148
149 equal = TRUE; /* initial value */
150 for (plane = 0; plane < pamP->depth; ++plane)
151 if (comparand[plane] != comparator[plane])
152 equal = FALSE;
153
154 return equal;
155 }
156
157
158
159
160 void
pnm_assigntuple(const struct pam * const pamP,tuple const dest,tuple const source)161 pnm_assigntuple(const struct pam * const pamP,
162 tuple const dest,
163 tuple const source) {
164
165 unsigned int plane;
166 for (plane = 0; plane < pamP->depth; ++plane) {
167 dest[plane] = source[plane];
168 }
169 }
170
171
172
173 static void
scaleTuple(const struct pam * const pamP,tuple const dest,tuple const source,sample const newmaxval)174 scaleTuple(const struct pam * const pamP,
175 tuple const dest,
176 tuple const source,
177 sample const newmaxval) {
178
179 unsigned int plane;
180 for (plane = 0; plane < pamP->depth; ++plane)
181 dest[plane] = pnm_scalesample(source[plane], pamP->maxval, newmaxval);
182 }
183
184
185
186 void
pnm_scaletuple(const struct pam * const pamP,tuple const dest,tuple const source,sample const newmaxval)187 pnm_scaletuple(const struct pam * const pamP,
188 tuple const dest,
189 tuple const source,
190 sample const newmaxval) {
191
192 scaleTuple(pamP, dest, source, newmaxval);
193 }
194
195
196
197 void
pnm_createBlackTuple(const struct pam * const pamP,tuple * const blackTupleP)198 pnm_createBlackTuple(const struct pam * const pamP,
199 tuple * const blackTupleP) {
200 /*----------------------------------------------------------------------------
201 Create a "black" tuple. By that we mean a tuple all of whose elements
202 are zero. If it's an RGB, grayscale, or b&w pixel, that means it's black.
203 -----------------------------------------------------------------------------*/
204 unsigned int i;
205
206 *blackTupleP = pnm_allocpamtuple(pamP);
207
208 for (i = 0; i < pamP->depth; ++i)
209 (*blackTupleP)[i] = 0;
210 }
211
212
213
214 static tuple *
allocPamRow(const struct pam * const pamP)215 allocPamRow(const struct pam * const pamP) {
216 /*----------------------------------------------------------------------------
217 We assume that the dimensions of the image are such that arithmetic
218 overflow will not occur in our calculations. NOTE: pnm_readpaminit()
219 ensures this assumption is valid.
220 -----------------------------------------------------------------------------*/
221 /* The tuple row data structure starts with pointers to the tuples,
222 immediately followed by the tuples themselves.
223 */
224
225 unsigned int const bytesPerTuple = allocationDepth(pamP) * sizeof(sample);
226 tuple * tuplerow;
227
228 tuplerow = malloc(pamP->width * (sizeof(tuple *) + bytesPerTuple));
229
230 if (tuplerow != NULL) {
231 /* Now we initialize the pointers to the individual tuples
232 to make this a regulation C two dimensional array.
233 */
234 char * p;
235 unsigned int col;
236
237 p = (char*) (tuplerow + pamP->width); /* location of Tuple 0 */
238 for (col = 0; col < pamP->width; ++col) {
239 tuplerow[col] = (tuple) p;
240 p += bytesPerTuple;
241 }
242 }
243 return tuplerow;
244 }
245
246
247
248 tuple *
pnm_allocpamrow(const struct pam * const pamP)249 pnm_allocpamrow(const struct pam * const pamP) {
250
251
252 tuple * const tuplerow = allocPamRow(pamP);
253
254 if (tuplerow == NULL)
255 pm_error("Out of memory allocating space for a tuple row of "
256 "%d tuples by %d samples per tuple by %u bytes per sample.",
257 pamP->width, allocationDepth(pamP), (unsigned)sizeof(sample));
258
259 return tuplerow;
260 }
261
262
263
264 static unsigned int
rowimagesize(const struct pam * const pamP)265 rowimagesize(const struct pam * const pamP) {
266
267 /* If repeatedly calculating this turns out to be a significant
268 performance problem, we could keep this in struct pam like
269 bytes_per_sample.
270 */
271
272 if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE)
273 return pbm_packed_bytes(pamP->width);
274 else
275 return (pamP->width * pamP->bytes_per_sample * pamP->depth);
276 }
277
278
279
280 unsigned char *
pnm_allocrowimage(const struct pam * const pamP)281 pnm_allocrowimage(const struct pam * const pamP) {
282
283 unsigned int const rowsize = rowimagesize(pamP);
284 unsigned int const overrunSpaceNeeded = 8;
285 /* This is the number of extra bytes of space libnetpbm needs to have
286 at the end of the buffer so it can use fast, lazy algorithms.
287 */
288 unsigned int const size = rowsize + overrunSpaceNeeded;
289
290 unsigned char * retval;
291
292 retval = malloc(size);
293
294 if (retval == NULL)
295 pm_error("Unable to allocate %u bytes for a row image buffer",
296 size);
297
298 return retval;
299 }
300
301
302
303 void
pnm_freerowimage(unsigned char * const rowimage)304 pnm_freerowimage(unsigned char * const rowimage) {
305 free(rowimage);
306 }
307
308
309
310 void
pnm_scaletuplerow(const struct pam * const pamP,tuple * const destRow,tuple * const sourceRow,sample const newMaxval)311 pnm_scaletuplerow(const struct pam * const pamP,
312 tuple * const destRow,
313 tuple * const sourceRow,
314 sample const newMaxval) {
315
316 if (pamP->maxval == newMaxval) {
317 /* Fast path for common case: no scaling needed */
318 if (destRow != sourceRow) {
319 /* Fast path for common case: it's already what it needs to be */
320 unsigned int col;
321 for (col = 0; col < pamP->width; ++col)
322 pnm_assigntuple(pamP, destRow[col], sourceRow[col]);
323 }
324 } else {
325 unsigned int col;
326 for (col = 0; col < pamP->width; ++col)
327 scaleTuple(pamP, destRow[col], sourceRow[col], newMaxval);
328 }
329 }
330
331
332
333 tuple **
pnm_allocpamarray(const struct pam * const pamP)334 pnm_allocpamarray(const struct pam * const pamP) {
335
336 tuple **tuplearray;
337
338 /* If the speed of this is ever an issue, it might be sped up a little
339 by allocating one large chunk.
340 */
341
342 MALLOCARRAY(tuplearray, pamP->height);
343 if (tuplearray == NULL)
344 pm_error("Out of memory allocating the row pointer section of "
345 "a %u row array", pamP->height);
346 else {
347 int row;
348 bool outOfMemory;
349
350 outOfMemory = FALSE;
351 for (row = 0; row < pamP->height && !outOfMemory; ++row) {
352 tuplearray[row] = allocPamRow(pamP);
353 if (tuplearray[row] == NULL) {
354 unsigned int freerow;
355 outOfMemory = TRUE;
356
357 for (freerow = 0; freerow < row; ++freerow)
358 pnm_freepamrow(tuplearray[row]);
359 }
360 }
361 if (outOfMemory) {
362 free(tuplearray);
363
364 pm_error("Out of memory allocating the %u rows %u columns wide by "
365 "%u planes deep", pamP->height, pamP->width,
366 allocationDepth(pamP));
367 }
368 }
369 return tuplearray;
370 }
371
372
373
374 void
pnm_freepamarray(tuple ** const tuplearray,const struct pam * const pamP)375 pnm_freepamarray(tuple ** const tuplearray, const struct pam * const pamP) {
376
377 int row;
378 for (row = 0; row < pamP->height; row++)
379 pnm_freepamrow(tuplearray[row]);
380
381 free(tuplearray);
382 }
383
384
385
386 void
pnm_setminallocationdepth(struct pam * const pamP,unsigned int const allocationDepth)387 pnm_setminallocationdepth(struct pam * const pamP,
388 unsigned int const allocationDepth) {
389
390 if (pamP->len < PAM_STRUCT_SIZE(allocation_depth))
391 pm_error("Can't set minimum allocation depth in pam structure, "
392 "because the structure is only %u bytes long, and to "
393 "have an allocation_depth field, it must bea at least %u",
394 pamP->len, (unsigned)PAM_STRUCT_SIZE(allocation_depth));
395
396 pamP->allocation_depth = MAX(allocationDepth, pamP->depth);
397
398 validateComputableSize(pamP);
399 }
400
401
402
403 void
pnm_setpamrow(const struct pam * const pamP,tuple * const tuplerow,sample const value)404 pnm_setpamrow(const struct pam * const pamP,
405 tuple * const tuplerow,
406 sample const value) {
407
408 int col;
409 for (col = 0; col < pamP->width; ++col) {
410 int plane;
411 for (plane = 0; plane < pamP->depth; ++plane)
412 tuplerow[col][plane] = value;
413 }
414 }
415
416
417
418
419 #define MAX_LABEL_LENGTH 8
420 #define MAX_VALUE_LENGTH 255
421
422 static void
parseHeaderLine(const char * const buffer,char * const label,char * const value)423 parseHeaderLine(const char * const buffer,
424 char * const label,
425 char * const value) {
426 /*----------------------------------------------------------------------------
427 We truncate the labe to MAX_LABEL_LENGTH and the value to
428 MAX_VALUE_LENGTH. There must be at least that much space (plus space
429 for a terminating NUL) at 'label' and 'value', respectively.
430 -----------------------------------------------------------------------------*/
431 unsigned int bufferCurs;
432
433 /* Skip initial white space */
434 for (bufferCurs = 0; ISSPACE(buffer[bufferCurs]); ++bufferCurs) {}
435
436 {
437 /* Read off label, put as much as will fit into label[] */
438 unsigned int labelCurs;
439
440 for (labelCurs = 0;
441 !ISSPACE(buffer[bufferCurs]) && buffer[bufferCurs] != '\0';
442 ++bufferCurs) {
443 if (labelCurs < MAX_LABEL_LENGTH)
444 label[labelCurs++] = buffer[bufferCurs];
445 }
446 label[labelCurs] = '\0'; /* null terminate it */
447 }
448
449 /* Skip white space between label and value */
450 while (ISSPACE(buffer[bufferCurs]))
451 ++bufferCurs;
452
453 /* copy value into value[], truncating as necessary */
454 strncpy(value, buffer+bufferCurs, MAX_VALUE_LENGTH);
455 value[MAX_VALUE_LENGTH] = '\0';
456
457 {
458 /* Remove trailing white space from value[] */
459 unsigned int valueCurs;
460
461 for (valueCurs = strlen(value);
462 valueCurs > 0 && ISSPACE(value[valueCurs-1]);
463 --valueCurs);
464
465 value[valueCurs] = '\0';
466 }
467 }
468
469
470
471 struct headerSeen {
472 /*----------------------------------------------------------------------------
473 This structure tells what we've seen so far in our progress through the
474 PAM header
475 -----------------------------------------------------------------------------*/
476 bool width;
477 bool height;
478 bool depth;
479 bool maxval;
480 bool endhdr;
481 };
482
483
484
485 static void
parseHeaderUint(const char * const valueString,unsigned int * const valueNumP,const char * const name)486 parseHeaderUint(const char * const valueString,
487 unsigned int * const valueNumP,
488 const char * const name) {
489 /*----------------------------------------------------------------------------
490 Interpret 'valueString' as the number in a header such as
491 "WIDTH 200".
492
493 'name' is the header name ("WIDTH" in the example).
494 -----------------------------------------------------------------------------*/
495
496 if (strlen(valueString) == 0)
497 pm_error("Missing value for %s in PAM file header.", name);
498 else {
499 char * endptr;
500 long int valueNum;
501 errno = 0; /* Clear errno so we can detect strtol() failure */
502 valueNum = strtol(valueString, &endptr, 10);
503 if (errno != 0)
504 pm_error("Too-large value for %s in "
505 "PAM file header: '%s'", name, valueString);
506 else if (*endptr != '\0')
507 pm_error("Non-numeric value for %s in "
508 "PAM file header: '%s'", name, valueString);
509 else if (valueNum < 0)
510 pm_error("Negative value for %s in "
511 "PAM file header: '%s'", name, valueString);
512 else if ((unsigned int)valueNum != valueNum)
513 pm_error("Ridiculously large value for %s in "
514 "PAM file header: %lu", name, valueNum);
515 else
516 *valueNumP = (unsigned int)valueNum;
517 }
518 }
519
520
521
522 static void
parseHeaderInt(const char * const valueString,int * const valueNumP,const char * const name)523 parseHeaderInt(const char * const valueString,
524 int * const valueNumP,
525 const char * const name) {
526 /*----------------------------------------------------------------------------
527 This is not what it seems. It is the same thing as
528 parseHeaderUint, except that the type of the value it returns is
529 "int" instead of "unsigned int". But that doesn't mean the value can
530 be negative. We throw an error is it is not positive.
531 -----------------------------------------------------------------------------*/
532 unsigned int valueNum;
533
534 parseHeaderUint(valueString, &valueNum, name);
535
536 if ((int)valueNum != valueNum)
537 pm_error("Ridiculously large value for %s in "
538 "PAM file header: %u", name, valueNum);
539 else
540 *valueNumP = (int)valueNum;
541 }
542
543
544
545 static void
processHeaderLine(char const buffer[],struct pam * const pamP,struct headerSeen * const headerSeenP)546 processHeaderLine(char const buffer[],
547 struct pam * const pamP,
548 struct headerSeen * const headerSeenP) {
549 /*----------------------------------------------------------------------------
550 Process a line from the PAM header. The line is buffer[], and it is not
551 a comment or blank.
552
553 Put the value that the line defines in *pamP (unless it's ENDHDR).
554
555 Update *headerSeenP with whatever we see.
556 -----------------------------------------------------------------------------*/
557 char label[MAX_LABEL_LENGTH+1];
558 char value[MAX_VALUE_LENGTH+1];
559
560 parseHeaderLine(buffer, label, value);
561
562 if (streq(label, "ENDHDR"))
563 headerSeenP->endhdr = TRUE;
564 else if (streq(label, "WIDTH")) {
565 parseHeaderInt(value, &pamP->width, label);
566 headerSeenP->width = TRUE;
567 } else if (streq(label, "HEIGHT")) {
568 parseHeaderInt(value, &pamP->height, label);
569 headerSeenP->height = TRUE;
570 } else if (streq(label, "DEPTH")) {
571 parseHeaderUint(value, &pamP->depth, label);
572 headerSeenP->depth = TRUE;
573 } else if (streq(label, "MAXVAL")) {
574 unsigned int maxval;
575 parseHeaderUint(value, &maxval, label);
576 if (maxval >= (1<<16))
577 pm_error("Maxval too large: %u. Max is 65535", maxval);
578 pamP->maxval = maxval;
579 headerSeenP->maxval = TRUE;
580 } else if (streq(label, "TUPLTYPE")) {
581 if (strlen(value) == 0)
582 pm_error("TUPLTYPE header does not have any tuple type text");
583 else {
584 size_t const oldLen = strlen(pamP->tuple_type);
585 if (oldLen + strlen(value) + 1 > sizeof(pamP->tuple_type)-1)
586 pm_error("TUPLTYPE value too long in PAM header");
587 if (oldLen == 0)
588 strcpy(pamP->tuple_type, value);
589 else {
590 strcat(pamP->tuple_type, " ");
591 strcat(pamP->tuple_type, value);
592 }
593 pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0';
594 }
595 } else
596 pm_error("Unrecognized header line type: '%s'. "
597 "Possible missing ENDHDR line?", label);
598 }
599
600
601
602 static void
appendComment(char ** const commentsP,const char * const commentHeader)603 appendComment(char ** const commentsP,
604 const char * const commentHeader) {
605
606 const char * const commentLine = &commentHeader[1];
607
608 size_t commentLen = strlen(*commentsP) + strlen(commentLine) + 1;
609
610 assert(commentHeader[0] == '#');
611
612 REALLOCARRAY(*commentsP, commentLen);
613
614 if (*commentsP == NULL)
615 pm_error("Couldn't get storage for %u characters of comments from "
616 "the PAM header", (unsigned)commentLen);
617
618 strcat(*commentsP, commentLine);
619 }
620
621
622
623 static void
disposeOfComments(const struct pam * const pamP,const char * const comments)624 disposeOfComments(const struct pam * const pamP,
625 const char * const comments) {
626
627 const char ** const retP = pamCommentP(pamP);
628
629 if (retP)
630 *retP = comments;
631 else
632 pm_strfree(comments);
633 }
634
635
636
637 static void
readpaminitrest(struct pam * const pamP)638 readpaminitrest(struct pam * const pamP) {
639 /*----------------------------------------------------------------------------
640 Read the rest of the PAM header (after the first line -- the magic
641 number line). Fill in all the information in *pamP.
642 -----------------------------------------------------------------------------*/
643 struct headerSeen headerSeen;
644 char * comments;
645
646 headerSeen.width = FALSE;
647 headerSeen.height = FALSE;
648 headerSeen.depth = FALSE;
649 headerSeen.maxval = FALSE;
650 headerSeen.endhdr = FALSE;
651
652 pamP->tuple_type[0] = '\0';
653
654 comments = strdup("");
655
656 {
657 int c;
658 /* Read off rest of 1st line -- probably just the newline after the
659 magic number
660 */
661 while ((c = getc(pamP->file)) != -1 && c != '\n');
662 }
663
664 while (!headerSeen.endhdr) {
665 char buffer[256];
666 char * rc;
667 rc = fgets(buffer, sizeof(buffer), pamP->file);
668 if (rc == NULL)
669 pm_error("EOF or error reading file while trying to read the "
670 "PAM header");
671 else {
672 buffer[256-1-1] = '\n'; /* In case fgets() truncated */
673 if (buffer[0] == '#')
674 appendComment(&comments, buffer);
675 else if (pm_stripeq(buffer, ""));
676 /* Ignore it; it's a blank line */
677 else
678 processHeaderLine(buffer, pamP, &headerSeen);
679 }
680 }
681
682 disposeOfComments(pamP, comments);
683
684 if (!headerSeen.height)
685 pm_error("No HEIGHT header line in PAM header");
686 if (!headerSeen.width)
687 pm_error("No WIDTH header line in PAM header");
688 if (!headerSeen.depth)
689 pm_error("No DEPTH header line in PAM header");
690 if (!headerSeen.maxval)
691 pm_error("No MAXVAL header line in PAM header");
692
693 if (pamP->height == 0)
694 pm_error("HEIGHT value is zero in PAM header");
695 if (pamP->width == 0)
696 pm_error("WIDTH value is zero in PAM header");
697 if (pamP->depth == 0)
698 pm_error("DEPTH value is zero in PAM header");
699 if (pamP->maxval == 0)
700 pm_error("MAXVAL value is zero in PAM header");
701 if (pamP->maxval > PAM_OVERALL_MAXVAL)
702 pm_error("MAXVAL value (%lu) in PAM header is greater than %u",
703 pamP->maxval, PAM_OVERALL_MAXVAL);
704 }
705
706
707
708 void
pnm_readpaminitrestaspnm(FILE * const fileP,int * const colsP,int * const rowsP,gray * const maxvalP,int * const formatP)709 pnm_readpaminitrestaspnm(FILE * const fileP,
710 int * const colsP,
711 int * const rowsP,
712 gray * const maxvalP,
713 int * const formatP) {
714 /*----------------------------------------------------------------------------
715 Read the rest of the PAM header (after the first line) and return
716 information as if it were PPM or PGM.
717
718 Die if it isn't a PAM of the sort we can treat as PPM or PGM.
719 -----------------------------------------------------------------------------*/
720 struct pam pam;
721
722 pam.size = sizeof(struct pam);
723 pam.file = fileP;
724 pam.len = PAM_STRUCT_SIZE(tuple_type);
725 pam.format = PAM_FORMAT;
726
727 readpaminitrest(&pam);
728
729
730 /* A PAM raster of depth 1 is identical to a PGM raster. A PAM
731 raster of depth 3 is identical to PPM raster. So
732 ppm_readppmrow() will be able to read the PAM raster as long as
733 the format it thinks it is (PGM or PPM) corresponds to the PAM
734 depth. Similar for pgm_readpgmrow().
735 */
736 switch (pam.depth) {
737 case 3:
738 *formatP = RPPM_FORMAT;
739 break;
740 case 1:
741 *formatP = RPGM_FORMAT;
742 break;
743 default:
744 pm_error("Cannot treat PAM image as PPM or PGM, "
745 "because its depth (%u) "
746 "is not 1 or 3.", pam.depth);
747 }
748
749 *colsP = pam.width;
750 *rowsP = pam.height;
751 *maxvalP = (gray)pam.maxval;
752 }
753
754
755
756 unsigned int
pnm_bytespersample(sample const maxval)757 pnm_bytespersample(sample const maxval) {
758 /*----------------------------------------------------------------------------
759 Return the number of bytes per sample in the PAM raster of a PAM image
760 with maxval 'maxval'. It's defined to be the minimum number of bytes
761 needed for that maxval, i.e. 1 for maxval < 256, 2 otherwise.
762 -----------------------------------------------------------------------------*/
763
764 /* The PAM format requires maxval to be greater than zero and less than
765 1<<16, but since that is a largely arbitrary restriction, we don't want
766 to rely on it.
767 */
768
769 unsigned int i;
770 sample a;
771
772 for (i = 0, a = maxval; i <= sizeof(maxval); ++i) {
773 if (a == 0)
774 return i;
775 a >>= 8;
776 }
777 return 0; /* silence compiler warning */
778 }
779
780
781
782 static void
validateMinDepth(const struct pam * const pamP,unsigned int const minDepth)783 validateMinDepth(const struct pam * const pamP,
784 unsigned int const minDepth) {
785
786 if (pamP->depth < minDepth)
787 pm_error("Depth %u is insufficient for tuple type '%s'. "
788 "Minimum depth is %u",
789 pamP->depth, pamP->tuple_type, minDepth);
790 }
791
792
793
794 static void
interpretTupleType(struct pam * const pamP)795 interpretTupleType(struct pam * const pamP) {
796 /*----------------------------------------------------------------------------
797 Fill in redundant convenience fields in *pamP with information the
798 pamP->tuple_type value implies:
799
800 visual
801 colorDepth
802 haveOpacity
803 opacityPlane
804
805 Validate the tuple type against the depth and maxval as well.
806 -----------------------------------------------------------------------------*/
807 const char * const tupleType =
808 pamP->len >= PAM_STRUCT_SIZE(tuple_type) ? pamP->tuple_type : "";
809
810 bool visual;
811 unsigned int colorDepth;
812 bool haveOpacity;
813 unsigned int opacityPlane;
814
815 assert(pamP->depth > 0);
816
817 switch (PAM_FORMAT_TYPE(pamP->format)) {
818 case PAM_TYPE: {
819 if (streq(tupleType, "BLACKANDWHITE")) {
820 visual = true;
821 colorDepth = 1;
822 haveOpacity = false;
823 if (pamP->maxval != 1)
824 pm_error("maxval %u is not consistent with tuple type "
825 "BLACKANDWHITE (should be 1)",
826 (unsigned)pamP->maxval);
827 } else if (streq(tupleType, "GRAYSCALE")) {
828 visual = true;
829 colorDepth = 1;
830 haveOpacity = false;
831 } else if (streq(tupleType, "GRAYSCALE_ALPHA")) {
832 visual = true;
833 colorDepth = 1;
834 haveOpacity = true;
835 opacityPlane = PAM_GRAY_TRN_PLANE;
836 validateMinDepth(pamP, 2);
837 } else if (streq(tupleType, "RGB")) {
838 visual = true;
839 colorDepth = 3;
840 haveOpacity = false;
841 validateMinDepth(pamP, 3);
842 } else if (streq(tupleType, "RGB_ALPHA")) {
843 visual = true;
844 colorDepth = 3;
845 haveOpacity = true;
846 opacityPlane = PAM_TRN_PLANE;
847 validateMinDepth(pamP, 4);
848 } else {
849 visual = false;
850 }
851 } break;
852 case PPM_TYPE:
853 visual = true;
854 colorDepth = 3;
855 haveOpacity = false;
856 assert(pamP->depth == 3);
857 break;
858 case PGM_TYPE:
859 visual = true;
860 colorDepth = 1;
861 haveOpacity = false;
862 break;
863 case PBM_TYPE:
864 visual = true;
865 colorDepth = 1;
866 haveOpacity = false;
867 break;
868 default:
869 assert(false);
870 }
871 if (pamP->size >= PAM_STRUCT_SIZE(visual))
872 pamP->visual = visual;
873 if (pamP->size >= PAM_STRUCT_SIZE(color_depth))
874 pamP->color_depth = colorDepth;
875 if (pamP->size >= PAM_STRUCT_SIZE(have_opacity))
876 pamP->have_opacity = haveOpacity;
877 if (pamP->size >= PAM_STRUCT_SIZE(opacity_plane))
878 pamP->opacity_plane = opacityPlane;
879 }
880
881
882
883 void
pnm_readpaminit(FILE * const file,struct pam * const pamP,int const size)884 pnm_readpaminit(FILE * const file,
885 struct pam * const pamP,
886 int const size) {
887
888 if (size < PAM_STRUCT_SIZE(tuple_type))
889 pm_error("pam object passed to pnm_readpaminit() is too small. "
890 "It must be large "
891 "enough to hold at least up to the "
892 "'tuple_type' member, but according "
893 "to the 'size' argument, it is only %d bytes long.",
894 size);
895
896 pamP->size = size;
897 pamP->file = file;
898 pamP->len = MIN(pamP->size, sizeof(struct pam));
899
900 if (size >= PAM_STRUCT_SIZE(allocation_depth))
901 pamP->allocation_depth = 0;
902
903 /* Get magic number. */
904 pamP->format = pm_readmagicnumber(file);
905
906 switch (PAM_FORMAT_TYPE(pamP->format)) {
907 case PAM_TYPE:
908 readpaminitrest(pamP);
909 break;
910 case PPM_TYPE: {
911 pixval maxval;
912 ppm_readppminitrest(pamP->file, &pamP->width, &pamP->height, &maxval);
913 pamP->maxval = (sample) maxval;
914 pamP->depth = 3;
915 strcpy(pamP->tuple_type, PAM_PPM_TUPLETYPE);
916 if (pamCommentP(pamP))
917 *pamP->comment_p = strdup("");
918 } break;
919
920 case PGM_TYPE: {
921 gray maxval;
922 pgm_readpgminitrest(pamP->file, &pamP->width, &pamP->height, &maxval);
923 pamP->maxval = (sample) maxval;
924 pamP->depth = 1;
925 strcpy(pamP->tuple_type, PAM_PGM_TUPLETYPE);
926 if (pamCommentP(pamP))
927 *pamP->comment_p = strdup("");
928 } break;
929
930 case PBM_TYPE:
931 pbm_readpbminitrest(pamP->file, &pamP->width,&pamP->height);
932 pamP->maxval = (sample) 1;
933 pamP->depth = 1;
934 strcpy(pamP->tuple_type, PAM_PBM_TUPLETYPE);
935 if (pamCommentP(pamP))
936 *pamP->comment_p = strdup("");
937 break;
938
939 default:
940 pm_error("bad magic number 0x%x - not a PAM, PPM, PGM, or PBM file",
941 pamP->format);
942 }
943
944 pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval);
945 pamP->plainformat = FALSE;
946 /* See below for complex explanation of why this is FALSE. */
947
948 interpretTupleType(pamP);
949
950 validateComputableSize(pamP);
951 }
952
953
954
955 static void
writeComments(const struct pam * const pamP)956 writeComments(const struct pam * const pamP) {
957 /*----------------------------------------------------------------------------
958 Write comments for a PAM header, insofar as *pamP specifies comments.
959 -----------------------------------------------------------------------------*/
960 const char ** const commentP = pamCommentP(pamP);
961
962 if (commentP) {
963 const char * const comment = *commentP;
964
965 const char * p;
966 bool startOfLine;
967
968 for (p = &comment[0], startOfLine = TRUE; *p; ++p) {
969 if (startOfLine)
970 fputc('#', pamP->file);
971
972 fputc(*p, pamP->file);
973
974 if (*p == '\n')
975 startOfLine = TRUE;
976 else
977 startOfLine = FALSE;
978 }
979 if (!startOfLine)
980 fputc('\n', pamP->file);
981 }
982 }
983
984
985
986 /* About the 'plainformat' member on image input operations:
987
988 'plainformat' is meaningless when reading an image, but we always
989 set it FALSE anyway.
990
991 That's because it is common for a program to copy a pam structure
992 used for input as a pam structure for output, and just update the
993 few fields it cares about -- mainly 'file'. We want a program like
994 that to write a raw format image, and 'plainformat' in an output
995 pam structure is what determines whether it is raw or plain. So we
996 set it false here so that it is false in the copied output pam
997 structure.
998
999 Before 10.32, we set 'plainformat' according to the
1000 plainness of the input image, and thought it was a good
1001 idea for a program that reads a plain format input image to
1002 write a plain format output image. But that is not what
1003 the older libnetpbm facilities (e.g. pnm_writepnm()) do, so
1004 for compatibility, the pam facility shouldn't either. This
1005 came to light as we converted programs from the pnm/pbm/ppm
1006 facilities to pam.
1007 */
1008
1009 void
pnm_writepaminit(struct pam * const pamP)1010 pnm_writepaminit(struct pam * const pamP) {
1011
1012 const char * tupleType;
1013
1014 if (pamP->size < pamP->len)
1015 pm_error("pam object passed to pnm_writepaminit() is smaller "
1016 "(%u bytes, according to its 'size' element) "
1017 "than the amount of data in it "
1018 "(%u bytes, according to its 'len' element).",
1019 pamP->size, pamP->len);
1020
1021 if (pamP->size < PAM_STRUCT_SIZE(bytes_per_sample))
1022 pm_error("pam object passed to pnm_writepaminit() is too small. "
1023 "It must be large "
1024 "enough to hold at least up through the "
1025 "'bytes_per_sample' member, but according "
1026 "to its 'size' member, it is only %u bytes long.",
1027 pamP->size);
1028 if (pamP->len < PAM_STRUCT_SIZE(maxval))
1029 pm_error("pam object must contain members at least through 'maxval', "
1030 "but according to the 'len' member, it is only %u bytes "
1031 "long.", pamP->len);
1032
1033 if (pamP->maxval > PAM_OVERALL_MAXVAL)
1034 pm_error("maxval (%lu) passed to pnm_writepaminit() "
1035 "is greater than %u", pamP->maxval, PAM_OVERALL_MAXVAL);
1036
1037 if (pamP->len < PAM_STRUCT_SIZE(tuple_type)) {
1038 tupleType = "";
1039 if (pamP->size >= PAM_STRUCT_SIZE(tuple_type))
1040 pamP->tuple_type[0] = '\0';
1041 } else
1042 tupleType = pamP->tuple_type;
1043
1044 pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval);
1045
1046 if (pamP->size >= PAM_STRUCT_SIZE(comment_p) &&
1047 pamP->len < PAM_STRUCT_SIZE(comment_p))
1048 pamP->comment_p = NULL;
1049
1050 if (pamP->size >= PAM_STRUCT_SIZE(allocation_depth) &&
1051 pamP->len < PAM_STRUCT_SIZE(allocation_depth))
1052 pamP->allocation_depth = 0;
1053
1054 interpretTupleType(pamP);
1055
1056 pamP->len = MIN(pamP->size, PAM_STRUCT_SIZE(opacity_plane));
1057
1058 switch (PAM_FORMAT_TYPE(pamP->format)) {
1059 case PAM_TYPE:
1060 /* See explanation below of why we ignore 'pm_plain_output' here. */
1061 fprintf(pamP->file, "P7\n");
1062 writeComments(pamP);
1063 fprintf(pamP->file, "WIDTH %u\n", (unsigned)pamP->width);
1064 fprintf(pamP->file, "HEIGHT %u\n", (unsigned)pamP->height);
1065 fprintf(pamP->file, "DEPTH %u\n", pamP->depth);
1066 fprintf(pamP->file, "MAXVAL %lu\n", pamP->maxval);
1067 if (!pm_stripeq(tupleType, ""))
1068 fprintf(pamP->file, "TUPLTYPE %s\n", pamP->tuple_type);
1069 fprintf(pamP->file, "ENDHDR\n");
1070 break;
1071 case PPM_TYPE:
1072 /* The depth must be exact, because pnm_writepamrow() is controlled
1073 by it, without regard to format.
1074 */
1075 if (pamP->depth != 3)
1076 pm_error("pnm_writepaminit() got PPM format, but depth = %d "
1077 "instead of 3, as required for PPM.",
1078 pamP->depth);
1079 if (pamP->maxval > PPM_OVERALLMAXVAL)
1080 pm_error("pnm_writepaminit() got PPM format, but maxval = %ld, "
1081 "which exceeds the maximum allowed for PPM: %d",
1082 pamP->maxval, PPM_OVERALLMAXVAL);
1083 ppm_writeppminit(pamP->file, pamP->width, pamP->height,
1084 (pixval) pamP->maxval, pamP->plainformat);
1085 break;
1086
1087 case PGM_TYPE:
1088 if (pamP->depth != 1)
1089 pm_error("pnm_writepaminit() got PGM format, but depth = %d "
1090 "instead of 1, as required for PGM.",
1091 pamP->depth);
1092 if (pamP->maxval > PGM_OVERALLMAXVAL)
1093 pm_error("pnm_writepaminit() got PGM format, but maxval = %ld, "
1094 "which exceeds the maximum allowed for PGM: %d",
1095 pamP->maxval, PGM_OVERALLMAXVAL);
1096 pgm_writepgminit(pamP->file, pamP->width, pamP->height,
1097 (gray) pamP->maxval, pamP->plainformat);
1098 break;
1099
1100 case PBM_TYPE:
1101 if (pamP->depth != 1)
1102 pm_error("pnm_writepaminit() got PBM format, but depth = %d "
1103 "instead of 1, as required for PBM.",
1104 pamP->depth);
1105 if (pamP->maxval != 1)
1106 pm_error("pnm_writepaminit() got PBM format, but maxval = %ld "
1107 "instead of 1, as required for PBM.", pamP->maxval);
1108 pbm_writepbminit(pamP->file, pamP->width, pamP->height,
1109 pamP->plainformat);
1110 break;
1111
1112 default:
1113 pm_error("Invalid format passed to pnm_writepaminit(): %d",
1114 pamP->format);
1115 }
1116 }
1117
1118
1119
1120 /* EFFECT OF -plain WHEN WRITING PAM FORMAT:
1121
1122 Before Netpbm 10.63 (June 2013), pnm_writepaminit() did a pm_error() here
1123 if 'pm_plain_output' was set (i.e. the user said -plain). But this isn't
1124 really logical, because -plain is a global option for the program and here
1125 we are just writing one image. As a global option, -plain must be defined
1126 to have effect where it makes sense and have no effect where it doesn't.
1127 Note that a program that generates GIF just ignores -plain. Note also that
1128 a program could conceivably generate both a PPM image and a PAM image.
1129
1130 Note also how we handle the other a user can request plain format: the
1131 'plainformat' member of the PAM struct. In the case of PAM, we ignore that
1132 member.
1133 */
1134
1135
1136
1137 void
pnm_checkpam(const struct pam * const pamP,enum pm_check_type const checkType,enum pm_check_code * const retvalP)1138 pnm_checkpam(const struct pam * const pamP,
1139 enum pm_check_type const checkType,
1140 enum pm_check_code * const retvalP) {
1141
1142 if (checkType != PM_CHECK_BASIC) {
1143 if (retvalP) *retvalP = PM_CHECK_UNKNOWN_TYPE;
1144 } else switch (PAM_FORMAT_TYPE(pamP->format)) {
1145 case PAM_TYPE: {
1146 pm_filepos const need_raster_size =
1147 pamP->width * pamP->height * pamP->depth * pamP->bytes_per_sample;
1148 pm_check(pamP->file, checkType, need_raster_size, retvalP);
1149 }
1150 break;
1151 case PPM_TYPE:
1152 pgm_check(pamP->file, checkType, pamP->format,
1153 pamP->width, pamP->height, pamP->maxval, retvalP);
1154 break;
1155 case PGM_TYPE:
1156 pgm_check(pamP->file, checkType, pamP->format,
1157 pamP->width, pamP->height, pamP->maxval, retvalP);
1158 break;
1159 case PBM_TYPE:
1160 pbm_check(pamP->file, checkType, pamP->format,
1161 pamP->width, pamP->height, retvalP);
1162 break;
1163 default:
1164 if (retvalP) *retvalP = PM_CHECK_UNCHECKABLE;
1165 }
1166 }
1167
1168
1169
1170 void
pnm_maketuplergb(const struct pam * const pamP,tuple const tuple)1171 pnm_maketuplergb(const struct pam * const pamP,
1172 tuple const tuple) {
1173
1174 if (allocationDepth(pamP) < 3)
1175 pm_error("allocation depth %u passed to pnm_maketuplergb(). "
1176 "Must be at least 3.", allocationDepth(pamP));
1177
1178 if (pamP->depth < 3)
1179 tuple[2] = tuple[1] = tuple[0];
1180 }
1181
1182
1183
1184 void
pnm_makerowrgb(const struct pam * const pamP,tuple * const tuplerow)1185 pnm_makerowrgb(const struct pam * const pamP,
1186 tuple * const tuplerow) {
1187
1188 if (pamP->depth < 3) {
1189 unsigned int col;
1190
1191 if (allocationDepth(pamP) < 3)
1192 pm_error("allocation depth %u passed to pnm_makerowrgb(). "
1193 "Must be at least 3.", allocationDepth(pamP));
1194
1195 for (col = 0; col < pamP->width; ++col) {
1196 tuple const thisTuple = tuplerow[col];
1197 thisTuple[2] = thisTuple[1] = thisTuple[0];
1198 }
1199 }
1200 }
1201
1202
1203
1204 void
pnm_makearrayrgb(const struct pam * const pamP,tuple ** const tuples)1205 pnm_makearrayrgb(const struct pam * const pamP,
1206 tuple ** const tuples) {
1207
1208 if (pamP->depth < 3) {
1209 unsigned int row;
1210 if (allocationDepth(pamP) < 3)
1211 pm_error("allocation depth %u passed to pnm_makearrayrgb(). "
1212 "Must be at least 3.", allocationDepth(pamP));
1213
1214 for (row = 0; row < pamP->height; ++row) {
1215 tuple * const tuplerow = tuples[row];
1216 unsigned int col;
1217 for (col = 0; col < pamP->width; ++col) {
1218 tuple const thisTuple = tuplerow[col];
1219 thisTuple[2] = thisTuple[1] = thisTuple[0];
1220 }
1221 }
1222 }
1223 }
1224
1225
1226
1227 void
pnm_makerowrgba(const struct pam * const pamP,tuple * const tuplerow)1228 pnm_makerowrgba(const struct pam * const pamP,
1229 tuple * const tuplerow) {
1230 /*----------------------------------------------------------------------------
1231 Make the tuples 'tuplerow' the RGBA equivalent of what they are now,
1232 which is described by *pamP.
1233
1234 This means afterward, *pamP no longer correctly describes these tuples;
1235 Caller must be sure to update *pamP it or not use it anymore.
1236
1237 We fail if Caller did not supply enough allocated space in 'tuplerow' for
1238 the extra planes (tuple allocation depth).
1239 -----------------------------------------------------------------------------*/
1240 if (pamP->len < PAM_STRUCT_SIZE(opacity_plane)) {
1241 pm_message("struct pam length %u is too small for pnm_makerowrgba(). "
1242 "This function requires struct pam fields through "
1243 "'opacity_plane'", pamP->len);
1244 abort();
1245 } else {
1246 if (!pamP->visual)
1247 pm_error("Non-visual tuples given to pnm_addopacityrow()");
1248
1249 if (pamP->color_depth >= 3 && pamP->have_opacity) {
1250 /* It's already in RGBA format. Leave it alone. */
1251 } else {
1252 unsigned int col;
1253
1254 if (allocationDepth(pamP) < 4)
1255 pm_error("allocation depth %u passed to pnm_makerowrgba(). "
1256 "Must be at least 4.", allocationDepth(pamP));
1257
1258 for (col = 0; col < pamP->width; ++col) {
1259 tuple const thisTuple = tuplerow[col];
1260 thisTuple[PAM_TRN_PLANE] =
1261 pamP->have_opacity ? thisTuple[pamP->opacity_plane] :
1262 pamP->maxval;
1263
1264 assert(PAM_RED_PLANE == 0);
1265 thisTuple[PAM_BLU_PLANE] = thisTuple[0];
1266 thisTuple[PAM_GRN_PLANE] = thisTuple[0];
1267 }
1268 }
1269 }
1270 }
1271
1272
1273
1274 void
pnm_addopacityrow(const struct pam * const pamP,tuple * const tuplerow)1275 pnm_addopacityrow(const struct pam * const pamP,
1276 tuple * const tuplerow) {
1277 /*----------------------------------------------------------------------------
1278 Add an opacity plane to the tuples in 'tuplerow', if one isn't already
1279 there.
1280
1281 This means afterward, *pamP no longer correctly describes these tuples;
1282 Caller must be sure to update *pamP it or not use it anymore.
1283
1284 We fail if Caller did not supply enough allocated space in 'tuplerow' for
1285 the extra plane (tuple allocation depth).
1286 -----------------------------------------------------------------------------*/
1287 if (pamP->len < PAM_STRUCT_SIZE(opacity_plane)) {
1288 pm_message("struct pam length %u is too small for pnm_makerowrgba(). "
1289 "This function requires struct pam fields through "
1290 "'opacity_plane'", pamP->len);
1291 abort();
1292 } else {
1293 if (!pamP->visual)
1294 pm_error("Non-visual tuples given to pnm_addopacityrow()");
1295
1296 if (pamP->have_opacity) {
1297 /* It already has opacity. Leave it alone. */
1298 } else {
1299 unsigned int const opacityPlane = pamP->color_depth;
1300
1301 unsigned int col;
1302
1303 if (allocationDepth(pamP) < opacityPlane + 1)
1304 pm_error("allocation depth %u passed to pnm_addopacityrow(). "
1305 "Must be at least %u.",
1306 allocationDepth(pamP), opacityPlane + 1);
1307
1308 for (col = 0; col < pamP->width; ++col)
1309 tuplerow[col][opacityPlane] = pamP->maxval;
1310 }
1311 }
1312 }
1313
1314
1315
1316 void
pnm_getopacity(const struct pam * const pamP,int * const haveOpacityP,unsigned int * const opacityPlaneP)1317 pnm_getopacity(const struct pam * const pamP,
1318 int * const haveOpacityP,
1319 unsigned int * const opacityPlaneP) {
1320
1321 /* Usage note: this is obsolete since we added 'have_opacity', etc.
1322 to struct pam.
1323 */
1324 if (streq(pamP->tuple_type, "RGB_ALPHA")) {
1325 *haveOpacityP = TRUE;
1326 *opacityPlaneP = PAM_TRN_PLANE;
1327 } else if (streq(pamP->tuple_type, "GRAYSCALE_ALPHA")) {
1328 *haveOpacityP = TRUE;
1329 *opacityPlaneP = PAM_GRAY_TRN_PLANE;
1330 } else
1331 *haveOpacityP = FALSE;
1332 }
1333
1334
1335
1336 tuple
pnm_backgroundtuple(struct pam * const pamP,tuple ** const tuples)1337 pnm_backgroundtuple(struct pam * const pamP,
1338 tuple ** const tuples) {
1339 /*--------------------------------------------------------------------
1340 This function was copied from libpnm3.c's pnm_backgroundxel() and
1341 modified to use tuples instead of xels.
1342 ----------------------------------------------------------------------*/
1343 tuple tuplePtr, bgtuple, ul, ur, ll, lr;
1344
1345 /* Guess a good background value. */
1346 ul = tuples[0][0];
1347 ur = tuples[0][pamP->width-1];
1348 ll = tuples[pamP->height-1][0];
1349 lr = tuples[pamP->height-1][pamP->width-1];
1350 bgtuple = NULL;
1351
1352 /* We first recognize three corners equal. If not, we look for any
1353 two. If not, we just average all four.
1354 */
1355 if (pnm_tupleequal(pamP, ul, ur) && pnm_tupleequal(pamP, ur, ll))
1356 tuplePtr = ul;
1357 else if (pnm_tupleequal(pamP, ul, ur) &&
1358 pnm_tupleequal(pamP, ur, lr))
1359 tuplePtr = ul;
1360 else if (pnm_tupleequal(pamP, ul, ll) &&
1361 pnm_tupleequal(pamP, ll, lr))
1362 tuplePtr = ul;
1363 else if (pnm_tupleequal(pamP, ur, ll) &&
1364 pnm_tupleequal(pamP, ll, lr))
1365 tuplePtr = ur;
1366 else if (pnm_tupleequal(pamP, ul, ur))
1367 tuplePtr = ul;
1368 else if (pnm_tupleequal(pamP, ul, ll))
1369 tuplePtr = ul;
1370 else if (pnm_tupleequal(pamP, ul, lr))
1371 tuplePtr = ul;
1372 else if (pnm_tupleequal(pamP, ur, ll))
1373 tuplePtr = ur;
1374 else if (pnm_tupleequal(pamP, ur, lr))
1375 tuplePtr = ur;
1376 else if (pnm_tupleequal(pamP, ll, lr))
1377 tuplePtr = ll;
1378 else {
1379 /* Reimplement libpnm3.c's mean4() but for tuples. */
1380 unsigned int plane;
1381 bgtuple = pnm_allocpamtuple(pamP);
1382 for (plane = 0; plane < pamP->depth; ++plane)
1383 bgtuple[plane] = (ul[plane] + ur[plane] + ll[plane] + lr[plane]) / 4;
1384 }
1385 if (!bgtuple) {
1386 unsigned int plane;
1387 bgtuple = pnm_allocpamtuple(pamP);
1388 for (plane = 0; plane < pamP->depth; ++plane)
1389 bgtuple[plane] = tuplePtr[plane];
1390 }
1391
1392 return bgtuple;
1393 }
1394
1395
1396
1397 /*=============================================================================
1398 pm_system() Standard Input feeder and Standard Output accepter functions.
1399 =============================================================================*/
1400
1401 void
pm_feed_from_pamtuples(int const pipeToFeedFd,void * const feederParm)1402 pm_feed_from_pamtuples(int const pipeToFeedFd,
1403 void * const feederParm) {
1404
1405 struct pamtuples * const inputTuplesP = feederParm;
1406
1407 struct pam outpam;
1408
1409 outpam = *inputTuplesP->pamP;
1410 outpam.file = fdopen(pipeToFeedFd, "w");
1411
1412 /* The following signals (and normally kills) the process with
1413 SIGPIPE if the pipe does not take all the data.
1414 */
1415 pnm_writepam(&outpam, *inputTuplesP->tuplesP);
1416
1417 pm_close(outpam.file);
1418 }
1419
1420
1421
1422 void
pm_accept_to_pamtuples(int const pipeToSuckFd,void * const accepterParm)1423 pm_accept_to_pamtuples(int const pipeToSuckFd,
1424 void * const accepterParm ) {
1425
1426 struct pamtuples * const outputTuplesP = accepterParm;
1427
1428 struct pam * const inpamP = outputTuplesP->pamP;
1429
1430 *outputTuplesP->tuplesP =
1431 pnm_readpam(fdopen(pipeToSuckFd, "r"), inpamP,
1432 PAM_STRUCT_SIZE(tuple_type));
1433
1434 pm_close(inpamP->file);
1435 }
1436