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