1 /* libpnm3.c - pnm utility library part 3
2 **
3 ** Copyright (C) 1989, 1991 by Jef Poskanzer.
4 **
5 ** Permission to use, copy, modify, and distribute this software and its
6 ** documentation for any purpose and without fee is hereby granted, provided
7 ** that the above copyright notice appear in all copies and that both that
8 ** copyright notice and this permission notice appear in supporting
9 ** documentation.  This software is provided "as is" without express or
10 ** implied warranty.
11 */
12 
13 #include <stdbool.h>
14 #include <assert.h>
15 
16 #include "pnm.h"
17 #include "ppm.h"
18 #include "pgm.h"
19 #include "pbm.h"
20 
21 
22 
23 static xel
mean4(int const format,xel const a,xel const b,xel const c,xel const d)24 mean4(int const format,
25       xel const a,
26       xel const b,
27       xel const c,
28       xel const d) {
29 /*----------------------------------------------------------------------------
30    Return cartesian mean of the 4 colors.
31 -----------------------------------------------------------------------------*/
32     xel retval;
33 
34     switch (PNM_FORMAT_TYPE(format)) {
35     case PPM_TYPE:
36         PPM_ASSIGN(
37             retval,
38             (PPM_GETR(a) + PPM_GETR(b) + PPM_GETR(c) + PPM_GETR(d)) / 4,
39             (PPM_GETG(a) + PPM_GETG(b) + PPM_GETG(c) + PPM_GETG(d)) / 4,
40             (PPM_GETB(a) + PPM_GETB(b) + PPM_GETB(c) + PPM_GETB(d)) / 4);
41         break;
42 
43     case PGM_TYPE:
44     case PBM_TYPE:
45         PNM_ASSIGN1(
46             retval,
47             (PNM_GET1(a) + PNM_GET1(b) + PNM_GET1(c) + PNM_GET1(d))/4);
48         break;
49 
50     default:
51         pm_error("Invalid format passed to pnm_backgroundxel()");
52     }
53     return retval;
54 }
55 
56 
57 
58 xel
pnm_backgroundxel(xel ** const xels,int const cols,int const rows,xelval const maxval,int const format)59 pnm_backgroundxel(xel**  const xels,
60                   int    const cols,
61                   int    const rows,
62                   xelval const maxval,
63                   int    const format) {
64 
65     xel bgxel, ul, ur, ll, lr;
66 
67     /* Guess a good background value. */
68     ul = xels[0][0];
69     ur = xels[0][cols-1];
70     ll = xels[rows-1][0];
71     lr = xels[rows-1][cols-1];
72 
73     /* We first recognize three corners equal.  If not, we look for any
74        two.  If not, we just average all four.
75     */
76     if (PNM_EQUAL(ul, ur) && PNM_EQUAL(ur, ll))
77         bgxel = ul;
78     else if (PNM_EQUAL(ul, ur) && PNM_EQUAL(ur, lr))
79         bgxel = ul;
80     else if (PNM_EQUAL(ul, ll) && PNM_EQUAL(ll, lr))
81         bgxel = ul;
82     else if (PNM_EQUAL(ur, ll) && PNM_EQUAL(ll, lr))
83         bgxel = ur;
84     else if (PNM_EQUAL(ul, ur))
85         bgxel = ul;
86     else if (PNM_EQUAL(ul, ll))
87         bgxel = ul;
88     else if (PNM_EQUAL(ul, lr))
89         bgxel = ul;
90     else if (PNM_EQUAL(ur, ll))
91         bgxel = ur;
92     else if (PNM_EQUAL(ur, lr))
93         bgxel = ur;
94     else if (PNM_EQUAL(ll, lr))
95         bgxel = ll;
96     else
97         bgxel = mean4(format, ul, ur, ll, lr);
98 
99     return bgxel;
100 }
101 
102 
103 
104 xel
pnm_backgroundxelrow(xel * const xelrow,int const cols,xelval const maxval,int const format)105 pnm_backgroundxelrow(xel *  const xelrow,
106                      int    const cols,
107                      xelval const maxval,
108                      int    const format) {
109 
110     xel bgxel, l, r;
111 
112     /* Guess a good background value. */
113     l = xelrow[0];
114     r = xelrow[cols-1];
115 
116     if (PNM_EQUAL(l, r))
117         /* Both corners are same color, so that's the background color,
118            without any extra computation.
119         */
120         bgxel = l;
121     else {
122         /* Corners are different, so use cartesian mean of them */
123         switch (PNM_FORMAT_TYPE(format)) {
124         case PPM_TYPE:
125             PPM_ASSIGN(bgxel,
126                        (PPM_GETR(l) + PPM_GETR(r)) / 2,
127                        (PPM_GETG(l) + PPM_GETG(r)) / 2,
128                        (PPM_GETB(l) + PPM_GETB(r)) / 2
129                 );
130             break;
131 
132         case PGM_TYPE:
133             PNM_ASSIGN1(bgxel, (PNM_GET1(l) + PNM_GET1(r)) / 2);
134             break;
135 
136         case PBM_TYPE: {
137             unsigned int col, blackCnt;
138 
139             /* One black, one white.  Gotta count. */
140             for (col = 0, blackCnt = 0; col < cols; ++col) {
141                 if (PNM_GET1(xelrow[col] ) == 0)
142                     ++blackCnt;
143             }
144             if (blackCnt >= cols / 2)
145                 PNM_ASSIGN1(bgxel, 0);
146             else
147                 PNM_ASSIGN1(bgxel, maxval);
148             break;
149         }
150 
151         default:
152             pm_error("Invalid format passed to pnm_backgroundxelrow()");
153         }
154     }
155 
156     return bgxel;
157 }
158 
159 
160 
161 xel
pnm_whitexel(xelval const maxval,int const format)162 pnm_whitexel(xelval const maxval,
163              int    const format) {
164 
165     xel retval;
166 
167     switch (PNM_FORMAT_TYPE(format)) {
168     case PPM_TYPE:
169         PPM_ASSIGN(retval, maxval, maxval, maxval);
170         break;
171 
172     case PGM_TYPE:
173     case PBM_TYPE:
174         PNM_ASSIGN1(retval, maxval);
175         break;
176 
177     default:
178         pm_error("Invalid format %d passed to pnm_whitexel()", format);
179     }
180 
181     return retval;
182 }
183 
184 
185 
186 xel
pnm_blackxel(xelval const maxval,int const format)187 pnm_blackxel(xelval const maxval,
188              int    const format) {
189 
190     xel retval;
191 
192     switch (PNM_FORMAT_TYPE(format)) {
193     case PPM_TYPE:
194         PPM_ASSIGN(retval, 0, 0, 0);
195         break;
196 
197     case PGM_TYPE:
198     case PBM_TYPE:
199         PNM_ASSIGN1(retval, 0);
200         break;
201 
202     default:
203         pm_error("Invalid format %d passed to pnm_blackxel()", format);
204     }
205 
206     return retval;
207 }
208 
209 
210 
211 void
pnm_invertxel(xel * const xP,xelval const maxval,int const format)212 pnm_invertxel(xel*   const xP,
213               xelval const maxval,
214               int    const format) {
215 
216     switch (PNM_FORMAT_TYPE(format)) {
217     case PPM_TYPE:
218         PPM_ASSIGN(*xP,
219                    maxval - PPM_GETR(*xP),
220                    maxval - PPM_GETG(*xP),
221                    maxval - PPM_GETB(*xP));
222         break;
223 
224     case PGM_TYPE:
225         PNM_ASSIGN1(*xP, maxval - PNM_GET1(*xP));
226         break;
227 
228     case PBM_TYPE:
229         PNM_ASSIGN1(*xP, (PNM_GET1(*xP) == 0) ? maxval : 0);
230         break;
231 
232     default:
233         pm_error("Invalid format passed to pnm_invertxel()");
234     }
235 }
236 
237 
238 
239 void
pnm_promoteformat(xel ** const xels,int const cols,int const rows,xelval const maxval,int const format,xelval const newmaxval,int const newformat)240 pnm_promoteformat(xel ** const xels,
241                   int    const cols,
242                   int    const rows,
243                   xelval const maxval,
244                   int    const format,
245                   xelval const newmaxval,
246                   int    const newformat) {
247 
248     unsigned int row;
249 
250     for (row = 0; row < rows; ++row)
251         pnm_promoteformatrow(
252             xels[row], cols, maxval, format, newmaxval, newformat);
253 }
254 
255 
256 
257 void
pnm_promoteformatrow(xel * const xelrow,int const cols,xelval const maxval,int const format,xelval const newmaxval,int const newformat)258 pnm_promoteformatrow(xel *  const xelrow,
259                      int    const cols,
260                      xelval const maxval,
261                      int    const format,
262                      xelval const newmaxval,
263                      int    const newformat) {
264 
265     if ((PNM_FORMAT_TYPE(format) == PPM_TYPE &&
266          (PNM_FORMAT_TYPE(newformat) == PGM_TYPE ||
267           PNM_FORMAT_TYPE(newformat) == PBM_TYPE)) ||
268         (PNM_FORMAT_TYPE(format) == PGM_TYPE &&
269          PNM_FORMAT_TYPE(newformat) == PBM_TYPE)) {
270 
271         pm_error( "pnm_promoteformatrow: can't promote downwards!" );
272     } else if (PNM_FORMAT_TYPE(format) == PNM_FORMAT_TYPE(newformat)) {
273         /* We're promoting to the same type - but not necessarily maxval */
274         if (PNM_FORMAT_TYPE(format) == PBM_TYPE) {
275             /* PBM doesn't have maxval, so this is idempotent */
276         } else if (newmaxval < maxval)
277             pm_error("pnm_promoteformatrow: can't decrease maxval - "
278                      "try using pamdepth");
279         else if (newmaxval == maxval) {
280             /* Same type, same maxval => idempotent function */
281         } else {
282             /* Increase maxval. */
283             switch (PNM_FORMAT_TYPE(format)) {
284             case PGM_TYPE: {
285                 unsigned int col;
286                 for (col = 0; col < cols; ++col)
287                     PNM_ASSIGN1(xelrow[col],
288                                 PNM_GET1(xelrow[col]) * newmaxval / maxval);
289             } break;
290 
291             case PPM_TYPE: {
292                 unsigned int col;
293                 for (col = 0; col < cols; ++col)
294                     PPM_DEPTH(xelrow[col], xelrow[col], maxval, newmaxval);
295             } break;
296 
297             default:
298                 pm_error("Invalid old format passed to "
299                          "pnm_promoteformatrow()");
300             }
301         }
302     } else {
303         /* Promote to a higher type. */
304         switch (PNM_FORMAT_TYPE(format)) {
305         case PBM_TYPE:
306             switch (PNM_FORMAT_TYPE(newformat)) {
307             case PGM_TYPE: {
308                 unsigned int col;
309                 for (col = 0; col < cols; ++col) {
310                     if (PNM_GET1(xelrow[col]) == 0)
311                         PNM_ASSIGN1(xelrow[col], 0);
312                     else
313                         PNM_ASSIGN1(xelrow[col], newmaxval);
314                 }
315             } break;
316 
317             case PPM_TYPE: {
318                 unsigned int col;
319                 for (col = 0; col < cols; ++col) {
320                     if (PNM_GET1(xelrow[col]) == 0)
321                         PPM_ASSIGN(xelrow[col], 0, 0, 0);
322                     else
323                         PPM_ASSIGN(xelrow[col],
324                                    newmaxval, newmaxval, newmaxval );
325                 }
326             } break;
327 
328             default:
329                 pm_error("Invalid new format passed to "
330                          "pnm_promoteformatrow()");
331             }
332             break;
333 
334         case PGM_TYPE:
335             switch (PNM_FORMAT_TYPE(newformat)) {
336             case PPM_TYPE:
337                 if (newmaxval < maxval)
338                     pm_error("pnm_promoteformatrow: can't decrease maxval - "
339                              "try using pamdepth");
340                 else if (newmaxval == maxval) {
341                     unsigned int col;
342                     for (col = 0; col < cols; ++col) {
343                         PPM_ASSIGN(xelrow[col],
344                                    PNM_GET1(xelrow[col]),
345                                    PNM_GET1(xelrow[col]),
346                                    PNM_GET1(xelrow[col]));
347                     }
348                 } else {
349                     /* Increase maxval. */
350                     unsigned int col;
351                     for (col = 0; col < cols; ++col) {
352                         PPM_ASSIGN(xelrow[col],
353                                    PNM_GET1(xelrow[col]) * newmaxval / maxval,
354                                    PNM_GET1(xelrow[col]) * newmaxval / maxval,
355                                    PNM_GET1(xelrow[col]) * newmaxval / maxval);
356                     }
357                 }
358                 break;
359 
360             default:
361                 pm_error("Invalid new format passed to "
362                          "pnm_promoteformatrow()");
363             }
364             break;
365 
366         default:
367             pm_error("Invalid old format passed to pnm_promoteformatrow()");
368         }
369     }
370 }
371 
372 
373 
374 pixel
pnm_xeltopixel(xel const inputXel,int const format)375 pnm_xeltopixel(xel const inputXel,
376                int const format) {
377 
378     pixel outputPixel;
379 
380     switch (PNM_FORMAT_TYPE(format)) {
381     case PPM_TYPE:
382         PPM_ASSIGN(outputPixel,
383                    PNM_GETR(inputXel),
384                    PNM_GETG(inputXel),
385                    PNM_GETB(inputXel));
386         break;
387     case PGM_TYPE:
388     case PBM_TYPE:
389         PPM_ASSIGN(outputPixel,
390                    PNM_GET1(inputXel),
391                    PNM_GET1(inputXel),
392                    PNM_GET1(inputXel));
393         break;
394     default:
395         pm_error("Invalid format code %d passed to pnm_xeltopixel()",
396                  format);
397     }
398 
399     return outputPixel;
400 }
401 
402 
403 
404 xel
pnm_pixeltoxel(pixel const inputPixel)405 pnm_pixeltoxel(pixel const inputPixel) {
406 
407     return inputPixel;
408 }
409 
410 
411 
412 xel
pnm_graytoxel(gray const inputGray)413 pnm_graytoxel(gray const inputGray) {
414 
415     xel outputXel;
416 
417     PNM_ASSIGN1(outputXel, inputGray);
418 
419     return outputXel;
420 }
421 
422 
423 xel
pnm_bittoxel(bit const inputBit,xelval const maxval)424 pnm_bittoxel(bit    const inputBit,
425              xelval const maxval) {
426 
427     switch (inputBit) {
428     case PBM_BLACK: return pnm_blackxel(maxval, PBM_TYPE); break;
429     case PBM_WHITE: return pnm_whitexel(maxval, PBM_TYPE); break;
430     default:
431         assert(false);
432     }
433 }
434 
435 
436 
437 xel
pnm_parsecolorxel(const char * const colorName,xelval const maxval,int const format)438 pnm_parsecolorxel(const char * const colorName,
439                   xelval       const maxval,
440                   int          const format) {
441 
442     pixel const bgColor = ppm_parsecolor(colorName, maxval);
443 
444     xel retval;
445 
446     switch(PNM_FORMAT_TYPE(format)) {
447     case PPM_TYPE:
448         PNM_ASSIGN(retval,
449                    PPM_GETR(bgColor), PPM_GETG(bgColor), PPM_GETB(bgColor));
450         break;
451     case PGM_TYPE:
452         if (PPM_ISGRAY(bgColor))
453             PNM_ASSIGN1(retval, PPM_GETB(bgColor));
454         else
455             pm_error("Non-gray color '%s' specified for a "
456                      "grayscale (PGM) image",
457                      colorName);
458                    break;
459     case PBM_TYPE:
460         if (PPM_EQUAL(bgColor, ppm_whitepixel(maxval)))
461             PNM_ASSIGN1(retval, maxval);
462         else if (PPM_EQUAL(bgColor, ppm_blackpixel()))
463             PNM_ASSIGN1(retval, 0);
464         else
465             pm_error ("Color '%s', which is neither black nor white, "
466                       "specified for a black and white (PBM) image",
467                       colorName);
468         break;
469     default:
470         pm_error("Invalid format code %d passed to pnm_parsecolorxel()",
471                  format);
472     }
473 
474     return retval;
475 }
476