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