BRL-CAD
rot.c
Go to the documentation of this file.
1 /* R O T . C
2  * BRL-CAD
3  *
4  * Copyright (c) 1986-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @file libicv/rot.c
21  *
22  * Rotate, Invert, and/or Reverse the pixels in an image file.
23  *
24  * The rotation logic was worked out for data ordered with "upper
25  * left" first. It is being used on files in first quadrant order
26  * (lower left first). Thus the "forward", "backward" flags are
27  * reversed.
28  *
29  * The code was designed to never need to seek on the input, while it
30  * *may* need to seek on output (if the max buffer is too small). It
31  * would be nice if we could handle the reverse case also (e.g. pipe
32  * on stdout).
33  *
34  * Note that this program can be applied to any collection of
35  * interleaved channel byte entities.
36  *
37  */
38 
39 #include "common.h"
40 
41 #include <stdlib.h>
42 #include <string.h>
43 #include "bio.h"
44 
45 #include "bu/str.h"
46 #include "bu/log.h"
47 #include "bu/file.h"
48 #include "bu/getopt.h"
49 #include "bu/malloc.h"
50 #include "bn.h"
51 
52 
54 ssize_t firsty = -1; /* first "y" scanline in buffer */
55 ssize_t lasty = -1; /* last "y" scanline in buffer */
56 unsigned char *bp;
57 unsigned char *obp;
58 
59 ssize_t nxin = 512;
60 ssize_t nyin = 512;
63 size_t pixbytes = 1;
64 
65 
66 int
67 get_args(int argc, char **argv, FILE **ifp, FILE **ofp, double *angle)
68 {
69  int c;
70  char *in_file_name = NULL;
71  char *out_file_name = NULL;
72 
73  if (!ifp || !ofp || !angle)
74  bu_exit(1, "internal error processing arguments\n");
75 
76  bu_optind = bu_opterr = 1; /* skip the command name */
77  while ((c = bu_getopt(argc, argv, "fbri#:a:s:o:w:n:S:W:N:h?")) != -1) {
78  switch (c) {
79  case 'f':
80  minus90++;
81  break;
82  case 'b':
83  plus90++;
84  break;
85  case 'r':
86  reverse++;
87  break;
88  case 'i':
89  invert++;
90  break;
91  case '#':
92  pixbytes = atoi(bu_optarg);
93  break;
94  case 'S':
95  case 's':
96  /* square size */
97  nxin = nyin = atoi(bu_optarg);
98  break;
99  case 'W':
100  case 'w':
101  nxin = atoi(bu_optarg);
102  break;
103  case 'N':
104  case 'n':
105  nyin = atoi(bu_optarg);
106  break;
107  case 'a':
108  *angle = atof(bu_optarg);
109  break;
110  case 'o':
111  out_file_name = bu_optarg;
112  *ofp = fopen(out_file_name, "wb+");
113  if (*ofp == NULL) {
114  bu_log("ERROR: %s cannot open \"%s\" for writing\n", argv[0], out_file_name);
115  return 0;
116  }
117  break;
118 
119  default: /* '?' 'h' */
120  bu_log("ERROR: %s encountered unrecognized '-%c' option\n", argv[0], c);
121  return 0;
122  }
123  }
124 
125  /* XXX - backward compatibility hack */
126  if (bu_optind+2 == argc) {
127  nxin = atoi(argv[bu_optind++]);
128  nyin = atoi(argv[bu_optind++]);
129  }
130 
131  if (bu_optind >= argc) {
132  in_file_name = "-";
133  } else {
134  in_file_name = argv[bu_optind];
135  }
136 
137  if (BU_STR_EQUAL(in_file_name, "-")) {
138  *ifp = stdin;
139  } else {
140  *ifp = fopen(in_file_name, "rb");
141  if (*ifp == NULL) {
142  bu_log("ERROR: %s cannot open \"%s\" for reading\n", argv[0], in_file_name);
143  return 0;
144  }
145  }
146 
147  /* sanity */
148  if (isatty(fileno(*ifp))) {
149  bu_log("ERROR: %s will not read data from a tty\nRedirect input or specify an input file.\n", argv[0]);
150  return 0;
151  }
152  if (isatty(fileno(*ofp))) {
153  bu_log("ERROR: %s will not write data to a tty\nRedirect output or use the -o output option.\n", argv[0]);
154  return 0;
155  }
156 
157  if (argc > ++bu_optind) {
158  bu_log("WARNING: excess argument(s) ignored\n");
159  }
160 
161  return 1; /* OK */
162 }
163 
164 
165 static void
166 fill_buffer(FILE *ifp, unsigned char *buf)
167 {
168  buflines = fread(buf, scanbytes, buflines, ifp);
169 
170  firsty = lasty + 1;
171  lasty = firsty + (buflines - 1);
172 }
173 
174 
175 static void
176 reverse_buffer(unsigned char *buf)
177 {
178  ssize_t i;
179  size_t j;
180  unsigned char *p1, *p2, temp;
181 
182  for (i = 0; i < buflines; i++) {
183  p1 = &buf[ i * scanbytes ];
184  p2 = p1 + (scanbytes - pixbytes);
185  while (p1 < p2) {
186  for (j = 0; j < pixbytes; j++) {
187  temp = *p1;
188  *p1++ = *p2;
189  *p2++ = temp;
190  }
191  p2 -= 2*pixbytes;
192  }
193  }
194 }
195 
196 
197 /*
198  * Arbitrary angle rotation.
199  *
200  * 'a' is rotation angle
201  *
202  * Currently this needs to be able to buffer the entire image
203  * in memory at one time.
204  *
205  * To rotate a point (x, y) CCW about the origin:
206  * x' = x cos(a) - y sin(a)
207  * y' = x sin(a) + y cos(a)
208  * To rotate it about a point (xc, yc):
209  * x' = (x-xc) cos(a) - (y-yc) sin(a) + xc
210  * = x cos(a) - y sin(a) + [xc - xc cos(a) + yc sin(a)]
211  * y' = (x-xc) sin(a) + (y-yc) cos(a) + yc
212  * = x sin(a) + y cos(a) + [yc - yc cos(a) - xc sin(a)]
213  * So, to take one step in x:
214  * dx' = cos(a)
215  * dy' = sin(a)
216  * or one step in y:
217  * dx' = -sin(a)
218  * dy' = cos(a)
219  */
220 static void
221 arbrot(double a, FILE *ifp, unsigned char *buf)
222 {
223 #define DtoR(x) ((x)*DEG2RAD)
224  size_t x, y; /* working coord */
225  double x2, y2; /* its rotated position */
226  double xc, yc; /* rotation origin */
227  size_t x_min, y_min, x_max, y_max; /* area to rotate */
228  double x_goop, y_goop;
229  double sina, cosa;
230 
231  if (buflines != nyin) {
232  /* I won't all fit in the buffer */
233  fprintf(stderr, "Sorry but I can't do an arbitrary rotate of an image this large\n");
234  bu_exit (1, NULL);
235  }
236  if (buflines > nyin) buflines = nyin;
237  fill_buffer(ifp, buf);
238 
239  /*
240  * Convert rotation angle to radians.
241  * Because we "pull down" the pixel from their rotated positions
242  * to their standard ones, the sign of the rotation is reversed.
243  */
244  a = -DtoR(a);
245  sina = sin(a);
246  cosa = cos(a);
247 
248  /* XXX - Let the user pick the rotation origin? */
249  xc = nxin / 2.0;
250  yc = nyin / 2.0;
251 
252  x_goop = xc - xc * cosa + yc * sina;
253  y_goop = yc - yc * cosa - xc * sina;
254 
255  x_min = 0;
256  y_min = 0;
257  x_max = nxin;
258  y_max = nyin;
259 
260  for (y = y_min; y < y_max; y++) {
261  x2 = x_min * cosa - y * sina + x_goop;
262  y2 = x_min * sina + y * cosa + y_goop;
263  for (x = x_min; x < x_max; x++) {
264  /* check for in bounds */
265  if (x2 > 0.0
266  && ZERO(x2)
267  && x2 < (double)nxin
268  && y2 > 0.0
269  && ZERO(y2)
270  && y2 < (double)nyin)
271  {
272  putchar(buf[(int)y2*nyin + (int)x2]);
273  } else {
274  putchar(0); /* XXX - settable color? */
275  }
276  /* "forward difference" our coordinates */
277  x2 += cosa;
278  y2 += sina;
279  }
280  }
281 }
282 
283 
284 int
285 icv_rot(int argc, char **argv)
286 {
287  const size_t MAXPIXELS = 16768 * 16768; /* boo hiss */
288 
289  ssize_t x, y;
290  size_t j;
291  int ret = 0;
292  off_t outbyte, outplace;
293  FILE *ifp, *ofp;
294  unsigned char *obuf;
295  unsigned char *buffer;
296  double angle = 0.0;
297  ssize_t wrote = 0;
298 
299  ifp = stdin;
300  ofp = stdout;
301  bu_setprogname(argv[0]);
302 
303  if (!get_args(argc, argv, &ifp, &ofp, &angle)) {
304  bu_exit(1, "Usage: %s [-rifb | -a angle] [-# bytes] [-s squaresize] [-w width] [-n height] [-o outputfile] inputfile [> outputfile]\n", argv[0]);
305  }
306 
307  scanbytes = nxin * pixbytes;
308  buflines = MAXPIXELS / nxin;
309  if (buflines <= 0) {
310  bu_exit(1, "ERROR: %s is not compiled to handle a scanline that long!\n", argv[0]);
311  }
312  if (buflines > nyin) buflines = nyin;
313  buffer = (unsigned char *)bu_malloc(buflines * scanbytes, "buffer");
314  obuf = (unsigned char *)bu_malloc((nyin > nxin) ? nyin*pixbytes : nxin*pixbytes, "obuf");
315 
316  /*
317  * Break out to added arbitrary angle routine
318  */
319  if (angle > 0.0) {
320  arbrot(angle, ifp, buffer);
321  goto done;
322  }
323 
324  /*
325  * Clear our "file pointer." We need to maintain this
326  * In order to tell if seeking is required. ftell() always
327  * fails on pipes, so we can't use it.
328  */
329  outplace = 0;
330 
331  yin = 0;
332  while (yin < nyin) {
333  /* Fill buffer */
334  fill_buffer(ifp, buffer);
335  if (!buflines)
336  break;
337  if (reverse)
338  reverse_buffer(buffer);
339  if (plus90) {
340  for (x = 0; x < nxin; x++) {
341  obp = obuf;
342  bp = &buffer[ (lasty-firsty)*scanbytes + x*pixbytes ];
343  for (y = lasty+1; y > yin; y--) {
344  /* firsty? */
345  for (j = 0; j < pixbytes; j++)
346  *obp++ = *bp++;
347  bp -= scanbytes + pixbytes;
348  }
349  yout = x;
350  xout = (nyin - 1) - lasty;
351  outbyte = ((yout * nyin) + xout) * pixbytes;
352  if (outplace != outbyte) {
353  if (bu_fseek(ofp, outbyte, SEEK_SET) < 0) {
354  ret = 3;
355  perror("fseek");
356  bu_log("ERROR: %s can't seek on output (ofp=%p, outbyte=%zd)\n", argv[0], (void *)ofp, outbyte);
357  goto done;
358  }
359  outplace = outbyte;
360  }
361  wrote = fwrite(obuf, pixbytes, buflines, ofp);
362  if (wrote != buflines) {
363  ret = 4;
364  perror("fwrite");
365  bu_log("ERROR: %s can't out write image data (wrote %zd of %zd)\n", argv[0], wrote, buflines);
366  goto done;
367  }
368  outplace += buflines*pixbytes;
369  }
370  } else if (minus90) {
371  for (x = nxin; x > 0; x--) {
372  obp = obuf;
373  bp = &buffer[ (x-1)*pixbytes ];
374  for (y = firsty+1; (ssize_t)y < lasty; y++) {
375  for (j = 0; j < pixbytes; j++)
376  *obp++ = *bp++;
377  bp += scanbytes - pixbytes;
378  }
379  yout = (nxin - 1) - x + 1;
380  xout = yin;
381  outbyte = ((yout * nyin) + xout) * pixbytes;
382  if (outplace != outbyte) {
383  if (bu_fseek(ofp, outbyte, SEEK_SET) < 0) {
384  ret = 3;
385  perror("fseek");
386  bu_log("ERROR: %s can't seek on output (ofp=%p, outbyte=%zd)\n", argv[0], (void *)ofp, outbyte);
387  goto done;
388  }
389  outplace = outbyte;
390  }
391  wrote = fwrite(obuf, pixbytes, buflines, ofp);
392  if (wrote != buflines) {
393  ret = 4;
394  perror("fwrite");
395  bu_log("ERROR: %s can't out write image data (wrote %zd of %zd)\n", argv[0], wrote, buflines);
396  goto done;
397  }
398  outplace += buflines*pixbytes;
399  }
400  } else if (invert) {
401  for (y = lasty+1; (ssize_t)y > firsty; y--) {
402  yout = (nyin - 1) - y + 1;
403  outbyte = yout * scanbytes;
404  if (outplace != outbyte) {
405  if (bu_fseek(ofp, outbyte, SEEK_SET) < 0) {
406  ret = 3;
407  perror("fseek");
408  bu_log("ERROR: %s can't seek on output (ofp=%p, outbyte=%zd)\n", argv[0], (void *)ofp, outbyte);
409  goto done;
410  }
411  outplace = outbyte;
412  }
413  wrote = fwrite(&buffer[(y-firsty-1)*scanbytes], 1, scanbytes, ofp);
414  if (wrote != scanbytes) {
415  ret = 4;
416  perror("fwrite");
417  bu_log("ERROR: %s can't out write image data (wrote %zd of %zd)\n", argv[0], wrote, scanbytes);
418  goto done;
419  }
420  outplace += scanbytes;
421  }
422  } else {
423  /* Reverse only */
424  for (y = 0; y < buflines; y++) {
425  wrote = fwrite(&buffer[y*scanbytes], 1, scanbytes, ofp);
426  if (wrote != scanbytes) {
427  ret = 4;
428  perror("fwrite");
429  bu_log("ERROR: %s can't out write image data (wrote %zd of %zd)\n", argv[0], wrote, scanbytes);
430  goto done;
431  }
432  }
433  }
434 
435  yin += buflines;
436  }
437 
438 done:
439  fclose(ifp);
440  bu_free(buffer, "buffer");
441  bu_free(obuf, "obuf");
442 
443  return ret;
444 }
445 
446 
447 /*
448  * Local Variables:
449  * mode: C
450  * tab-width: 8
451  * indent-tabs-mode: t
452  * c-file-style: "stroustrup"
453  * End:
454  * ex: shiftwidth=4 tabstop=8
455  */
ptrdiff_t ssize_t
Definition: common.h:119
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
size_t pixbytes
Definition: rot.c:63
ssize_t scanbytes
Definition: rot.c:53
int bu_fseek(FILE *stream, off_t offset, int origin)
Definition: file.c:330
ssize_t yin
Definition: rot.c:61
char * bu_optarg
Definition: globals.c:91
ssize_t firsty
Definition: rot.c:54
Header file for the BRL-CAD common definitions.
int bu_optind
Definition: globals.c:89
int bu_getopt(int nargc, char *const nargv[], const char *ostr)
Definition: getopt.c:43
void * bu_malloc(size_t siz, const char *str)
Definition: malloc.c:314
#define SEEK_SET
Definition: db_open.c:52
unsigned char * obp
Definition: rot.c:57
ssize_t buflines
Definition: rot.c:53
void bu_exit(int status, const char *fmt,...) _BU_ATTR_NORETURN _BU_ATTR_PRINTF23
Definition: bomb.c:195
int plus90
Definition: rot.c:62
int minus90
Definition: rot.c:62
unsigned char * bp
Definition: rot.c:56
int get_args(int argc, char **argv, FILE **ifp, FILE **ofp, double *angle)
Definition: rot.c:67
ssize_t xout
Definition: rot.c:61
ssize_t yout
Definition: rot.c:61
#define ZERO(val)
Definition: units.c:38
int reverse
Definition: rot.c:62
ssize_t nxin
Definition: rot.c:59
ssize_t lasty
Definition: rot.c:55
int bu_opterr
Definition: globals.c:88
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
int icv_rot(int argc, char **argv)
Definition: rot.c:285
int invert
Definition: rot.c:62
#define DtoR(x)
ssize_t nyin
Definition: rot.c:60
void bu_setprogname(const char *path)
Definition: progname.c:143
#define BU_STR_EQUAL(s1, s2)
Definition: str.h:126