1 /*
2  * This is derived from the file of the same name dated June 5, 1995,
3  * copied from the Army High Performance Computing Research Center's
4  * media-tools.tar.gz package, received from
5  * http://www.arc.umn.edu/gvl-software/media-tools.tar.gz on 2000.04.13.
6  *
7  * This software is copyrighted as noted below.  It may be freely copied,
8  * modified, and redistributed, provided that the copyright notice is
9  * preserved on all copies.
10  *
11  * There is no warranty or other guarantee of fitness for this software,
12  * it is provided solely "as is".  Bug reports or fixes may be sent
13  * to the author, who may or may not act on them as he desires.
14  *
15  * You may not include this software in a program or other software product
16  * without supplying the source, or without informing the end-user that the
17  * source is available for no extra charge.
18  *
19  * If you modify this software, you should include a notice giving the
20  * name of the person performing the modification, the date of modification,
21  * and the reason for such modification.
22  */
23 /*
24  * rletopnm - A conversion program to convert from Utah's "rle" image format
25  *            to pbmplus ppm or pgm image formats.
26  *
27  * Author:      Wes Barris (wes@msc.edu)
28  *              AHPCRC
29  *              Minnesota Supercomputer Center, Inc.
30  * Date:        March 30, 1994
31  * Copyright (c) Minnesota Supercomputer Center 1994
32  *
33  * 2000.04.13 adapted for Netpbm by Bryan Henderson.  Quieted compiler
34  *            warnings.  Added --alpha option.  Accept input on stdin
35  *
36  */
37 
38 #define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
39 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
40 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
41 
42 /*-----------------------------------------------------------------------------
43  * System includes.
44  */
45 #include <string.h>
46 #include <stdio.h>
47 #define NO_DECLARE_MALLOC
48 #include <rle.h>
49 
50 #include "pm_c_util.h"
51 #include "pnm.h"
52 #include "shhopt.h"
53 #include "mallocvar.h"
54 #include "nstring.h"
55 
56 #define HMSG if (headerDump) pm_message
57 #define GRAYSCALE   001 /* 8 bits, no colormap */
58 #define PSEUDOCOLOR 010 /* 8 bits, colormap */
59 #define TRUECOLOR   011 /* 24 bits, colormap */
60 #define DIRECTCOLOR 100 /* 24 bits, no colormap */
61 #define RLE_MAXVAL 255
62 /*
63  * Utah type declarations.
64  */
65 static rle_hdr hdr;
66 static rle_map * colormap;
67 
68 
69 struct CmdlineInfo {
70     /* All the information the user supplied in the command line,
71        in a form easy for the program to use.
72     */
73     char *       inputFilename;
74     unsigned int headerdump;
75     unsigned int verbose;
76     char *       alphaout;
77     bool         alphaStdout;
78 };
79 
80 
81 
82 /*
83  * Other declarations.
84  */
85 static int visual, maplen;
86 static int width, height;
87 
88 
89 
90 static void
parseCommandLine(int argc,char ** argv,struct CmdlineInfo * const cmdlineP)91 parseCommandLine(int argc, char ** argv,
92                  struct CmdlineInfo * const cmdlineP) {
93 /*----------------------------------------------------------------------------
94    Note that many of the strings that this function returns in the
95    *cmdlineP structure are actually in the supplied argv array.  And
96    sometimes, one of these strings is actually just a suffix of an entry
97    in argv!
98 -----------------------------------------------------------------------------*/
99     optEntry * option_def;  /* malloc'ed */
100     optStruct3 opt;
101     unsigned int option_def_index;
102 
103     unsigned int alphaoutSpec;
104 
105     MALLOCARRAY(option_def, 100);
106 
107     option_def_index = 0;   /* incremented by OPTENT3 */
108     OPTENT3('h', "headerdump", OPT_FLAG,
109             NULL,                      &cmdlineP->headerdump,     0);
110     OPTENT3('v', "verbose",    OPT_FLAG,
111             NULL,                      &cmdlineP->verbose,        0);
112     OPTENT3(0,   "alphaout",   OPT_STRING,
113             &cmdlineP->alphaout, &alphaoutSpec,                   0);
114 
115     opt.opt_table = option_def;
116     opt.short_allowed = TRUE;  /* We have short (old-fashioned) options */
117     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
118 
119     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
120         /* Uses and sets argc, argv, and all of *cmdlineP. */
121 
122     if (!alphaoutSpec)
123         cmdlineP->alphaout = NULL;
124 
125     if (argc - 1 == 0)
126         cmdlineP->inputFilename = NULL;  /* he wants stdin */
127     else if (argc - 1 == 1) {
128         if (streq(argv[1], "-"))
129             cmdlineP->inputFilename = NULL;  /* he wants stdin */
130         else
131             cmdlineP->inputFilename = strdup(argv[1]);
132     } else
133         pm_error("Too many arguments.  The only argument accepted "
134                  "is the input file specification");
135 
136     if (cmdlineP->alphaout &&
137         streq(cmdlineP->alphaout, "-"))
138         cmdlineP->alphaStdout = TRUE;
139     else
140         cmdlineP->alphaStdout = FALSE;
141 }
142 
143 
144 
145 static void
reportRleGetSetupError(int const rleGetSetupRc)146 reportRleGetSetupError(int const rleGetSetupRc) {
147 
148     switch (rleGetSetupRc) {
149     case -1:
150         pm_error("According to the URT library, the input is not "
151                  "an RLE file.  rle_get_setup() failed.");
152         break;
153     case -2:
154         pm_error("Unable to get memory for the color map.  "
155                  "rle_get_setup() failed.");
156         break;
157     case -3:
158         pm_error("Input file is empty.  rle_get_setup() failed.");
159         break;
160     case -4:
161         pm_error("End of file in the middle of where the RLE header should "
162                  "be.  rle_get_setup() failed.");
163         break;
164     default:
165         pm_error("rle_get_setup() failed for an unknown reason");
166     }
167 }
168 
169 
170 
171 static void
readRleHeader(FILE * const ifP,bool const headerDump)172 readRleHeader(FILE * const ifP,
173               bool   const headerDump) {
174 
175     int rc;
176     int  i;
177     hdr.rle_file = ifP;
178     rc = rle_get_setup(&hdr);
179     if (rc != 0)
180         reportRleGetSetupError(rc);
181 
182     width = hdr.xmax - hdr.xmin + 1;
183     height = hdr.ymax - hdr.ymin + 1;
184     HMSG("Image size: %dx%d", width, height);
185     if (hdr.ncolors == 1 && hdr.ncmap == 3) {
186         visual = PSEUDOCOLOR;
187         colormap = hdr.cmap;
188         maplen = (1 << hdr.cmaplen);
189         HMSG("Mapped color image with a map of length %d.",
190                 maplen);
191     }
192     else if (hdr.ncolors == 3 && hdr.ncmap == 0) {
193         visual = DIRECTCOLOR;
194         HMSG("24 bit color image, no colormap.");
195     }
196     else if (hdr.ncolors == 3 && hdr.ncmap == 3) {
197         visual = TRUECOLOR;
198         colormap = hdr.cmap;
199         maplen = (1 << hdr.cmaplen);
200         HMSG(
201                 "24 bit color image with color map of length %d" ,maplen);
202     }
203     else if (hdr.ncolors == 1 && hdr.ncmap == 0) {
204         visual = GRAYSCALE;
205         HMSG("Grayscale image.");
206     }
207     else {
208         fprintf(stderr,
209                 "ncolors = %d, ncmap = %d, I don't know how to handle this!",
210                 hdr.ncolors, hdr.ncmap);
211         exit(-1);
212     }
213     if (hdr.alpha == 0) {
214         HMSG("No alpha channel.");
215     } else if (hdr.alpha == 1) {
216         HMSG("Alpha channel exists!");
217     } else {
218         fprintf(stderr, "alpha = %d, I don't know how to handle this!\n",
219                 hdr.alpha);
220         exit(-1);
221     }
222     switch (hdr.background) {
223     case 0:
224         HMSG("Use all pixels, ignore background color.");
225         break;
226     case 1:
227         HMSG("Use only non-background pixels, ignore background color.");
228         break;
229     case 2:
230         HMSG("Use only non-background pixels, "
231              "clear to background color (default).");
232         break;
233     default:
234         HMSG("Unknown background flag!");
235         break;
236     }
237     if (hdr.background == 2)
238         for (i = 0; i < hdr.ncolors; i++)
239             HMSG(" %d", hdr.bg_color[i]);
240     if (hdr.ncolors == 1 && hdr.ncmap == 3) {
241         HMSG(" (%d %d %d)",
242                 hdr.cmap[hdr.bg_color[0]]>>8,
243                 hdr.cmap[hdr.bg_color[0]+256]>>8,
244                 hdr.cmap[hdr.bg_color[0]+512]>>8);
245     }
246     else if (hdr.ncolors == 3 && hdr.ncmap == 3) {
247         HMSG(" (%d %d %d)",
248                 hdr.cmap[hdr.bg_color[0]]>>8,
249                 hdr.cmap[hdr.bg_color[1]+256]>>8,
250                 hdr.cmap[hdr.bg_color[2]+512]>>8);
251     }
252     if (hdr.comments)
253         for (i = 0; hdr.comments[i] != NULL; i++)
254             HMSG("%s", hdr.comments[i]);
255 }
256 
257 
258 
259 static void
writePpmRaster(FILE * const imageoutFileP,FILE * const alphaFileP)260 writePpmRaster(FILE * const imageoutFileP,
261                FILE * const alphaFileP) {
262 
263     rle_pixel ***scanlines, **scanline;
264     pixval r, g, b;
265     pixel *pixelrow;
266     gray *alpharow;
267 
268     int scan;
269     int x;
270     /*
271      *  Allocate some stuff.
272      */
273     pixelrow = ppm_allocrow(width);
274     alpharow = pgm_allocrow(width);
275 
276     MALLOCARRAY(scanlines, height);
277     if (!scanlines)
278         pm_error("Failed to allocate memory for %u scanline pointers", height);
279 
280     for (scan = 0; scan < height; ++scan) {
281         int rc;
282         rc = rle_row_alloc(&hdr, &scanlines[scan]);
283         if (rc < 0)
284             pm_error("Failed to allocate memory for a scanline");
285     }
286     /*
287      * Loop through those scan lines.
288      */
289     for (scan = 0; scan < height; ++scan)
290         rle_getrow(&hdr, scanlines[height - scan - 1]);
291     for (scan = 0; scan < height; ++scan) {
292         scanline = scanlines[scan];
293         switch (visual) {
294         case GRAYSCALE:    /* 8 bits without colormap */
295             for (x = 0; x < width; x++) {
296                 r = scanline[0][x];
297                 g = scanline[0][x];
298                 b = scanline[0][x];
299                 PPM_ASSIGN(pixelrow[x], r, g, b);
300                 if (hdr.alpha)
301                     alpharow[x] = scanline[-1][x];
302                 else
303                     alpharow[x] = 0;
304             }
305             break;
306         case TRUECOLOR:    /* 24 bits with colormap */
307             for (x = 0; x < width; x++) {
308                 r = colormap[scanline[0][x]]>>8;
309                 g = colormap[scanline[1][x]+256]>>8;
310                 b = colormap[scanline[2][x]+512]>>8;
311                 PPM_ASSIGN(pixelrow[x], r, g, b);
312                 if (hdr.alpha)
313                     alpharow[x] = colormap[scanline[-1][x]];
314                 else
315                     alpharow[x] = 0;
316             }
317             break;
318         case DIRECTCOLOR:  /* 24 bits without colormap */
319             for (x = 0; x < width; x++) {
320                 r = scanline[0][x];
321                 g = scanline[1][x];
322                 b = scanline[2][x];
323                 PPM_ASSIGN(pixelrow[x], r, g, b);
324                 if (hdr.alpha)
325                     alpharow[x] = scanline[-1][x];
326                 else
327                     alpharow[x] = 0;
328             }
329             break;
330         case PSEUDOCOLOR:  /* 8 bits with colormap */
331             for (x = 0; x < width; x++) {
332                 r = colormap[scanline[0][x]]>>8;
333                 g = colormap[scanline[0][x]+256]>>8;
334                 b = colormap[scanline[0][x]+512]>>8;
335                 PPM_ASSIGN(pixelrow[x], r, g, b);
336                 if (hdr.alpha)
337                     alpharow[x] = colormap[scanline[-1][x]];
338                 else
339                     alpharow[x] = 0;
340             }
341             break;
342         default:
343             break;
344         }
345         /*
346          * Write the scan line.
347          */
348         if (imageoutFileP)
349             ppm_writeppmrow(imageoutFileP, pixelrow, width, RLE_MAXVAL, 0);
350         if (alphaFileP)
351             pgm_writepgmrow(alphaFileP, alpharow, width, RLE_MAXVAL, 0);
352 
353     } /* end of for scan = 0 to height */
354 
355     /* Free scanline memory. */
356     for (scan = 0; scan < height; ++scan)
357         rle_row_free(&hdr, scanlines[scan]);
358     free (scanlines);
359     ppm_freerow(pixelrow);
360     pgm_freerow(alpharow);
361 }
362 
363 
364 
365 static void
writePgmRaster(FILE * const imageoutFileP,FILE * const alphaFileP)366 writePgmRaster(FILE * const imageoutFileP,
367                FILE * const alphaFileP) {
368 /*----------------------------------------------------------------------------
369    Write the PGM image data
370 -----------------------------------------------------------------------------*/
371     rle_pixel ***scanlines, **scanline;
372     gray * pixelrow;
373     gray * alpharow;
374     int scan;
375     /*
376      *  Allocate some stuff.
377      */
378     pixelrow = pgm_allocrow(width);
379     alpharow = pgm_allocrow(width);
380 
381     MALLOCARRAY(scanlines, height);
382     if (!scanlines)
383         pm_error("Failed to allocate memory for %u scanline pointers", height);
384 
385     for (scan = 0; scan < height; ++scan) {
386         int rc;
387         rc = rle_row_alloc(&hdr, &scanlines[scan]);
388         if (rc < 0)
389             pm_error("Failed to allocate memory for a scanline");
390     }
391     /*
392      * Loop through those scan lines.
393      */
394     for (scan = 0; scan < height; ++scan)
395         rle_getrow(&hdr, scanlines[height - scan - 1]);
396 
397     for (scan = 0; scan < height; ++scan) {
398         int x;
399         scanline = scanlines[scan];
400         for (x = 0; x < width; ++x) {
401             pixelrow[x] = scanline[0][x];
402             if (hdr.alpha)
403                 alpharow[x] = scanline[1][x];
404             else
405                 alpharow[x] = 0;
406         }
407         if (imageoutFileP)
408             pgm_writepgmrow(imageoutFileP, pixelrow, width, RLE_MAXVAL, 0);
409         if (alphaFileP)
410             pgm_writepgmrow(alphaFileP, alpharow, width, RLE_MAXVAL, 0);
411 
412         }   /* end of for scan = 0 to height */
413 
414     /* Free scanline memory. */
415     for (scan = 0; scan < height; ++scan)
416         rle_row_free(&hdr, scanlines[scan]);
417     free(scanlines);
418     pgm_freerow(pixelrow);
419     pgm_freerow(alpharow);
420 }
421 
422 
423 
424 int
main(int argc,char ** argv)425 main(int argc, char ** argv) {
426 
427     struct CmdlineInfo cmdline;
428     FILE * ifP;
429     FILE * imageoutFileP;
430     FILE * alphaFileP;
431     char * fname;
432 
433     pnm_init( &argc, argv );
434 
435     parseCommandLine(argc, argv, &cmdline);
436 
437     fname = NULL;  /* initial value */
438 
439     if (cmdline.inputFilename != NULL )
440         ifP = pm_openr(cmdline.inputFilename);
441     else
442         ifP = stdin;
443 
444     if (cmdline.alphaStdout)
445         alphaFileP = stdout;
446     else if (cmdline.alphaout == NULL)
447         alphaFileP = NULL;
448     else {
449         alphaFileP = pm_openw(cmdline.alphaout);
450     }
451 
452     if (cmdline.alphaStdout)
453         imageoutFileP = NULL;
454     else
455         imageoutFileP = stdout;
456 
457 
458     /*
459      * Open the file.
460      */
461     /* Initialize header. */
462     hdr = *rle_hdr_init(NULL);
463     rle_names(&hdr, cmd_name( argv ), fname, 0);
464 
465     /*
466      * Read the rle file header.
467      */
468     readRleHeader(ifP, cmdline.headerdump || cmdline.verbose);
469     if (cmdline.headerdump)
470         exit(0);
471 
472     /*
473      * Write the alpha file header
474      */
475     if (alphaFileP)
476         pgm_writepgminit(alphaFileP, width, height, RLE_MAXVAL, 0);
477 
478     /*
479      * Write the pnm file header.
480      */
481     switch (visual) {
482     case GRAYSCALE:   /* 8 bits without colormap -> pgm */
483         if (cmdline.verbose)
484             pm_message("Writing pgm file.");
485         if (imageoutFileP)
486             pgm_writepgminit(imageoutFileP, width, height, RLE_MAXVAL, 0);
487         writePgmRaster(imageoutFileP, alphaFileP);
488         break;
489     default:      /* anything else -> ppm */
490         if (cmdline.verbose)
491             pm_message("Writing ppm file.");
492         if (imageoutFileP)
493             ppm_writeppminit(imageoutFileP, width, height, RLE_MAXVAL, 0);
494         writePpmRaster(imageoutFileP, alphaFileP);
495         break;
496     }
497 
498     pm_close(ifP);
499     if (imageoutFileP)
500         pm_close(imageoutFileP);
501     if (alphaFileP)
502         pm_close(alphaFileP);
503 
504     return 0;
505 }
506