ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/px/x11image.c
Revision: 1.4
Committed: Fri Mar 2 14:58:57 1990 UTC (34 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 1.3: +11 -6 lines
Log Message:
changed order of call to XCreateWindow to support different visuals

File Contents

# User Rev Content
1 greg 1.1 /* Copyright (c) 1990 Regents of the University of California */
2    
3     #ifndef lint
4     static char SCCSid[] = "$SunId$ LBL";
5     #endif
6    
7     /*
8     * x11image.c - driver for X-windows
9     *
10     * 3/1/90
11     */
12    
13     /*
14     * Modified for X11
15     *
16     * January 1990
17     *
18     * Anat Grynberg and Greg Ward
19     */
20    
21    
22     #include "standard.h"
23    
24     #include <X11/Xlib.h>
25     #include <X11/cursorfont.h>
26     #include <X11/Xutil.h>
27    
28     #include "color.h"
29     #include "view.h"
30     #include "pic.h"
31     #include "x11raster.h"
32     #include "random.h"
33    
34     #define FONTNAME "8x13" /* text font we'll use */
35    
36     #define CTRL(c) ('c'-'@')
37    
38     #define BORWIDTH 5 /* border width */
39    
40     #define ourscreen DefaultScreen(thedisplay)
41     #define ourblack BlackPixel(thedisplay,ourscreen)
42     #define ourwhite WhitePixel(thedisplay,ourscreen)
43     #define ourroot RootWindow(thedisplay,ourscreen)
44     #define ourgc DefaultGC(thedisplay,ourscreen)
45    
46 greg 1.2 #define redraw(x,y,w,h) patch_raster(wind,(x)-xoff,(y)-yoff,x,y,w,h,ourras)
47    
48 greg 1.1 double gamcor = 2.2; /* gamma correction */
49    
50     int dither = 1; /* dither colors? */
51     int fast = 0; /* keep picture in Pixmap? */
52    
53     Window wind = 0; /* our output window */
54     Font fontid; /* our font */
55    
56 greg 1.2 int maxcolors = 0; /* maximum colors */
57 greg 1.1 int greyscale = 0; /* in grey */
58    
59     int scale = 0; /* scalefactor; power of two */
60    
61     int xoff = 0; /* x image offset */
62     int yoff = 0; /* y image offset */
63    
64     VIEW ourview = STDVIEW; /* image view parameters */
65     int gotview = 0; /* got parameters from file */
66    
67     COLR *scanline; /* scan line buffer */
68    
69     int xmax, ymax; /* picture resolution */
70     int width, height; /* window size */
71     char *fname = NULL; /* input file name */
72     FILE *fin = stdin; /* input file */
73     long *scanpos = NULL; /* scan line positions in file */
74     int cury = 0; /* current scan location */
75    
76     double exposure = 1.0; /* exposure compensation used */
77    
78     XRASTER *ourras; /* our stored image */
79     unsigned char *ourdata; /* our image data */
80    
81     struct {
82     int xmin, ymin, xsiz, ysiz;
83     } box = {0, 0, 0, 0}; /* current box */
84    
85     char *geometry = NULL; /* geometry specification */
86    
87     char *progname;
88    
89     char errmsg[128];
90    
91     extern long ftell();
92    
93     extern char *malloc(), *calloc();
94    
95     extern double atof(), pow(), log();
96    
97     Display *thedisplay;
98    
99     main(argc, argv)
100     int argc;
101     char *argv[];
102     {
103     int headline();
104     int i;
105    
106     progname = argv[0];
107    
108     for (i = 1; i < argc; i++)
109     if (argv[i][0] == '-')
110     switch (argv[i][1]) {
111     case 'c':
112     maxcolors = atoi(argv[++i]);
113     break;
114     case 'b':
115     greyscale = !greyscale;
116     break;
117     case 'm':
118     maxcolors = 2;
119     break;
120     case 'd':
121     dither = !dither;
122     break;
123     case 'f':
124     fast = !fast;
125     break;
126     case 'e':
127     if (argv[i+1][0] != '+' && argv[i+1][0] != '-')
128     goto userr;
129     scale = atoi(argv[++i]);
130     break;
131     case 'g':
132     if (!strcmp(argv[i], "-geometry"))
133     geometry = argv[++i];
134     else
135     gamcor = atof(argv[++i]);
136     break;
137     default:
138     goto userr;
139     }
140 greg 1.3 else if (argv[i][0] == '=')
141     geometry = argv[i];
142 greg 1.1 else
143     break;
144    
145 greg 1.3 if (i == argc-1) {
146 greg 1.1 fname = argv[i];
147     fin = fopen(fname, "r");
148     if (fin == NULL) {
149     sprintf(errmsg, "can't open file \"%s\"", fname);
150     quiterr(errmsg);
151     }
152 greg 1.3 } else if (i != argc)
153     goto userr;
154 greg 1.1 /* get header */
155     getheader(fin, headline);
156     /* get picture dimensions */
157     if (fgetresolu(&xmax, &ymax, fin) != (YMAJOR|YDECR))
158     quiterr("bad picture size");
159     /* set view parameters */
160     if (gotview && setview(&ourview) != NULL)
161     gotview = 0;
162     if ((scanline = (COLR *)malloc(xmax*sizeof(COLR))) == NULL)
163     quiterr("out of memory");
164    
165     init(); /* get file and open window */
166    
167     for ( ; ; )
168     getevent(); /* main loop */
169     userr:
170     fprintf(stderr,
171     "Usage: %s [-geometry spec][-b][-m][-d][-f][-c ncolors][-e +/-stops] file\n",
172     progname);
173     quit(1);
174     }
175    
176    
177     headline(s) /* get relevant info from header */
178     char *s;
179     {
180     static char *altname[] = {"rview","rpict","pinterp",VIEWSTR,NULL};
181     register char **an;
182    
183 greg 1.2 if (isexpos(s))
184     exposure *= exposval(s);
185 greg 1.1 else
186     for (an = altname; *an != NULL; an++)
187     if (!strncmp(*an, s, strlen(*an))) {
188 greg 1.2 if (sscanview(&ourview, s+strlen(*an)) > 0)
189 greg 1.1 gotview++;
190     return;
191     }
192     }
193    
194    
195     init() /* get data and open window */
196     {
197 greg 1.4 XSetWindowAttributes ourwinattr;
198 greg 1.1 XSizeHints oursizhints;
199     register int i;
200    
201     if (fname != NULL) {
202     scanpos = (long *)malloc(ymax*sizeof(long));
203     if (scanpos == NULL)
204     goto memerr;
205     for (i = 0; i < ymax; i++)
206     scanpos[i] = -1;
207     }
208     if ((thedisplay = XOpenDisplay(NULL)) == NULL)
209     quiterr("can't open display; DISPLAY variable set?");
210     if (maxcolors == 0) { /* get number of available colors */
211 greg 1.2 i = DisplayPlanes(thedisplay,ourscreen);
212     maxcolors = i > 8 ? 256 : 1<<i;
213 greg 1.1 if (maxcolors > 4) maxcolors -= 2;
214     }
215 greg 1.4 /* store image */
216     getras();
217     /* open window */
218     ourwinattr.border_pixel = ourblack;
219     ourwinattr.background_pixel = ourwhite;
220     wind = XCreateWindow(thedisplay, ourroot, 0, 0, xmax, ymax, BORWIDTH,
221     0, InputOutput, ourras->visual,
222     CWBackPixel|CWBorderPixel, &ourwinattr);
223     if (wind == 0)
224     quiterr("can't create window");
225 greg 1.1 fontid = XLoadFont(thedisplay, FONTNAME);
226     if (fontid == 0)
227     quiterr("can't get font");
228     XSetFont(thedisplay, ourgc, fontid);
229     XStoreName(thedisplay, wind, fname == NULL ? progname : fname);
230     XDefineCursor(thedisplay, wind, XCreateFontCursor(thedisplay,
231     XC_diamond_cross));
232     if (geometry != NULL) {
233     bzero((char *)&oursizhints, sizeof(oursizhints));
234     i = XParseGeometry(geometry, &oursizhints.x, &oursizhints.y,
235     &oursizhints.width, &oursizhints.height);
236 greg 1.3 if ((i&(WidthValue|HeightValue)) == (WidthValue|HeightValue))
237     oursizhints.flags |= USSize;
238     else {
239     oursizhints.width = xmax;
240     oursizhints.height = ymax;
241     oursizhints.flags |= PSize;
242     }
243     if ((i&(XValue|YValue)) == (XValue|YValue)) {
244 greg 1.1 oursizhints.flags |= USPosition;
245     if (i & XNegative)
246 greg 1.3 oursizhints.x += DisplayWidth(thedisplay,
247     ourscreen)-1-oursizhints.width-2*BORWIDTH;
248 greg 1.1 if (i & YNegative)
249 greg 1.3 oursizhints.y += DisplayHeight(thedisplay,
250     ourscreen)-1-oursizhints.height-2*BORWIDTH;
251 greg 1.1 }
252     XSetNormalHints(thedisplay, wind, &oursizhints);
253     }
254 greg 1.2 XSelectInput(thedisplay, wind, ButtonPressMask|ButtonReleaseMask
255     |ButtonMotionMask|StructureNotifyMask
256     |KeyPressMask|ExposureMask);
257 greg 1.1 XMapWindow(thedisplay, wind);
258     return;
259     memerr:
260     quiterr("out of memory");
261     } /* end of init */
262    
263    
264     quiterr(err) /* print message and exit */
265     char *err;
266     {
267     if (err != NULL) {
268     fprintf(stderr, "%s: %s\n", progname, err);
269     exit(1);
270     }
271     exit(0);
272     }
273    
274    
275     eputs(s)
276     char *s;
277     {
278     fputs(s, stderr);
279     }
280    
281    
282     quit(code)
283     int code;
284     {
285     exit(code);
286     }
287    
288    
289     getras() /* get raster file */
290     {
291     colormap ourmap;
292     XVisualInfo vinfo;
293    
294     if (maxcolors <= 2) { /* monochrome */
295     ourdata = (unsigned char *)malloc(ymax*((xmax+7)/8));
296     if (ourdata == NULL)
297     goto fail;
298     ourras = make_raster(thedisplay, ourscreen, 1, ourdata,
299     xmax, ymax, 8);
300     if (ourras == NULL)
301     goto fail;
302     getmono();
303     } else if (XMatchVisualInfo(thedisplay,ourscreen,24,TrueColor,&vinfo)) {
304     ourdata = (unsigned char *)malloc(xmax*ymax*3);
305     if (ourdata == NULL)
306     goto fail;
307     ourras = make_raster(thedisplay, ourscreen, 24, ourdata,
308     xmax, ymax, 8);
309     if (ourras == NULL)
310     goto fail;
311     getfull();
312     } else {
313     ourdata = (unsigned char *)malloc(xmax*ymax);
314     if (ourdata == NULL)
315     goto fail;
316     ourras = make_raster(thedisplay, ourscreen, 8, ourdata,
317     xmax, ymax, 8);
318     if (ourras == NULL)
319     goto fail;
320     if (greyscale)
321     biq(dither,maxcolors,1,ourmap);
322     else
323     ciq(dither,maxcolors,1,ourmap);
324 greg 1.3 if (init_rcolors(ourras, ourmap[0], ourmap[1], ourmap[2]) == 0)
325 greg 1.1 goto fail;
326     }
327     return;
328     fail:
329     quit("could not create raster image");
330     }
331    
332    
333     getevent() /* process the next event */
334     {
335     union {
336     XEvent u;
337     XConfigureEvent c;
338     XExposeEvent e;
339     XButtonPressedEvent b;
340     XKeyPressedEvent k;
341     } e;
342    
343     XNextEvent(thedisplay, &e.u);
344     switch (e.u.type) {
345     case KeyPress:
346     docom(&e.k);
347     break;
348     case ConfigureNotify:
349     width = e.c.width;
350     height = e.c.height;
351     break;
352     case MapNotify:
353     map_rcolors(ourras, wind);
354     if (fast)
355     make_rpixmap(ourras);
356     break;
357     case UnmapNotify:
358     unmap_rcolors(ourras);
359     break;
360     case Expose:
361     redraw(e.e.x, e.e.y, e.e.width, e.e.height);
362     break;
363     case ButtonPress:
364     if (e.b.state & (ShiftMask|ControlMask))
365     moveimage(&e.b);
366     else
367     getbox(&e.b);
368     break;
369     }
370     }
371    
372    
373     docom(ekey) /* execute command */
374     XKeyPressedEvent *ekey;
375     {
376     char buf[80];
377     COLOR cval;
378     XColor cvx;
379     int com, n;
380     double comp;
381     FVECT rorg, rdir;
382    
383     n = XLookupString(ekey, buf, sizeof(buf), NULL, NULL);
384     if (n == 0)
385     return(0);
386     com = buf[0];
387     switch (com) { /* interpret command */
388     case 'q':
389     case CTRL(D): /* quit */
390     quit(0);
391     case '\n':
392     case '\r':
393     case 'l':
394     case 'c': /* value */
395     if (avgbox(cval) == -1)
396     return(-1);
397     switch (com) {
398     case '\n':
399     case '\r': /* radiance */
400     sprintf(buf, "%.3f", intens(cval)/exposure);
401     break;
402     case 'l': /* luminance */
403     sprintf(buf, "%.0fn", bright(cval)*683.0/exposure);
404     break;
405     case 'c': /* color */
406     comp = pow(2.0, (double)scale);
407     sprintf(buf, "(%.2f,%.2f,%.2f)",
408     colval(cval,RED)*comp,
409     colval(cval,GRN)*comp,
410     colval(cval,BLU)*comp);
411     break;
412     }
413 greg 1.3 XDrawImageString(thedisplay, wind, ourgc,
414     box.xmin, box.ymin+box.ysiz, buf, strlen(buf));
415 greg 1.1 return(0);
416     case 'i': /* identify (contour) */
417     if (ourras->pixels == NULL)
418     return(-1);
419     n = ourdata[ekey->x-xoff+xmax*(ekey->y-yoff)];
420     n = ourras->pmap[n];
421     cvx.pixel = ourras->cdefs[n].pixel;
422     cvx.red = random() & 65535;
423     cvx.green = random() & 65535;
424     cvx.blue = random() & 65535;
425 greg 1.2 cvx.flags = DoRed|DoGreen|DoBlue;
426     XStoreColor(thedisplay, ourras->cmap, &cvx);
427 greg 1.1 return(0);
428     case 'p': /* position */
429     sprintf(buf, "(%d,%d)", ekey->x-xoff, ymax-1-ekey->y+yoff);
430     XDrawImageString(thedisplay, wind, ourgc, ekey->x, ekey->y,
431     buf, strlen(buf));
432     return(0);
433     case 't': /* trace */
434     if (!gotview) {
435     XBell(thedisplay, 0);
436     return(-1);
437     }
438     viewray(rorg, rdir, &ourview,
439     (ekey->x-xoff+.5)/xmax,
440     (ymax-1-ekey->y+yoff+.5)/ymax);
441     printf("%e %e %e ", rorg[0], rorg[1], rorg[2]);
442     printf("%e %e %e\n", rdir[0], rdir[1], rdir[2]);
443     fflush(stdout);
444     return(0);
445     case '=': /* adjust exposure */
446     if (avgbox(cval) == -1)
447     return(-1);
448     n = log(.5/bright(cval))/.69315 - scale; /* truncate */
449     if (n == 0)
450     return(0);
451     scale_rcolors(ourras, pow(2.0, (double)n));
452     scale += n;
453     sprintf(buf, "%+d", scale);
454 greg 1.3 XDrawImageString(thedisplay, wind, ourgc,
455     box.xmin, box.ymin+box.ysiz, buf, strlen(buf));
456 greg 1.1 XFlush(thedisplay);
457     free(ourdata);
458     free_raster(ourras);
459     getras();
460     /* fall through */
461     case CTRL(R): /* redraw */
462     case CTRL(L):
463     unmap_rcolors(ourras);
464     XClearWindow(thedisplay, wind);
465     map_rcolors(ourras, wind);
466     if (fast)
467     make_rpixmap(ourras);
468     redraw(0, 0, width, height);
469     return(0);
470     case ' ': /* clear */
471     redraw(box.xmin, box.ymin, box.xsiz, box.ysiz);
472     return(0);
473     default:
474 greg 1.2 XBell(thedisplay, 0);
475 greg 1.1 return(-1);
476     }
477     }
478    
479    
480 greg 1.2 moveimage(ebut) /* shift the image */
481     XButtonPressedEvent *ebut;
482 greg 1.1 {
483 greg 1.2 union {
484 greg 1.3 XEvent u;
485     XButtonReleasedEvent b;
486     XPointerMovedEvent m;
487     } e;
488     int nxo, nyo;
489 greg 1.1
490 greg 1.3 XMaskEvent(thedisplay, ButtonReleaseMask|ButtonMotionMask, &e.u);
491     while (e.u.type == MotionNotify) {
492     nxo = xoff + e.m.x - ebut->x;
493     nyo = yoff + e.m.y - ebut->y;
494     revbox(nxo, nyo, nxo+xmax, nyo+ymax);
495     XMaskEvent(thedisplay,ButtonReleaseMask|ButtonMotionMask,&e.u);
496     revbox(nxo, nyo, nxo+xmax, nyo+ymax);
497     }
498 greg 1.2 xoff += e.b.x - ebut->x;
499     yoff += e.b.y - ebut->y;
500 greg 1.1 XClearWindow(thedisplay, wind);
501     redraw(0, 0, width, height);
502     }
503    
504    
505     getbox(ebut) /* get new box */
506     XButtonPressedEvent *ebut;
507     {
508     union {
509 greg 1.2 XEvent u;
510 greg 1.1 XButtonReleasedEvent b;
511     XPointerMovedEvent m;
512     } e;
513    
514 greg 1.2 XMaskEvent(thedisplay, ButtonReleaseMask|ButtonMotionMask, &e.u);
515     while (e.u.type == MotionNotify) {
516 greg 1.1 revbox(ebut->x, ebut->y, box.xmin = e.m.x, box.ymin = e.m.y);
517 greg 1.2 XMaskEvent(thedisplay,ButtonReleaseMask|ButtonMotionMask,&e.u);
518 greg 1.1 revbox(ebut->x, ebut->y, box.xmin, box.ymin);
519     }
520     box.xmin = e.b.x<0 ? 0 : (e.b.x>=width ? width-1 : e.b.x);
521     box.ymin = e.b.y<0 ? 0 : (e.b.y>=height ? height-1 : e.b.y);
522     if (box.xmin > ebut->x) {
523     box.xsiz = box.xmin - ebut->x + 1;
524     box.xmin = ebut->x;
525     } else {
526     box.xsiz = ebut->x - box.xmin + 1;
527     }
528     if (box.ymin > ebut->y) {
529     box.ysiz = box.ymin - ebut->y + 1;
530     box.ymin = ebut->y;
531     } else {
532     box.ysiz = ebut->y - box.ymin + 1;
533     }
534     }
535    
536    
537     revbox(x0, y0, x1, y1) /* draw box with reversed lines */
538     int x0, y0, x1, y1;
539     {
540     static GC mygc = 0;
541    
542     if (mygc == 0) {
543     mygc = XCreateGC(thedisplay, wind, 0, 0);
544     XSetFunction(thedisplay, mygc, GXinvert);
545     }
546     XDrawLine(thedisplay, wind, mygc, x0, y0, x1, y0);
547     XDrawLine(thedisplay, wind, mygc, x0, y1, x1, y1);
548     XDrawLine(thedisplay, wind, mygc, x0, y0, x0, y1);
549     XDrawLine(thedisplay, wind, mygc, x1, y0, x1, y1);
550     } /* end of revbox */
551    
552    
553     avgbox(clr) /* average color over current box */
554     COLOR clr;
555     {
556     int left, right, top, bottom;
557     int y;
558     double d;
559     COLOR ctmp;
560     register int x;
561    
562     setcolor(clr, 0.0, 0.0, 0.0);
563     left = box.xmin - xoff;
564     right = left + box.xsiz;
565     if (left < 0)
566     left = 0;
567     if (right > xmax)
568     right = xmax;
569     if (left >= right)
570     return(-1);
571     top = box.ymin - yoff;
572     bottom = top + box.ysiz;
573     if (top < 0)
574     top = 0;
575     if (bottom > ymax)
576     bottom = ymax;
577     if (top >= bottom)
578     return(-1);
579     for (y = top; y < bottom; y++) {
580     if (getscan(y) == -1)
581     return(-1);
582     for (x = left; x < right; x++) {
583     colr_color(ctmp, scanline[x]);
584     addcolor(clr, ctmp);
585     }
586     }
587     d = 1.0/((right-left)*(bottom-top));
588     scalecolor(clr, d);
589     return(0);
590     }
591    
592    
593     getmono() /* get monochrome data */
594     {
595     register unsigned char *dp;
596     register int x, err;
597     int y;
598     rgbpixel *inline;
599     short *cerr;
600    
601     if ((inline = (rgbpixel *)malloc(xmax*sizeof(rgbpixel))) == NULL
602     || (cerr = (short *)calloc(xmax,sizeof(short))) == NULL)
603     quit("out of memory in getmono");
604     dp = ourdata - 1;
605     for (y = 0; y < ymax; y++) {
606     picreadline3(y, inline);
607     err = 0;
608     for (x = 0; x < xmax; x++) {
609     if (!(x&7))
610     *++dp = 0;
611     err += rgb_bright(&inline[x]) + cerr[x];
612     if (err > 127)
613     err -= 255;
614     else
615 greg 1.3 *dp |= 1<<(7-(x&07));
616 greg 1.1 cerr[x] = err >>= 1;
617     }
618     }
619     free((char *)inline);
620     free((char *)cerr);
621     }
622    
623    
624     getfull() /* get full (24-bit) data */
625     {
626     int y;
627    
628     for (y = 0; y < ymax; y++)
629     picreadline3(y, (rgbpixel *)(ourdata+y*xmax*3));
630     }
631    
632    
633     scale_rcolors(xr, sf) /* scale color map */
634     register XRASTER *xr;
635     double sf;
636     {
637     register int i;
638     long maxv;
639    
640     if (xr->pixels == NULL)
641     return;
642    
643     sf = pow(sf, 1.0/gamcor);
644     maxv = 65535/sf;
645    
646     for (i = xr->ncolors; i--; ) {
647     xr->cdefs[i].red = xr->cdefs[i].red > maxv ?
648     65535 :
649     xr->cdefs[i].red * sf;
650     xr->cdefs[i].green = xr->cdefs[i].green > maxv ?
651     65535 :
652     xr->cdefs[i].green * sf;
653     xr->cdefs[i].blue = xr->cdefs[i].blue > maxv ?
654     65535 :
655     xr->cdefs[i].blue * sf;
656     }
657     XStoreColors(thedisplay, xr->cmap, xr->cdefs, xr->ncolors);
658     }
659    
660    
661     getscan(y)
662     int y;
663     {
664     if (y != cury) {
665     if (scanpos == NULL || scanpos[y] == -1)
666     return(-1);
667     if (fseek(fin, scanpos[y], 0) == -1)
668     quit("fseek error");
669     cury = y;
670     } else if (scanpos != NULL)
671     scanpos[y] = ftell(fin);
672    
673     if (freadcolrs(scanline, xmax, fin) < 0)
674     quiterr("read error");
675    
676     cury++;
677     return(0);
678     }
679    
680    
681     picreadline3(y, l3) /* read in 3-byte scanline */
682     int y;
683     register rgbpixel *l3;
684     {
685     register int i;
686     /* read scanline */
687     if (getscan(y) < 0)
688     quiterr("cannot seek for picreadline");
689     /* convert scanline */
690     normcolrs(scanline, xmax, scale);
691     for (i = 0; i < xmax; i++) {
692     l3[i].r = scanline[i][RED];
693     l3[i].g = scanline[i][GRN];
694     l3[i].b = scanline[i][BLU];
695     }
696     }
697    
698    
699     picwriteline(y, l) /* add 8-bit scanline to image */
700     int y;
701     pixel *l;
702     {
703     bcopy((char *)l, (char *)ourdata+y*xmax, xmax);
704     }
705    
706    
707     picreadcm(map) /* do gamma correction */
708     colormap map;
709     {
710     extern double pow();
711     register int i, val;
712    
713     for (i = 0; i < 256; i++) {
714     val = pow(i/256.0, 1.0/gamcor) * 256.0;
715     map[0][i] = map[1][i] = map[2][i] = val;
716     }
717     }
718    
719    
720     picwritecm(map) /* handled elsewhere */
721     colormap map;
722     {
723     #ifdef DEBUG
724     register int i;
725    
726     for (i = 0; i < 256; i++)
727     printf("%d %d %d\n", map[0][i],map[1][i],map[2][i]);
728     #endif
729     }