1 /*=========================================================================
2 ppmcolormask
3 ===========================================================================
4
5 This program produces a PBM mask of areas containing a certain color.
6
7 By Bryan Henderson, Olympia WA; April 2000.
8
9 Contributed to the public domain by its author.
10 =========================================================================*/
11
12 #define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
13 #define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */
14 #define _BSD_SOURCE /* Make sure strdup() is in <string.h> */
15 #include <assert.h>
16 #include <string.h>
17
18 #include "pm_c_util.h"
19 #include "shhopt.h"
20 #include "mallocvar.h"
21 #include "nstring.h"
22 #include "ppm.h"
23 #include "pam.h"
24
25 typedef enum {
26 MATCH_EXACT,
27 MATCH_BK
28 } MatchType;
29
30 struct CmdlineInfo {
31 /* All the information the user supplied in the command line,
32 in a form easy for the program to use.
33 */
34 const char * inputFilename;
35 unsigned int colorCt;
36 struct {
37 MatchType matchType;
38 union {
39 tuplen color; /* matchType == MATCH_EXACT */
40 bk_color bkColor; /* matchType == MATCH_BK */
41 } u;
42 } maskColor[16];
43 unsigned int verbose;
44 };
45
46
47
48 static void
freeCmdline(struct CmdlineInfo * const cmdlineP)49 freeCmdline(struct CmdlineInfo * const cmdlineP) {
50
51 unsigned int i;
52
53 for (i = 0; i < cmdlineP->colorCt; ++ i) {
54 if (cmdlineP->maskColor[i].matchType == MATCH_EXACT)
55 free(cmdlineP->maskColor[i].u.color);
56 }
57 }
58
59
60
61 static void
parseColorOpt(const char * const colorOpt,struct CmdlineInfo * const cmdlineP)62 parseColorOpt(const char * const colorOpt,
63 struct CmdlineInfo * const cmdlineP) {
64
65 unsigned int colorCt;
66 char * colorOptWork;
67 char * cursor;
68 bool eol;
69
70 colorOptWork = strdup(colorOpt);
71 cursor = &colorOptWork[0];
72
73 eol = FALSE; /* initial value */
74 colorCt = 0; /* initial value */
75 while (!eol && colorCt < ARRAY_SIZE(cmdlineP->maskColor)) {
76 const char * token;
77 token = pm_strsep(&cursor, ",");
78 if (token) {
79 if (strneq(token, "bk:", 3)) {
80 cmdlineP->maskColor[colorCt].matchType = MATCH_BK;
81 cmdlineP->maskColor[colorCt].u.bkColor =
82 ppm_bk_color_from_name(&token[3]);
83 } else {
84 cmdlineP->maskColor[colorCt].matchType = MATCH_EXACT;
85 cmdlineP->maskColor[colorCt].u.color =
86 pnm_parsecolorn(token);
87 }
88 ++colorCt;
89 } else
90 eol = TRUE;
91 }
92 free(colorOptWork);
93
94 cmdlineP->colorCt = colorCt;
95 }
96
97
98
99 static void
parseCommandLine(int argc,const char ** argv,struct CmdlineInfo * cmdlineP)100 parseCommandLine(int argc, const char ** argv,
101 struct CmdlineInfo *cmdlineP) {
102 /*----------------------------------------------------------------------------
103 Note that many of the strings that this function returns in the
104 *cmdlineP structure are actually in the supplied argv array. And
105 sometimes, one of these strings is actually just a suffix of an entry
106 in argv!
107 -----------------------------------------------------------------------------*/
108 optEntry * option_def;
109 /* Instructions to OptParseOptions3 on how to parse our options. */
110 optStruct3 opt;
111
112 unsigned int option_def_index;
113 const char * colorOpt;
114 unsigned int colorSpec;
115
116 MALLOCARRAY_NOFAIL(option_def, 100);
117
118 option_def_index = 0; /* incremented by OPTENT3 */
119 OPTENT3(0, "color", OPT_STRING, &colorOpt, &colorSpec, 0);
120 OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0);
121
122 opt.opt_table = option_def;
123 opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */
124 opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */
125
126 pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
127 /* Uses and sets argc, argv, and all of *cmdlineP. */
128
129 if (colorSpec)
130 parseColorOpt(colorOpt, cmdlineP);
131
132 if (colorSpec) {
133 if (argc-1 < 1)
134 cmdlineP->inputFilename = "-"; /* he wants stdin */
135 else if (argc-1 == 1)
136 cmdlineP->inputFilename = argv[1];
137 else
138 pm_error("Too many arguments. When you specify -color, "
139 "the only argument accepted is the optional input "
140 "file name.");
141 } else {
142 if (argc-1 < 1)
143 pm_error("You must specify the -color option.");
144 else {
145 cmdlineP->colorCt = 1;
146 cmdlineP->maskColor[0].matchType = MATCH_EXACT;
147 cmdlineP->maskColor[0].u.color = pnm_parsecolorn(argv[1]);
148
149 if (argc - 1 < 2)
150 cmdlineP->inputFilename = "-"; /* he wants stdin */
151 else if (argc-1 == 2)
152 cmdlineP->inputFilename = argv[2];
153 else
154 pm_error("Too many arguments. The only arguments accepted "
155 "are the mask color and optional input file name");
156 }
157 }
158 }
159
160
161
162 static void
setupOutput(FILE * const fileP,unsigned int const width,unsigned int const height,struct pam * const outPamP)163 setupOutput(FILE * const fileP,
164 unsigned int const width,
165 unsigned int const height,
166 struct pam * const outPamP) {
167
168 outPamP->size = sizeof(*outPamP);
169 outPamP->len = PAM_STRUCT_SIZE(tuple_type);
170 outPamP->file = fileP;
171 outPamP->format = RPBM_FORMAT;
172 outPamP->plainformat = 0;
173 outPamP->height = height;
174 outPamP->width = width;
175 outPamP->depth = 1;
176 outPamP->maxval = 1;
177 outPamP->bytes_per_sample = 1;
178 strcpy(outPamP->tuple_type, PAM_PBM_TUPLETYPE);
179 }
180
181
182
183 static bool
isBkColor(tuple const comparator,struct pam * const pamP,bk_color const comparand)184 isBkColor(tuple const comparator,
185 struct pam * const pamP,
186 bk_color const comparand) {
187
188 pixel comparatorPixel;
189 bk_color comparatorBk;
190
191 /* TODO: keep a cache of the bk color for each color in
192 a colorhash_table.
193 */
194
195 assert(pamP->depth >= 3);
196
197 PPM_ASSIGN(comparatorPixel,
198 comparator[PAM_RED_PLANE],
199 comparator[PAM_GRN_PLANE],
200 comparator[PAM_BLU_PLANE]);
201
202 comparatorBk = ppm_bk_color_from_color(comparatorPixel, pamP->maxval);
203
204 return comparatorBk == comparand;
205 }
206
207
208
209 static bool
colorIsInSet(tuple const color,struct pam * const pamP,struct CmdlineInfo const cmdline)210 colorIsInSet(tuple const color,
211 struct pam * const pamP,
212 struct CmdlineInfo const cmdline) {
213
214 bool isInSet;
215 unsigned int i;
216 tuple maskColorUnnorm;
217
218 maskColorUnnorm = pnm_allocpamtuple(pamP);
219
220 for (i = 0, isInSet = FALSE; i < cmdline.colorCt && !isInSet; ++i) {
221
222 assert(i < ARRAY_SIZE(cmdline.maskColor));
223
224 switch(cmdline.maskColor[i].matchType) {
225 case MATCH_EXACT:
226 pnm_unnormalizetuple(pamP,
227 cmdline.maskColor[i].u.color,
228 maskColorUnnorm);
229 if (pnm_tupleequal(pamP, color, maskColorUnnorm))
230 isInSet = TRUE;
231 break;
232 case MATCH_BK:
233 if (isBkColor(color, pamP, cmdline.maskColor[i].u.bkColor))
234 isInSet = TRUE;
235 break;
236 }
237 }
238
239 free(maskColorUnnorm);
240
241 return isInSet;
242 }
243
244
245
246 int
main(int argc,const char * argv[])247 main(int argc, const char *argv[]) {
248
249 struct CmdlineInfo cmdline;
250
251 FILE * ifP;
252 struct pam inPam;
253 struct pam outPam;
254
255 pm_proginit(&argc, argv);
256
257 parseCommandLine(argc, argv, &cmdline);
258
259 ifP = pm_openr(cmdline.inputFilename);
260
261 pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(allocation_depth));
262
263 pnm_setminallocationdepth(&inPam, 3);
264
265 setupOutput(stdout, inPam.width, inPam.height, &outPam);
266
267 pnm_writepaminit(&outPam);
268 {
269 tuple * const inputRow = pnm_allocpamrow(&inPam);
270 tuple * const maskRow = pnm_allocpamrow(&outPam);
271
272 unsigned int numPixelsMasked;
273
274 unsigned int row;
275
276 for (row = 0, numPixelsMasked = 0; row < inPam.height; ++row) {
277 unsigned int col;
278 pnm_readpamrow(&inPam, inputRow);
279 pnm_makerowrgb(&inPam, inputRow);
280 for (col = 0; col < inPam.width; ++col) {
281 if (colorIsInSet(inputRow[col], &inPam, cmdline)) {
282 maskRow[col][0] = PAM_BLACK;
283 ++numPixelsMasked;
284 } else
285 maskRow[col][0] = PAM_BW_WHITE;
286 }
287 pnm_writepamrow(&outPam, maskRow);
288 }
289
290 if (cmdline.verbose)
291 pm_message("%u pixels found matching %u requested colors",
292 numPixelsMasked, cmdline.colorCt);
293
294 pnm_freepamrow(maskRow);
295 pnm_freepamrow(inputRow);
296 }
297 freeCmdline(&cmdline);
298 pm_close(ifP);
299
300 return 0;
301 }
302
303
304
305