/**************************************************************************** * xwind.c * * This file contains the XWindows code for the display-as-you-trace feature. * * from Persistence of Vision Raytracer * Copyright 1993 Persistence of Vision Team *--------------------------------------------------------------------------- * NOTICE: This source code file is provided so that users may experiment * with enhancements to POV-Ray and to port the software to platforms other * than those supported by the POV-Ray Team. There are strict rules under * which you are permitted to use this file. The rules are in the file * named POVLEGAL.DOC which should be distributed with this file. If * POVLEGAL.DOC is not available or for more info please contact the POV-Ray * Team Coordinator by leaving a message in CompuServe's Graphics Developer's * Forum. The latest version of POV-Ray may be found there as well. * * This program is based on the popular DKB raytracer version 2.12. * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * *****************************************************************************/ /******************************************************************************/ /* */ /* X Windows code for POV-Ray. */ /* Written by Christopher J. Cason. */ /* CIS 100032,1644 */ /* Internet 100032.1644@compuserve.com */ /* */ /******************************************************************************/ /******************************************************************************/ /* */ /* Original IBM VGA "colour" output routines for MS/DOS by Aaron A. Collins. */ /* */ /* Converted for X Windows and arbitrary #of colours by Christopher J. Cason. */ /* */ /* This will deliver approximate colorings using HSV values for the selection.*/ /* The palette map is divided into 4 parts - upper and lower half generated */ /* with full and half "value" (intensity), respectively. These halves are */ /* further halved by full and half saturation values of each range (pastels). */ /* There are three constant colors, black, white, and grey. They are used */ /* when the saturation is low enough that the hue becomes undefined, and which*/ /* one is selected is based on a simple range map of "value". Usage of the */ /* palette is accomplished by converting the requested color RGB into an HSV */ /* value. If the saturation is too low (< .25) then black, white or grey is */ /* selected. If there is enough saturation to consider looking at the hue, */ /* then the hue range of 1-63 is scaled into one of the 4 palette quadrants */ /* based on its "value" and "saturation" characteristics. */ /* */ /******************************************************************************/ /******************************************************************************/ /* */ /* define X_GETS_ARGS if you want to use X options such as '-display xxx:n.n' */ /* */ /******************************************************************************/ #define DBL double #define NAME "POV-Ray" #include #include #include #include #include #include #include #include "config.h" #define MIN_COLOURS 128 #define MAX_COLOURS 256 #define min(x,y) ((x) < (y) ? (x) : (y)) #define max(x,y) ((x) > (y) ? (x) : (y)) char filename [192] ; /* don't really need it */ unsigned gWidth ; /* +wxxx */ unsigned gHeight ; /* +hyyy */ unsigned gScreenNumber ; /* screen number */ unsigned nColours ; /* colours available */ unsigned quarterPalette ; /* 1/4 of nColours */ unsigned CooperationLevel = 5 ; /* for message loop */ unsigned screenWidth ; /* of root screen */ unsigned screenHeight ; /* ditto */ unsigned long gColours [MAX_COLOURS] ; /* colour lookup */ GC gGc ; /* graphics context */ Screen *gScreen ; /* the X screen */ XImage *gXimage = NULL ; /* to buffer the trace */ Widget gParent ; /* parent widget */ Widget gPopupwindow ; /* trace displayed here */ Window gWindow ; /* child of gPopupWin.. */ Display *gDisplay ; /* X display connection */ Colormap gColourmap ; /* the colourmap */ XtAppContext gAppcontext ; /* application context */ /* in POVRAY.C */ unsigned alt_main (unsigned argc, char *argv []) ; /******************************************************************************/ /* */ /* set a pixel in an XImage */ /* */ /******************************************************************************/ void SetPixel (Display *display, XImage *ximage, Window window, GC gc, unsigned x, unsigned y, unsigned index) { static unsigned lastY = 0 ; if (lastY > y) lastY = y ; if (y != lastY) { /* for efficiency, only display the pixels after a full line is buffered */ XPutImage (display, window, gc, ximage, 0, lastY, 0, lastY, gWidth, gHeight) ; lastY = y ; } XPutPixel (ximage, x, y, gColours [index]) ; } /******************************************************************************/ /* */ /* create an XImage */ /* */ /******************************************************************************/ XImage *CreateXImage (Display *display, Visual *visual, unsigned depth, unsigned width, unsigned height) { unsigned format ; unsigned number_of_bytes ; XImage *ximage ; format = depth == 1 ? XYBitmap : ZPixmap ; ximage = XCreateImage (display, visual, depth, format, 0, NULL, width, height, XBitmapPad (display), 0) ; if (ximage == NULL) return (NULL) ; number_of_bytes = ximage->bytes_per_line * ximage->height ; if ((ximage->data = malloc (number_of_bytes)) == NULL) return (NULL) ; return (ximage) ; } /******************************************************************************/ /* */ /* create an XImage to be compatible with the supplied widget */ /* */ /******************************************************************************/ XImage *CreateXImageFromWidget (Widget widget, unsigned width, unsigned height) { unsigned depth ; XImage *ximage ; Visual *visual ; Display *display ; XtVaGetValues (widget, XtNdepth, &depth, XtNvisual, &visual, NULL) ; display = XtDisplay (widget) ; ximage = CreateXImage (display, visual, depth, width, height) ; return (ximage) ; } /******************************************************************************/ /* */ /* make an XImage given a widget, width and height */ /* */ /******************************************************************************/ XImage *MakeImage (Widget widget, unsigned width, unsigned height) { unsigned x ; unsigned y ; XImage *ximage ; if ((ximage = CreateXImageFromWidget (widget, width, height)) != NULL) { for (x = 0 ; x < width ; x++) for (y = 0 ; y < height ; y++) XPutPixel (ximage, x, y, gColours [0]) ; } return (ximage) ; } /******************************************************************************/ /* */ /* redraw the traced data using the XImage */ /* */ /******************************************************************************/ void processExpose (XExposeEvent *expose) { if (gXimage == NULL || (void *) gWindow == NULL) return ; XPutImage (gDisplay, gWindow, gGc, gXimage, expose->x, expose->y, expose->x, expose->y, expose->width, expose->height) ; } /******************************************************************************/ /* */ /* this is called by the COOPERATE macro from within POVRAY */ /* */ /******************************************************************************/ void XTraceEventHandler (void) { XEvent event ; static unsigned cooperationCounter = 10 ; if (CooperationLevel < 10 && --cooperationCounter) return ; cooperationCounter = 11 - CooperationLevel ; if (CooperationLevel > 5) { /* loop until all messages retrieved */ while (XtAppPending (gAppcontext)) { XtAppNextEvent (gAppcontext, &event) ; if (event.type == Expose && event.xexpose.window == gWindow) { if (event.xexpose.count == 0) processExpose (&event.xexpose) ; return ; } XtDispatchEvent (&event) ; } } else { /* only process 1 event */ if (XtAppPending (gAppcontext)) { XtAppNextEvent (gAppcontext, &event) ; if (event.type == Expose && event.xexpose.window == gWindow) { if (event.xexpose.count == 0) processExpose (&event.xexpose) ; return ; } XtDispatchEvent (&event) ; } } } /******************************************************************************/ /* */ /* called if the user closes the popup window during a trace */ /* */ /******************************************************************************/ void destroyProc (Widget widget) { XDestroyImage (gXimage) ; XFreeGC (gDisplay, gGc) ; gPopupwindow = NULL ; } /******************************************************************************/ /* */ /* create a popup window, centered on the screen */ /* */ /******************************************************************************/ Widget CreatePopupWindow (Widget parent, char *name, unsigned width, unsigned height, void (*destroyProc) (Widget)) { int n ; unsigned x = 0 ; unsigned y = 0 ; Arg args [8] ; Widget window ; n = 0 ; if (width < screenWidth) x = screenWidth / 2 - width / 2 ; if (height < screenHeight) y = screenHeight / 2 - height / 2 ; XtSetArg (args [n], XtNwidth, width) ; n++ ; XtSetArg (args [n], XtNheight, height) ; n++ ; XtSetArg (args [n], XtNx, x) ; n++ ; XtSetArg (args [n], XtNy, y) ; n++ ; window = XtCreatePopupShell (name, topLevelShellWidgetClass, parent, args, n) ; XtAddCallback (window, XtNdestroyCallback, destroyProc, window) ; return (window) ; } /******************************************************************************/ /* */ /* allocate a colour based on the RGB values and place it at index */ /* */ /******************************************************************************/ void set_palette (unsigned index, unsigned red, unsigned green, unsigned blue) { XColor colour ; colour.pixel = gColours [index] ; colour.flags = DoRed | DoGreen | DoBlue ; colour.red = (65535 * red) / 256 ; colour.blue = (65535 * blue) / 256 ; colour.green = (65535 * green) / 256 ; XStoreColor (gDisplay, gColourmap, &colour) ; } /******************************************************************************/ /* */ /* Conversion from Hue, Saturation, Value to Red, Green, and Blue and back */ /* From "Computer Graphics", Donald Hearn & M. Pauline Baker, p. 304 */ /* Extracted from the POV machine specific file IBM.C and modified for X */ /* */ /* See the start of this file for information as to how this code works */ /* */ /******************************************************************************/ void hsv_to_rgb (DBL hue, DBL s, DBL v, unsigned *r, unsigned *g, unsigned *b) { DBL i ; DBL f ; DBL p1 ; DBL p2 ; DBL p3 ; DBL xh ; DBL nr ; DBL ng ; DBL nb ; if (hue == 360.0) hue = 0.0 ; xh = hue / 60.0 ; /* convert hue to be in 0..6 */ i = floor (xh) ; /* i = greatest integer <= h */ f = xh - i ; /* f = fractional part of h */ p1 = v * (1 - s) ; p2 = v * (1 - (s * f)) ; p3 = v * (1 - (s * (1 - f))) ; switch ((int) i) { case 0 : nr = v ; ng = p3 ; nb = p1 ; break ; case 1 : nr = p2 ; ng = v ; nb = p1 ; break ; case 2 : nr = p1 ; ng = v ; nb = p3 ; break ; case 3 : nr = p1 ; ng = p2 ; nb = v ; break ; case 4 : nr = p3 ; ng = p1 ; nb = v ; break ; case 5 : nr = v ; ng = p1 ; nb = p2 ; break ; default : nr = ng = nb = 0 ; } *r = (unsigned) (nr * 255.0) ; *g = (unsigned) (ng * 255.0) ; *b = (unsigned) (nb * 255.0) ; } void rgb_to_hsv (unsigned r, unsigned g, unsigned b, DBL *h, DBL *s, DBL *v) { DBL m ; DBL r1 ; DBL g1 ; DBL b1 ; DBL nr ; DBL ng ; DBL nb ; DBL nh = 0.0 ; DBL ns = 0.0 ; DBL nv ; nr = (DBL) r / 255.0 ; ng = (DBL) g / 255.0 ; nb = (DBL) b / 255.0 ; nv = max (nr, max (ng, nb)) ; m = min (nr, min (ng, nb)) ; if (nv != 0.0) ns = (nv - m) / nv ; if (ns == 0.0) { /* hue undefined if no saturation */ *h = 0.0 ; *s = 0.0 ; *v = nv ; return ; } r1 = (nv - nr) / (nv - m) ; /* distance of color from red */ g1 = (nv - ng) / (nv - m) ; /* distance of color from green */ b1 = (nv - nb) / (nv - m) ; /* distance of color from blue */ if (nv == nr) { if (m == ng) nh = 5.0 + b1 ; else nh = 1.0 - g1 ; } if (nv == ng) { if (m == nb) nh = 1.0 + r1 ; else nh = 3.0 - b1 ; } if (nv == nb) { if (m == nr) nh = 3.0 + g1 ; else nh = 5.0 - r1 ; } *h = nh * 60.0 ; /* return h converted to degrees */ *s = ns ; *v = nv ; } /******************************************************************************/ /* */ /* set up the X palette */ /* try to allocate at least as many colours as MIN_COLOURS */ /* exit with an error if we cannot get at least this many */ /* divide the available colour up into four quadrants, and divide each */ /* quadrant up into specific hues from 0 to 359 degrees */ /* */ /******************************************************************************/ void palette_init (void) { unsigned m ; unsigned r ; unsigned g ; unsigned b ; DBL hue ; DBL sat ; DBL val ; for (nColours = MAX_COLOURS ; nColours >= MIN_COLOURS ; nColours--) if (XAllocColorCells (gDisplay, gColourmap, False, NULL, 0, gColours, nColours)) break ; if (nColours < MIN_COLOURS) { printf ("failed to allocate colour cells needed for display\r\n") ; exit (1) ; } quarterPalette = nColours / 4 ; /* for the first quarter of the palette ... */ for (m = 1 ; m < quarterPalette ; m++) { /* normalise to 360 */ hue = 360.0 * ((DBL) (m)) / (DBL) quarterPalette ; hsv_to_rgb (hue, 0.5, 0.5, &r, &g, &b) ; set_palette (m, r, g, b) ; hue = 360.0 * ((DBL) (m)) / (DBL) quarterPalette ; hsv_to_rgb (hue, 1.0, 0.5, &r, &g, &b) ; set_palette (m + quarterPalette, r, g, b) ; hue = 360.0 * ((DBL) (m)) / (DBL) quarterPalette ; hsv_to_rgb (hue, 0.5, 1.0, &r, &g, &b) ; set_palette (m + quarterPalette * 2, r, g, b) ; hue = 360.0 * ((DBL) (m)) / (DBL) quarterPalette ; hsv_to_rgb (hue, 1.0, 1.0, &r, &g, &b) ; set_palette (m + quarterPalette * 3, r, g, b) ; } set_palette (0, 0, 0, 0) ; /* black */ set_palette (quarterPalette, 255, 255, 255) ; /* white */ set_palette (quarterPalette * 2, 128, 128, 128) ; /* dark grey */ set_palette (quarterPalette * 3, 192, 192, 192) ; /* light grey */ } /******************************************************************************/ /* */ /* display code called directly by POV-Ray via [machine-name].C */ /* */ /******************************************************************************/ void x_display_finished () { } void x_display_init (unsigned width, unsigned height) { unsigned n ; unsigned y = 0 ; unsigned x = 0 ; unsigned long white ; unsigned long black ; Arg args [20] ; gDisplay = XtDisplay (gParent) ; gScreen = XtScreen (gParent) ; gScreenNumber = XScreenNumberOfScreen (gScreen) ; gColourmap = DefaultColormapOfScreen (gScreen) ; screenWidth = DisplayWidth (gDisplay, gScreenNumber) ; screenHeight = DisplayHeight (gDisplay, gScreenNumber) ; white = WhitePixel (gDisplay, gScreenNumber) ; black = BlackPixel (gDisplay, gScreenNumber) ; n = 0 ; x = screenWidth / 2 - gWidth / 2 ; y = screenHeight / 2 - gHeight / 2 ; XtSetArg (args [n], XtNx, x) ; n++ ; XtSetArg (args [n], XtNy, y) ; n++ ; XtSetValues (gParent, args, n) ; gWidth = width ; gHeight = height ; gPopupwindow = CreatePopupWindow (gParent, NAME, width, height, destroyProc) ; XtRealizeWidget (gPopupwindow) ; XtMapWidget (gPopupwindow) ; gWindow = XCreateSimpleWindow (gDisplay, XtWindow (gPopupwindow), 0, 0, width, height, 0, white, black) ; XSetWindowBackground (gDisplay, gWindow, black) ; XMapWindow (gDisplay, gWindow) ; XtPopup (gPopupwindow, XtGrabNonexclusive) ; palette_init () ; gGc = XCreateGC (gDisplay, gWindow, 0L, NULL) ; gXimage = MakeImage (gPopupwindow, width, height) ; /* draw a rectangle into the XImage to show trace progress */ for (x = 0 ; x < width ; x++) { XPutPixel (gXimage, x, 0, white) ; XPutPixel (gXimage, x, height - 1, white) ; } for (y = 0 ; y < height ; y++) { XPutPixel (gXimage, 0, y, white) ; XPutPixel (gXimage, width - 1, y, white) ; } XSelectInput (gDisplay, gWindow, ExposureMask) ; } void x_display_close () { if (gPopupwindow) XtDestroyWidget (gPopupwindow) ; } void x_display_plot (unsigned x, unsigned y, char Red, char Green, char Blue) { unsigned colour ; DBL h ; DBL s ; DBL v ; if (gPopupwindow == NULL) return ; rgb_to_hsv ((unsigned) Red, (unsigned) Green, (unsigned) Blue, &h, &s, &v) ; if (s < 0.20) /* black or white if no saturation of colour ... */ { if (v < 0.25) colour = 0 ; /* black */ else if (v > 0.8) colour = quarterPalette ; /* white */ else if (v > 0.5) colour = quarterPalette * 3 ; /* light grey */ else colour = quarterPalette * 2 ; /* dark grey */ } else { colour = (unsigned) ((DBL) quarterPalette * h / 360.0) ; if (colour == 0) colour = 1 ; /* avoid black, white or grey */ if (colour > quarterPalette) colour = quarterPalette ; /* avoid same */ if (v > 0.50) colour += quarterPalette * 2 ; /* colours 128-255 for high intensity */ if (s > 0.50) /* more than half saturated ? */ colour += quarterPalette ; /* colour range 64-128 or 192-255 */ } /* only displays after a full line is received (the Y co-ordinate changes) */ SetPixel (gDisplay, gXimage, gWindow, gGc, x, y, colour) ; } /******************************************************************************/ /* */ /* our main routine. when XWIND is linked in this is main () for POV-Ray */ /* */ /******************************************************************************/ int main (unsigned argc, char *argv []) { unsigned n ; unsigned count = 0 ; Arg args [20] ; strncpy (filename, argv [0], sizeof (filename)) ; n = 0 ; XtSetArg (args [n], XtNmappedWhenManaged, False) ; n++ ; XtSetArg (args [n], XtNallowShellResize, False) ; n++ ; XtSetArg (args [n], XtNwidth, 10) ; n++ ; XtSetArg (args [n], XtNheight, 10) ; n++ ; /* define X_GETS_ARGS if you want to use X options such as '-display xxx:n.n' */ #ifdef X_GETS_ARGS gParent = XtAppInitialize (&gAppcontext, NAME, NULL, 0, &argc, argv, NULL, args, n) ; #else gParent = XtAppInitialize (&gAppcontext, NAME, NULL, 0, &count, NULL, NULL, args, n) ; #endif /* call POV-Ray ! */ alt_main (argc, argv) ; return (0) ; }