1 /* rawtopgm.c - convert raw grayscale bytes into a portable graymap
2 **
3 ** Copyright (C) 1989 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 <math.h>
14 
15 #include "pm_c_util.h"
16 #include "mallocvar.h"
17 #include "shhopt.h"
18 #include "pgm.h"
19 
20 struct cmdline_info {
21     /* All the information the user supplied in the command line,
22        in a form easy for the program to use.
23     */
24     const char * inputFileName;
25     unsigned int headerskip;
26     float rowskip;
27     int bottomfirst;  /* the -bottomfirst/-bt option */
28     int autosize;  /* User wants us to figure out the size */
29     unsigned int width;
30     unsigned int height;
31     int bpp;
32       /* bytes per pixel in input format.  1 or 2 */
33     int littleendian;
34       /* logical: samples in input are least significant byte first */
35     int maxval;  /* -maxval option, or -1 if none */
36 };
37 
38 
39 static void
parse_command_line(int argc,char ** argv,struct cmdline_info * cmdlineP)40 parse_command_line(int argc, char ** argv,
41                    struct cmdline_info *cmdlineP) {
42 /*----------------------------------------------------------------------------
43    Note that the file spec array we return is stored in the storage that
44    was passed to us as the argv array.
45 -----------------------------------------------------------------------------*/
46     optEntry * option_def;
47         /* Instructions to OptParseOptions3 on how to parse our options.
48          */
49     optStruct3 opt;
50 
51     unsigned int option_def_index;
52 
53     MALLOCARRAY_NOFAIL(option_def, 100);
54 
55     option_def_index = 0;   /* incremented by OPTENT3 */
56     OPTENT3(0,   "bottomfirst",   OPT_FLAG,   &cmdlineP->bottomfirst,
57             NULL,   0);
58     OPTENT3(0,   "bt",            OPT_FLAG,   &cmdlineP->bottomfirst,
59             NULL,   0);
60     OPTENT3(0,   "topbottom",     OPT_FLAG,   &cmdlineP->bottomfirst,
61             NULL,   0);
62     OPTENT3(0,   "tb",            OPT_FLAG,   &cmdlineP->bottomfirst,
63             NULL,   0);
64     OPTENT3(0,   "headerskip",    OPT_UINT,   &cmdlineP->headerskip,
65             NULL,   0);
66     OPTENT3(0,   "rowskip",       OPT_FLOAT,  &cmdlineP->rowskip,
67             NULL,   0);
68     OPTENT3(0,   "bpp",           OPT_INT,    &cmdlineP->bpp,
69             NULL,   0);
70     OPTENT3(0,   "littleendian",  OPT_FLAG,   &cmdlineP->littleendian,
71             NULL,   0);
72     OPTENT3(0,   "maxval",        OPT_UINT,   &cmdlineP->maxval,
73             NULL,   0);
74 
75     /* Set the defaults */
76     cmdlineP->bottomfirst = FALSE;
77     cmdlineP->headerskip = 0;
78     cmdlineP->rowskip = 0.0;
79     cmdlineP->bpp = 1;
80     cmdlineP->littleendian = 0;
81     cmdlineP->maxval = -1;
82 
83     opt.opt_table = option_def;
84     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
85     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
86 
87     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
88         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
89 
90     if (argc-1 == 0) {
91         cmdlineP->inputFileName = "-";
92         cmdlineP->autosize = TRUE;
93     } else if (argc-1 == 1) {
94         cmdlineP->inputFileName = argv[1];
95         cmdlineP->autosize = TRUE;
96     } else if (argc-1 == 2) {
97         cmdlineP->inputFileName = "-";
98         cmdlineP->autosize = FALSE;
99         cmdlineP->width = pm_parse_width(argv[1]);
100         cmdlineP->height = pm_parse_height(argv[2]);
101     } else if (argc-1 == 3) {
102         cmdlineP->inputFileName = argv[3];
103         cmdlineP->autosize = FALSE;
104         cmdlineP->width = pm_parse_width(argv[1]);
105         cmdlineP->height = pm_parse_height(argv[2]);
106     } else
107         pm_error("Program takes zero, one, two, or three arguments.  You "
108                  "specified %d", argc-1);
109 
110     if (cmdlineP->bpp != 1 && cmdlineP->bpp != 2)
111         pm_error("Bytes per pixel (-bpp) must be 1 or 2.  You specified %d.",
112                  cmdlineP->bpp);
113 
114     if (cmdlineP->maxval == 0)
115         pm_error("Maxval (-maxval) may not be zero.");
116 
117     if (cmdlineP->maxval > 255 && cmdlineP->bpp == 1)
118         pm_error("You have specified one byte per pixel, but a maxval "
119                  "too large to fit in one byte: %d", cmdlineP->maxval);
120     if (cmdlineP->maxval > 65535)
121         pm_error("Maxval must be less than 65536.  You specified %d.",
122                  cmdlineP->maxval);
123 
124     if (cmdlineP->rowskip && cmdlineP->autosize)
125         pm_error("If you specify -rowskip, you must also give the image "
126                  "dimensions.");
127     if (cmdlineP->rowskip && cmdlineP->bottomfirst)
128         pm_error("You cannot specify both -rowskip and -bottomfirst.  This is "
129                  "a limitation of this program.");
130 
131 }
132 
133 
134 
135 static void
compute_image_size(const struct cmdline_info cmdline,const long nread,int * const rows_p,int * const cols_p)136 compute_image_size(const struct cmdline_info cmdline, const long nread,
137                    int * const rows_p, int * const cols_p) {
138 
139     if (cmdline.autosize) {
140         int sqrt_trunc =
141             (int) sqrt((double) (nread-cmdline.headerskip));
142         if (sqrt_trunc*sqrt_trunc+cmdline.headerskip != nread)
143             pm_error( "You must specify the dimensions of the image unless "
144                       "it is a quadratic image.  This one is not quadratic: "
145                       "The number of "
146                       "pixels in the input is %ld, which is not a perfect "
147                       "square.", nread-cmdline.headerskip);
148         *rows_p = *cols_p = sqrt_trunc;
149         pm_message( "Image size: %d cols, %d rows", *cols_p, *rows_p);
150     } else {
151         *rows_p = cmdline.height;
152         *cols_p = cmdline.width;
153     }
154 }
155 
156 
157 
158 static void
skip_header(FILE * ifp,const int headerskip)159 skip_header(FILE *ifp, const int headerskip) {
160     int i;
161 
162     for ( i = 0; i < headerskip; ++i ) {
163         /* Read a byte out of the file */
164         int val;
165         val = getc( ifp );
166         if ( val == EOF )
167             pm_error("EOF / read error reading Byte %d in the header", i );
168     }
169 }
170 
171 
172 
173 static gray
read_from_file(FILE * ifp,const int bpp,const int row,const int col,const int littleendian)174 read_from_file(FILE *ifp, const int bpp, const int row, const int col,
175                const int littleendian) {
176 /*----------------------------------------------------------------------------
177    Return the next sample value from the input file 'ifp', assuming the
178    input stream is 'bpp' bytes per pixel (1 or 2).  In the case of two
179    bytes, if 'littleendian', assume least significant byte is first.
180    Otherwise, assume MSB first.
181 
182    In error messages, say this is Column 'col', Row 'row'.  Exit program if
183    error.
184 -----------------------------------------------------------------------------*/
185     gray retval;
186 
187     if (bpp == 1) {
188         int val;
189         val = getc(ifp);
190         if (val == EOF)
191             pm_error( "EOF / read error at Row %d Column %d",
192                       row, col);
193         retval = (gray) val;
194     } else {
195         short val;
196         int rc;
197         rc = littleendian ?
198             pm_readlittleshort(ifp, &val) : pm_readbigshort(ifp, &val);
199         if (rc != 0)
200             pm_error( "EOF / read error at Row %d Column %d",
201                       row, col);
202         retval = (gray) val;
203     }
204     return retval;
205 }
206 
207 
208 
209 int
main(int argc,char * argv[])210 main(int argc, char *argv[] ) {
211 
212     struct cmdline_info cmdline;
213     FILE* ifp;
214     gray* grayrow;
215     int rows, cols;
216     gray maxval;
217     char* buf;
218     /* pixels_1 and pixels_2 are the array of pixels in the input buffer
219        (assuming we are using an input buffer).  pixels_1 is the array
220        as if the pixels are one byte each.  pixels_2 is the array as if
221        they are two bytes each.
222        */
223     unsigned char *pixels_1;
224     unsigned short *pixels_2;
225     long nread;
226     int row;
227     float toskip;
228 
229     pgm_init( &argc, argv );
230 
231     parse_command_line(argc, argv, &cmdline);
232 
233     ifp = pm_openr(cmdline.inputFileName);
234 
235     if (cmdline.autosize || cmdline.bottomfirst) {
236         buf = pm_read_unknown_size( ifp, &nread );
237         pixels_1 = (unsigned char *) buf;
238         pixels_2 = (unsigned short *) buf;
239     } else
240         buf = NULL;
241 
242     compute_image_size(cmdline, nread, &rows, &cols);
243 
244     if (!buf)
245         skip_header(ifp, cmdline.headerskip);
246 
247     toskip = 0.00001;
248 
249     if (cmdline.maxval == -1)
250         maxval = (cmdline.bpp == 1 ? (gray) 255 : (gray) 65535);
251     else
252         maxval = (gray) cmdline.maxval;
253 
254     pgm_writepgminit( stdout, cols, rows, maxval, 0 );
255     grayrow = pgm_allocrow( cols );
256 
257     for ( row = 0; row < rows; ++row) {
258         int col;
259         unsigned int rowpos; /* index of this row in pixel array */
260         if (cmdline.bottomfirst)
261             rowpos = (rows-row-1) * cols;
262         else
263             rowpos = row * cols;
264 
265         for ( col = 0; col < cols; ++col )
266             if (buf) {
267                 if (cmdline.bpp == 1)
268                     grayrow[col] = pixels_1[rowpos+col];
269                 else
270                     grayrow[col] = pixels_2[rowpos+col];
271             } else {
272                 grayrow[col] = read_from_file(ifp, cmdline.bpp,
273                                               row, col,
274                                               cmdline.littleendian);
275             }
276         for ( toskip += cmdline.rowskip; toskip >= 1.0; toskip -= 1.0 ) {
277             /* Note that if we're using a buffer, cmdline.rowskip is zero */
278             int val;
279             val = getc( ifp );
280             if ( val == EOF )
281                 pm_error( "EOF / read error skipping bytes at the end "
282                           "of Row %d.", row);
283         }
284         pgm_writepgmrow( stdout, grayrow, cols, maxval, 0 );
285     }
286 
287     if (buf)
288         free(buf);
289     pm_close( ifp );
290     pm_close( stdout );
291 
292     exit( 0 );
293 }
294 
295 
296 
297