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