1 /* rasttopnm.c - read a Sun rasterfile and produce a portable anymap
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 "pm_c_util.h"
14 #include "mallocvar.h"
15 #include "shhopt.h"
16 #include "pnm.h"
17 #include "rast.h"
18
19
20
21 struct cmdlineInfo {
22 /* All the information the user supplied in the command line,
23 in a form easy for the program to use.
24 */
25 const char * inputFileName;
26 unsigned int index;
27 unsigned int dumpheader;
28 unsigned int dumpcolormap;
29 };
30
31
32
33 static void
parseCommandLine(int argc,const char ** argv,struct cmdlineInfo * const cmdlineP)34 parseCommandLine(int argc, const char ** argv,
35 struct cmdlineInfo * const cmdlineP) {
36 /*----------------------------------------------------------------------------
37 Note that the file spec array we return is stored in the storage that
38 was passed to us as the argv array.
39 -----------------------------------------------------------------------------*/
40 optEntry * option_def;
41 /* Instructions to OptParseOptions2 on how to parse our options.
42 */
43 optStruct3 opt;
44
45 unsigned int option_def_index;
46
47 MALLOCARRAY_NOFAIL(option_def, 100);
48
49 option_def_index = 0; /* incremented by OPTENT3 */
50
51 opt.opt_table = option_def;
52 opt.short_allowed = false; /* We have no short (old-fashioned) options */
53 opt.allowNegNum = false; /* We have no parms that are negative numbers */
54
55 OPTENT3(0, "index", OPT_FLAG, NULL,
56 &cmdlineP->index, 0);
57 OPTENT3(0, "dumpheader", OPT_FLAG, NULL,
58 &cmdlineP->dumpheader, 0);
59 OPTENT3(0, "dumpcolormap", OPT_FLAG, NULL,
60 &cmdlineP->dumpcolormap, 0);
61
62 pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
63 /* Uses and sets argc, argv, and some of *cmdlineP and others. */
64
65 if (argc-1 == 0)
66 cmdlineP->inputFileName = "-";
67 else if (argc-1 != 1)
68 pm_error("Program takes zero or one argument (filename). You "
69 "specified %d", argc-1);
70 else
71 cmdlineP->inputFileName = argv[1];
72 }
73
74
75
76 static bool
colorMapIsGrayscale(colormap_t const colorMap)77 colorMapIsGrayscale(colormap_t const colorMap) {
78 /*----------------------------------------------------------------------------
79 The color map contains only gray.
80 -----------------------------------------------------------------------------*/
81 unsigned int i;
82 bool grayscale;
83
84 for (i = 0, grayscale = true; i < colorMap.length; ++i) {
85 if (colorMap.map[0][i] != colorMap.map[1][i] ||
86 colorMap.map[1][i] != colorMap.map[2][i]) {
87 grayscale = false;
88 }
89 }
90 return grayscale;
91 }
92
93
94
95 static void
analyzeImage(struct rasterfile const header,colormap_t const colorMap,int * const formatP,xelval * const maxvalP,bool * const grayscaleP,xel * const zeroP,xel * const oneP)96 analyzeImage(struct rasterfile const header,
97 colormap_t const colorMap,
98 int * const formatP,
99 xelval * const maxvalP,
100 bool * const grayscaleP,
101 xel * const zeroP,
102 xel * const oneP) {
103
104 bool const grayscale =
105 header.ras_maplength == 0 || colorMapIsGrayscale(colorMap);
106
107 *grayscaleP = grayscale;
108
109 switch (header.ras_depth) {
110 case 1:
111 if (header.ras_maptype == RMT_NONE && header.ras_maplength == 0) {
112 *maxvalP = 1;
113 *formatP = PBM_TYPE;
114 PNM_ASSIGN1(*zeroP, 1);
115 PNM_ASSIGN1(*oneP, 0);
116 } else if (header.ras_maptype == RMT_EQUAL_RGB &&
117 header.ras_maplength == 6) {
118 if (grayscale) {
119 *maxvalP = 255;
120 *formatP = PGM_TYPE;
121 PNM_ASSIGN1(*zeroP, colorMap.map[0][0]);
122 PNM_ASSIGN1(*oneP, colorMap.map[0][1]);
123 } else {
124 *maxvalP = 255;
125 *formatP = PPM_TYPE;
126 PPM_ASSIGN(
127 *zeroP, colorMap.map[0][0], colorMap.map[1][0],
128 colorMap.map[2][0]);
129 PPM_ASSIGN(
130 *oneP, colorMap.map[0][1], colorMap.map[1][1],
131 colorMap.map[2][1]);
132 }
133 } else
134 pm_error(
135 "this depth-1 rasterfile has a non-standard colormap - "
136 "type %ld length %ld",
137 header.ras_maptype, header.ras_maplength);
138 break;
139
140 case 8:
141 if (grayscale) {
142 *maxvalP = 255;
143 *formatP = PGM_TYPE;
144 } else if (header.ras_maptype == RMT_EQUAL_RGB) {
145 *maxvalP = 255;
146 *formatP = PPM_TYPE;
147 } else
148 pm_error(
149 "this depth-8 rasterfile has a non-standard colormap - "
150 "type %ld length %ld",
151 header.ras_maptype, header.ras_maplength);
152 break;
153
154 case 24:
155 case 32:
156 if (header.ras_maptype == RMT_NONE && header.ras_maplength == 0);
157 else if (header.ras_maptype == RMT_RAW || header.ras_maplength == 768);
158 else
159 pm_error(
160 "this depth-%ld rasterfile has a non-standard colormap - "
161 "type %ld length %ld",
162 header.ras_depth, header.ras_maptype, header.ras_maplength);
163 *maxvalP = 255;
164 *formatP = PPM_TYPE;
165 break;
166
167 default:
168 pm_error("invalid depth: %ld. Can handle only depth 1, 8, 24, or 32.",
169 header.ras_depth);
170 }
171 }
172
173
174
175 static void
reportOutputType(int const format)176 reportOutputType(int const format) {
177
178 switch (PNM_FORMAT_TYPE(format)) {
179 case PBM_TYPE:
180 pm_message("writing PBM file");
181 break;
182 case PGM_TYPE:
183 pm_message("writing PGM file");
184 break;
185 case PPM_TYPE:
186 pm_message("writing PPM file");
187 break;
188 default:
189 abort();
190 }
191 }
192
193
194
195 static void
convertRowDepth1(const unsigned char * const rastLine,unsigned int const cols,xel const zeroXel,xel const oneXel,xel * const xelrow)196 convertRowDepth1(const unsigned char * const rastLine,
197 unsigned int const cols,
198 xel const zeroXel,
199 xel const oneXel,
200 xel * const xelrow) {
201
202 const unsigned char * byteP;
203 unsigned int col;
204 unsigned char mask;
205
206 byteP = rastLine; /* initial value */
207
208 for (col = 0, mask = 0x80; col < cols; ++col) {
209 if (mask == 0x00) {
210 ++byteP;
211 mask = 0x80;
212 }
213 xelrow[col] = (*byteP & mask) ? oneXel : zeroXel;
214 mask = mask >> 1;
215 }
216 }
217
218
219
220 static void
convertRowDepth8(const unsigned char * const lineStart,unsigned int const cols,bool const colorMapped,bool const useIndexForColor,bool const grayscale,colormap_t const colorMap,xel * const xelrow)221 convertRowDepth8(const unsigned char * const lineStart,
222 unsigned int const cols,
223 bool const colorMapped,
224 bool const useIndexForColor,
225 bool const grayscale,
226 colormap_t const colorMap,
227 xel * const xelrow) {
228 /*----------------------------------------------------------------------------
229 Convert a line of raster data from the RAST input to a row of raster
230 data for the PNM output.
231
232 'lineStart' is where the RAST row starts. 'xelrow' is where to put the
233 PNM row. 'cols' is the number of pixels in the row.
234
235 'colorMapped' means the RAST image is colormapped. If so, 'colorMap'
236 is the color map from the RAST file and 'useIndexForColor' means not
237 to use that map but instead to create a PGM row of the colormap
238 _indices_.
239
240 'grayscale' means it is a grayscale image; the output is PGM.
241 -----------------------------------------------------------------------------*/
242 const unsigned char * byteP;
243 unsigned int col;
244
245 byteP = lineStart; /* initial value */
246
247 for (col = 0; col < cols; ++col) {
248 if (colorMapped && !useIndexForColor) {
249 if (grayscale)
250 PNM_ASSIGN1(xelrow[col], colorMap.map[0][*byteP]);
251 else
252 PPM_ASSIGN(xelrow[col],
253 colorMap.map[0][*byteP],
254 colorMap.map[1][*byteP],
255 colorMap.map[2][*byteP]);
256 } else
257 PNM_ASSIGN1(xelrow[col], *byteP);
258
259 ++byteP;
260 }
261 }
262
263
264
265 static void
convertRowRgb(const unsigned char * const lineStart,unsigned int const cols,unsigned int const depth,long const rastType,bool const colorMapped,bool const useIndexForColor,colormap_t const colorMap,xel * const xelrow)266 convertRowRgb(const unsigned char * const lineStart,
267 unsigned int const cols,
268 unsigned int const depth,
269 long const rastType,
270 bool const colorMapped,
271 bool const useIndexForColor,
272 colormap_t const colorMap,
273 xel * const xelrow) {
274
275 const unsigned char * byteP;
276 unsigned int col;
277
278 byteP = lineStart; /* initial value */
279
280 for (col = 0; col < cols; ++col) {
281 xelval r, g, b;
282
283 if (depth == 32)
284 ++byteP;
285 if (rastType == RT_FORMAT_RGB) {
286 r = *byteP++;
287 g = *byteP++;
288 b = *byteP++;
289 } else {
290 b = *byteP++;
291 g = *byteP++;
292 r = *byteP++;
293 }
294 if (colorMapped && !useIndexForColor)
295 PPM_ASSIGN(xelrow[col],
296 colorMap.map[0][r],
297 colorMap.map[1][g],
298 colorMap.map[2][b]);
299 else
300 PPM_ASSIGN(xelrow[col], r, g, b);
301 }
302 }
303
304
305
306 static void
writePnm(FILE * const ofP,const struct pixrect * const pixRectP,unsigned int const cols,unsigned int const rows,xelval const maxval,int const format,unsigned int const depth,long const rastType,bool const grayscale,bool const colorMapped,colormap_t const colorMap,xel const zeroXel,xel const oneXel,bool const useIndexForColor)307 writePnm(FILE * const ofP,
308 const struct pixrect * const pixRectP,
309 unsigned int const cols,
310 unsigned int const rows,
311 xelval const maxval,
312 int const format,
313 unsigned int const depth,
314 long const rastType,
315 bool const grayscale,
316 bool const colorMapped,
317 colormap_t const colorMap,
318 xel const zeroXel,
319 xel const oneXel,
320 bool const useIndexForColor) {
321
322 struct mpr_data const mprData = *pixRectP->pr_data;
323
324 xel * xelrow;
325 unsigned int row;
326 unsigned char * lineStart;
327
328 pnm_writepnminit(ofP, cols, rows, maxval, format, 0);
329
330 xelrow = pnm_allocrow(cols);
331
332 reportOutputType(format);
333
334 for (row = 0, lineStart = mprData.md_image;
335 row < rows;
336 ++row, lineStart += mprData.md_linebytes) {
337
338 switch (depth) {
339 case 1:
340 convertRowDepth1(lineStart, cols, zeroXel, oneXel, xelrow);
341 break;
342 case 8:
343 convertRowDepth8(lineStart, cols, colorMapped, useIndexForColor,
344 grayscale, colorMap, xelrow);
345 break;
346 case 24:
347 case 32:
348 convertRowRgb(lineStart, cols, depth, rastType, colorMapped,
349 useIndexForColor, colorMap, xelrow);
350 break;
351 default:
352 pm_error("Invalid depth value: %u", depth);
353 }
354 pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
355 }
356 }
357
358
359
360 static void
dumpHeader(struct rasterfile const header)361 dumpHeader(struct rasterfile const header) {
362
363 const char * typeName;
364
365 switch (header.ras_type) {
366 case RT_OLD: typeName = "old"; break;
367 case RT_STANDARD: typeName = "standard"; break;
368 case RT_BYTE_ENCODED: typeName = "byte encoded"; break;
369 case RT_FORMAT_RGB: typeName = "format rgb"; break;
370 case RT_FORMAT_TIFF: typeName = "format_tiff"; break;
371 case RT_FORMAT_IFF: typeName = "format_iff"; break;
372 case RT_EXPERIMENTAL: typeName = "experimental"; break;
373 default: typeName = "???";
374 }
375
376 pm_message("type: %s (%lu)", typeName, (unsigned long)header.ras_type);
377 pm_message("%luw x %lul x %lud",
378 (unsigned long)header.ras_width,
379 (unsigned long)header.ras_height,
380 (unsigned long)header.ras_depth);
381 pm_message("raster length: %lu", (unsigned long)header.ras_length);
382
383 if (header.ras_maplength)
384 pm_message("Has color map");
385 }
386
387
388
389 static void
dumpHeaderAnalysis(bool const grayscale,unsigned int const depth,xel const zero,xel const one)390 dumpHeaderAnalysis(bool const grayscale,
391 unsigned int const depth,
392 xel const zero,
393 xel const one) {
394
395 pm_message("grayscale: %s", grayscale ? "YES" : "NO");
396
397 if (depth == 1) {
398 pm_message("Zero color: (%u,%u,%u)",
399 PNM_GETR(zero),
400 PNM_GETG(zero),
401 PNM_GETB(zero));
402
403 pm_message("One color: (%u,%u,%u)",
404 PNM_GETR(one),
405 PNM_GETG(one),
406 PNM_GETB(one));
407 }
408 }
409
410
411
412 static void
dumpColorMap(colormap_t const colorMap)413 dumpColorMap(colormap_t const colorMap) {
414
415 unsigned int i;
416 const char * typeName;
417
418 switch (colorMap.type) {
419 case RMT_NONE: typeName = "NONE"; break;
420 case RMT_EQUAL_RGB: typeName = "EQUAL_RGB"; break;
421 case RMT_RAW: typeName = "RAW"; break;
422 default: typeName = "???";
423 }
424
425 pm_message("color map type = %s (%u)", typeName, colorMap.type);
426
427 pm_message("color map size = %u", colorMap.length);
428
429 for (i = 0; i < colorMap.length; ++i)
430 pm_message("color %u: (%u, %u, %u)", i,
431 (unsigned char)colorMap.map[0][i],
432 (unsigned char)colorMap.map[1][i],
433 (unsigned char)colorMap.map[2][i]);
434 }
435
436
437
438 int
main(int argc,const char ** const argv)439 main(int argc, const char ** const argv) {
440
441 struct cmdlineInfo cmdline;
442 FILE * ifP;
443 struct rasterfile header;
444 colormap_t colorMap;
445 bool grayscale;
446 struct pixrect * pr;
447 int format;
448 xelval maxval;
449 xel zero, one;
450 int rc;
451
452 pm_proginit(&argc, argv);
453
454 parseCommandLine(argc, argv, &cmdline);
455
456 ifP = pm_openr(cmdline.inputFileName);
457
458 rc = pr_load_header(ifP, &header);
459 if (rc != 0 )
460 pm_error("unable to read in rasterfile header");
461
462 if (cmdline.dumpheader)
463 dumpHeader(header);
464
465 if (header.ras_maplength != 0) {
466 int rc;
467
468 rc = pr_load_colormap(ifP, &header, &colorMap);
469
470 if (rc != 0 )
471 pm_error("unable to read colormap from RAST file");
472
473 if (cmdline.dumpcolormap)
474 dumpColorMap(colorMap);
475 }
476
477 analyzeImage(header, colorMap, &format, &maxval, &grayscale, &zero, &one);
478
479 if (cmdline.dumpheader)
480 dumpHeaderAnalysis(grayscale, header.ras_depth, zero, one);
481
482 pr = pr_load_image(ifP, &header, NULL);
483 if (pr == NULL )
484 pm_error("unable to read in the image from the rasterfile" );
485
486 if (cmdline.index && header.ras_maplength == 0)
487 pm_error("You requested to use color map indices as colors (-index), "
488 "but this is not a color mapped image");
489
490 writePnm(stdout, pr, header.ras_width, header.ras_height, maxval, format,
491 header.ras_depth, header.ras_type, grayscale,
492 header.ras_maplength > 0, colorMap, zero, one, cmdline.index);
493
494 pm_close(ifP);
495 pm_close(stdout);
496
497 return 0;
498 }
499