1 /* xloadimage.c:
2 *
3 * generic image loader for X11
4 *
5 * jim frost 09.27.89
6 *
7 * Copyright 1989, 1990, 1991 Jim Frost.
8 * See included file "copyright.h" for complete copyright information.
9 */
10
11 #include "copyright.h"
12 #include "xloadimage.h"
13 #include "options.h"
14 #include "misc.h"
15 #ifdef VMS
16 #include "patchlevel."
17 #define NO_FORK
18 #else
19 #include "patchlevel"
20 #endif
21 #ifdef HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif
24 #include <signal.h>
25
26 char *ProgramName= "xloadimage";
27
28 /* if an image loader needs to have our display and screen, it will get
29 * them from here. this is done to keep most of the image routines
30 * clean
31 */
32
33 Display *Disp= NULL;
34 int Scrn= 0;
35
36 /* used for the -default option. this is the root weave bitmap with
37 * the bits in the order that xloadimage likes.
38 */
39
40 #define root_weave_width 4
41 #define root_weave_height 4
42 static byte root_weave_bits[] = {
43 0xe0, 0xb0, 0xd0, 0x70
44 };
45
doProcessOnImage(image,option,verbose)46 static Image *doProcessOnImage(image, option, verbose)
47 Image *image;
48 Option *option;
49 { Image *retimage= image;
50 XColor xcolor; /* color for foreground/background */
51
52 switch (option->type) {
53 case BACKGROUND:
54 if (image->depth > 1)
55 break;
56 XParseColor(Disp, DefaultColormap(Disp, Scrn), option->info.background,
57 &xcolor);
58 image->rgb.red[0]= xcolor.red;
59 image->rgb.green[0]= xcolor.green;
60 image->rgb.blue[0]= xcolor.blue;
61 break;
62
63 case BRIGHT:
64 brighten(image, option->info.bright, verbose);
65 break;
66
67 case CLIP:
68 retimage= clip(image, option->info.clip.x, option->info.clip.y,
69 option->info.clip.w, option->info.clip.h, verbose);
70 break;
71
72 case COLORS:
73 retimage= reduce(image, option->info.colors, verbose);
74 break;
75
76 case DITHER:
77 retimage= dither(image, verbose);
78 break;
79
80 case FOREGROUND:
81 if (image->depth > 1)
82 break;
83 XParseColor(Disp, DefaultColormap(Disp, Scrn), option->info.foreground,
84 &xcolor);
85 image->rgb.red[1]= xcolor.red;
86 image->rgb.green[1]= xcolor.green;
87 image->rgb.blue[1]= xcolor.blue;
88 break;
89
90 case GAMMA:
91 gammacorrect(image, option->info.gamma, verbose);
92 break;
93
94 case GRAY:
95 if (BITMAPP(image))
96 retimage= undither(image, verbose);
97 else
98 gray(image, verbose);
99 break;
100
101 case HALFTONE:
102 retimage= halftone(image, verbose);
103 break;
104
105 case NORMALIZE:
106 retimage= normalize(image, verbose);
107 break;
108
109 case ROTATE:
110 retimage= rotate(image, option->info.rotate, verbose);
111 break;
112
113 case SMOOTH:
114 retimage= smooth(image, 1, verbose);
115 break;
116
117 case TITLE:
118 if (image->title)
119 lfree((byte *)image->title);
120 image->title= dupString(option->info.title);
121 break;
122
123 case ZOOM:
124 retimage= zoom(image, option->info.zoom.x, option->info.zoom.y, verbose);
125 break;
126
127 default:
128 /* Nothing to do */
129 break;
130 }
131 return(retimage);
132 }
133
134 /* process a list of options on an image
135 */
processImage(image,global_options,image_options)136 static Image *processImage(image, global_options, image_options)
137 Image *image;
138 OptionSet *global_options;
139 OptionSet *image_options;
140 { Option *opt;
141 Image *tmpimage;
142 unsigned int verbose;
143
144 verbose= (getOption(global_options, VERBOSE) != NULL);
145
146 /* go through the global options and process them
147 */
148 for (opt= global_options->options; opt; opt= opt->next) {
149
150 /* if option already exists locally for this image, ignore it
151 */
152 if (getOption(image_options, opt->type))
153 continue;
154 tmpimage= doProcessOnImage(image, opt, verbose);
155 if (tmpimage != image) {
156 freeImage(image);
157 image= tmpimage;
158 }
159 }
160
161 /* go through local options
162 */
163 for (opt= image_options->options; opt; opt= opt->next) {
164 tmpimage= doProcessOnImage(image, opt, verbose);
165 if (tmpimage != image) {
166 freeImage(image);
167 image= tmpimage;
168 }
169 }
170 return(image);
171 }
172
173 /* the real thing
174 */
175
main(argc,argv)176 int main(argc, argv)
177 int argc;
178 char *argv[];
179 { Option *opt;
180 char *dname;
181 Image *dispimage; /* image that will be sent to the display */
182 Image *newimage; /* new image we're loading */
183 Image *tmpimage;
184 Display *disp; /* display we're sending to */
185 int scrn; /* screen we're sending to */
186 char *border; /* name of border color */
187 XColor xcolor; /* color for border option */
188 OptionSet *global_options; /* set of global options */
189 OptionSet *image_options; /* set of image options */
190 OptionSet *optset, *tmpset;
191 Option *dump;
192 unsigned int fullscreen;
193 unsigned int onroot;
194 unsigned int verbose;
195 unsigned int winwidth, winheight; /* geometry of image */
196 unsigned int shrinktofit;
197
198 /* set up internal error handlers
199 */
200
201 signal(SIGSEGV, internalError);
202 #ifdef SIGBUS
203 signal(SIGBUS, internalError);
204 #endif
205 signal(SIGFPE, internalError);
206 signal(SIGILL, internalError);
207 #if defined(_AIX) && defined(_IBMR2)
208 /* the RS/6000 (AIX 3.1) has a new signal, SIGDANGER, which you get
209 * when memory is exhausted. since malloc() can overcommit, it's a good
210 * idea to trap this one.
211 */
212 signal(SIGDANGER, memoryExhausted);
213 #endif
214
215 ProgramName= argv[0];
216 if (argc < 2)
217 usage();
218
219 /* defaults and other initial settings. some of these depend on what
220 * our name was when invoked.
221 */
222
223 loadPathsAndExts();
224
225 processOptions(argc, argv, &global_options, &image_options);
226
227 verbose= (getOption(global_options, VERBOSE) != NULL);
228
229 /* if no images are specified and we're not setting the default root,
230 * this invocation is a no-op
231 */
232 if ((image_options->next == NULL) &&
233 (getOption(image_options, NAME) == NULL) &&
234 (getOption(global_options, DEFAULT) == NULL)) {
235 fprintf(stderr, "%s: No images were specified.\n", argv[0]);
236 usageHelp();
237 /* NOTREACHED */
238 }
239
240 if (getOption(global_options, IDENTIFY)) {
241 for (optset= image_options; optset; optset= optset->next) {
242 if ((opt= getOption(optset, NAME)))
243 identifyImage(opt->info.name);
244 }
245 exit(0);
246 }
247
248 /* start talking to the display
249 */
250
251 opt= getOption(global_options, DISPLAY);
252 dname= (opt ? opt->info.display : NULL);
253 if (! (Disp= disp= XOpenDisplay(dname))) {
254 printf("%s: Cannot open display\n", XDisplayName(dname));
255 exit(1);
256 }
257 Scrn= scrn= DefaultScreen(disp);
258 XSetErrorHandler(errorHandler);
259
260 /* background ourselves if the user asked us to
261 */
262
263 #ifndef NO_FORK
264 if (getOption(global_options, FORK))
265 switch(fork()) {
266 case -1:
267 perror("fork");
268 /* FALLTHRU */
269 case 0:
270 break;
271 default:
272 exit(0);
273 }
274 #endif /* !NO_FORK */
275
276 dispimage= NULL;
277
278 onroot= (getOption(global_options, ONROOT) != NULL);
279 fullscreen= (getOption(global_options, FULLSCREEN) != NULL);
280 shrinktofit= (getOption(global_options, SHRINKTOFIT) != NULL);
281 if ((opt= getOption(global_options, GEOMETRY))) {
282 winwidth= opt->info.geometry.w;
283 winheight= opt->info.geometry.h;
284 }
285 else {
286 winwidth= 0;
287 winheight= 0;
288 }
289
290 /* find out if we're supposed to dump this silly thing
291 */
292 dump= getOption(global_options, DUMP);
293
294 if (!getOption(global_options, DEFAULT) &&
295 (dump || onroot) && (winwidth || winheight ||
296 getOption(image_options, CENTER) ||
297 getOption(image_options, AT) ||
298 fullscreen)) {
299 if (!winwidth)
300 winwidth= DisplayWidth(disp, scrn);
301 if (!winheight)
302 winheight= DisplayHeight(disp, scrn);
303 opt= getOption(global_options, BORDER);
304 border= (opt ? opt->info.border : NULL);
305 if (border)
306 XParseColor(disp, DefaultColormap(disp, scrn), border, &xcolor);
307 else
308 xcolor.red= xcolor.green= xcolor.blue = 65535;
309 if (DefaultDepth(disp, scrn) == 1) {
310 dispimage= newBitImage(winwidth, winheight);
311 *(dispimage->rgb.red)= xcolor.red;
312 *(dispimage->rgb.green)= xcolor.green;
313 *(dispimage->rgb.blue)= xcolor.blue;
314 if (xcolor.red || xcolor.blue || xcolor.green) {
315 *(dispimage->rgb.red + 1)= 0;
316 *(dispimage->rgb.green + 1)= 0;
317 *(dispimage->rgb.blue + 1)= 0;
318 }
319 else {
320 *(dispimage->rgb.red + 1)= 65535;
321 *(dispimage->rgb.green + 1)= 65535;
322 *(dispimage->rgb.blue + 1)= 65535;
323 }
324 fill(dispimage, 0, 0, winwidth, winheight, 0);
325 }
326 else {
327 dispimage= newTrueImage(winwidth, winheight);
328 dispimage->rgb.used= 1;
329 fill(dispimage, 0, 0, winwidth, winheight,
330 RGB_TO_TRUE(xcolor.red, xcolor.green, xcolor.blue));
331 }
332 dispimage->title= dupString("Root Image");
333 }
334
335 /* load in each named image
336 */
337
338 for (optset= image_options; optset; optset= optset->next) {
339 get_another_image:
340
341 /* handle -default option. this creates a base image using the
342 * default tile weave.
343 */
344 if (getOption(image_options, DEFAULT)) {
345 newimage= newBitImage(root_weave_width, root_weave_height);
346 bcopy(root_weave_bits, newimage->data,
347 ((root_weave_width / 8) + (root_weave_width % 8 ? 1 : 0)) *
348 root_weave_height);
349 }
350 else if (! (opt= getOption(optset, NAME))) {
351
352 /* this gets post-processing accomplished for -dump and -onroot.
353 */
354 if (dispimage)
355 dispimage= processImage(dispimage, global_options, optset);
356 continue;
357 }
358 else if (! (newimage= loadImage(global_options, optset, opt->info.name, verbose)))
359 continue;
360
361 /* retitle the image if we were asked to
362 */
363 if ((opt= getOption(optset, TITLE))) {
364 if (newimage->title)
365 lfree((byte *)newimage->title);
366 newimage->title= dupString(opt->info.title);
367 }
368
369 /* if this is the first image and we're putting it on the root window
370 * in fullscreen mode, set the zoom factors and
371 * location to something reasonable.
372 */
373
374 if ((optset == image_options) && onroot && fullscreen &&
375 !getOption(optset, ZOOM) && !getOption(optset, AT) &&
376 !getOption(optset, CENTER)) {
377
378 opt= newOption(ZOOM);
379 if ((newimage->width > DisplayWidth(disp, scrn)) ||
380 (newimage->height > DisplayHeight(disp, scrn))) {
381 opt->info.zoom.x= opt->info.zoom.y=
382 ((int)newimage->width - DisplayWidth(disp, scrn) >
383 (int)newimage->height - DisplayHeight(disp, scrn) ?
384 (float)DisplayWidth(disp, scrn) / (float)newimage->width * 100.0 :
385 (float)DisplayHeight(disp, scrn) / (float)newimage->height * 100.0);
386 }
387 else {
388 opt->info.zoom.x= opt->info.zoom.y=
389 (DisplayWidth(disp, scrn) - newimage->width <
390 DisplayHeight(disp, scrn) - newimage->height ?
391 (float)DisplayWidth(disp, scrn) / (float)newimage->width * 100.0 :
392 (float)DisplayHeight(disp, scrn) / (float)newimage->height * 100.0);
393 }
394 addOption(optset, opt);
395 opt= newOption(CENTER);
396 addOption(optset, opt);
397 }
398
399 if ((optset == image_options) && shrinktofit && !onroot &&
400 !getOption(optset, ZOOM)) {
401
402 opt= newOption(ZOOM);
403
404 opt->info.zoom.x= opt->info.zoom.y=
405 (newimage->width - (DisplayWidth(disp, scrn) * 0.9) >
406 newimage->height - (DisplayHeight(disp, scrn) * 0.9) ?
407 ((float)DisplayWidth(disp, scrn) * 0.9)
408 / (float)newimage->width * 100.0 :
409 ((float)DisplayHeight(disp, scrn) * 0.9)
410 / (float)newimage->height * 100.0);
411 if ((opt->info.zoom.x > 100) || (opt->info.zoom.y > 100))
412 opt->info.zoom.x=opt->info.zoom.y=100;
413
414 addOption(optset, opt);
415 }
416
417 newimage= processImage(newimage, global_options, optset);
418
419 /* handle -center
420 */
421 if (dispimage && getOption(optset, CENTER)) {
422 tmpimage= merge(dispimage, newimage,
423 (int)(dispimage->width - newimage->width) / 2,
424 (int)(dispimage->height - newimage->height) / 2,
425 verbose);
426 if (dispimage != tmpimage) {
427 freeImage(dispimage);
428 dispimage= tmpimage;
429 }
430 }
431
432 /* merge onto previous image
433 */
434 else if (dispimage) {
435 if (! dispimage->title)
436 dispimage->title= dupString(newimage->title);
437
438 /* handle -at
439 */
440 if ((opt= getOption(optset, AT)))
441 tmpimage= merge(dispimage, newimage,
442 opt->info.at.x, opt->info.at.y, verbose);
443 else
444 tmpimage= merge(dispimage, newimage, 0, 0, verbose);
445 if (dispimage != tmpimage) {
446 freeImage(dispimage);
447 dispimage= tmpimage;
448 }
449 freeImage(newimage);
450 }
451 else
452 dispimage= newimage;
453
454 /* if the user asked for tiling we tile the image now
455 */
456 if (getOption(optset, TILE)) {
457 if (!winwidth)
458 winwidth= DisplayWidth(disp, scrn);
459 if (!winheight)
460 winheight= DisplayHeight(disp, scrn);
461 dispimage= tile(newimage, 0, 0, winwidth, winheight, verbose);
462 }
463
464 /* if next image is to be merged onto this one, read it
465 */
466 if (dump || onroot || (getOption(optset->next, MERGE)))
467 continue;
468
469 redisplay_in_window:
470 switch(imageInWindow(disp, scrn, dispimage, global_options,
471 optset, argc, argv, verbose)) {
472 case '\0': /* window got nuked by someone */
473 XCloseDisplay(disp);
474 exit(1);
475 case '\003':
476 case 'q': /* user quit */
477 cleanUpWindow(disp);
478 XCloseDisplay(disp);
479 exit(0);
480 case ' ':
481 case 'n': /* next image */
482 if ((opt= getOption(optset->next, GOTO))) {
483 char *tag= opt->info.go_to;
484
485 for (tmpset= image_options; tmpset; tmpset= tmpset->next) {
486 if ((opt= getOption(tmpset, NAME)) &&
487 !strcmp(tag, opt->info.name)) {
488 optset= tmpset;
489 freeImage(dispimage);
490 dispimage= NULL;
491 goto get_another_image; /* ick */
492 }
493 }
494 fprintf(stderr, "Target for -goto %s was not found\n", tag);
495 }
496 break;
497 case 'p': /* previous image */
498 for (tmpset= image_options; tmpset && (tmpset->next != optset);
499 tmpset= tmpset->next)
500 /* EMPTY */
501 ;
502 if (!tmpset)
503 goto redisplay_in_window; /* ick */
504 optset= tmpset;
505 freeImage(dispimage);
506 dispimage= NULL;
507 goto get_another_image; /* ick */
508 case '<':
509 if ((opt = getOption(optset,ZOOM)) == NULL) {
510 opt= newOption(ZOOM);
511 opt->info.zoom.x= opt->info.zoom.y= 50.0;
512 addOption(optset, opt);
513 } else {
514 opt->info.zoom.x= opt->info.zoom.x ? opt->info.zoom.x * 0.5 : 50;
515 opt->info.zoom.y= opt->info.zoom.y ? opt->info.zoom.y * 0.5 : 50;
516 }
517 tmpimage= dispimage;
518 dispimage=
519 zoom(dispimage, 50, 50,
520 (getOption(global_options, VERBOSE) != NULL));
521 if (tmpimage != dispimage)
522 free(tmpimage);
523 goto redisplay_in_window; /* ick */
524 case '>':
525 if ((opt = getOption(optset,ZOOM)) == NULL) {
526 opt= newOption(ZOOM);
527 opt->info.zoom.x= opt->info.zoom.y= 200.0;
528 addOption(optset, opt);
529 } else {
530 opt->info.zoom.x= opt->info.zoom.x ? opt->info.zoom.x * 2.0 : 200;
531 opt->info.zoom.y= opt->info.zoom.y ? opt->info.zoom.y * 2.0 : 200;
532 }
533 tmpimage= dispimage;
534 dispimage=
535 zoom(dispimage, 200, 200,
536 (getOption(global_options, VERBOSE) != NULL));
537 if (tmpimage != dispimage)
538 free(tmpimage);
539 goto redisplay_in_window; /* ick */
540 }
541 freeImage(dispimage);
542 dispimage= NULL;
543 }
544
545 /* dump image into a NIFF file rather than displaying
546 */
547 if (dump && dispimage) {
548 for (optset= image_options; optset && optset->next; optset= optset->next)
549 /* EMPTY */
550 ;
551 if ((opt= getOption(optset, NAME))) {
552 if (dispimage->title)
553 lfree((byte *)dispimage->title);
554 dispimage->title= dupString(opt->info.title);
555 }
556 dumpImage(dispimage, dump->info.dump.type, dump->info.dump.file, verbose);
557 freeImage(dispimage);
558 dispimage= NULL;
559 exit(0);
560 }
561
562 /* display image on root
563 */
564 if (onroot && dispimage)
565 imageOnRoot(disp, scrn, dispimage, global_options, verbose);
566
567 /* shut down
568 */
569 XCloseDisplay(disp);
570 exit(0);
571 }
572