#include "coxplot.h"
/*****************************************************************************
This software is copyrighted and owned by the Medical College of Wisconsin.
See the file README.Copyright for details.
******************************************************************************/
/*--------------------------------------------------------------------------
Routines to render a memplot into an X11 window.
----------------------------------------------------------------------------*/
static Display * old_dpy = NULL ;
static X11_colordef * old_cd = NULL ;
static Window old_w = (Window) 0 ;
static GC old_GC ;
/*--------------------------------------------------------------------------
If we have a new X11 display, will get its coloring scheme.
Note the tacit assumption that all windows on the same
display in the same program will use the same visual and colormap!
----------------------------------------------------------------------------*/
static void setup_X11_plotting( Display * dpy , Window w )
{
XGCValues gcv;
if( old_dpy == dpy ){ old_w = w ; return ; }
FREE_X11_colordef(old_cd) ;
old_cd = get_X11_colordef( dpy , w ) ;
if( old_dpy != NULL ) XFreeGC( old_dpy , old_GC ) ;
gcv.function = GXcopy ;
gcv.fill_style = FillSolid ; /* 21 Mar 2001 */
old_GC = XCreateGC( dpy , w , GCFunction|GCFillStyle , &gcv ) ;
old_dpy = dpy ; old_w = getwin_from_XDBE(dpy,w) ;
return ;
}
/*---------------------------------------------------------------------------
Set the background color of a window. This is necessary since
neither memplot or plotpak contains the concept of a background color,
only the concept of the line drawing color.
-----------------------------------------------------------------------------*/
void set_X11_background( Display * dpy , Window w ,
unsigned char rr , unsigned char gg , unsigned char bb )
{
unsigned long pix ;
if( dpy == NULL || w == (Window) 0 ) return ;
setup_X11_plotting( dpy , w ) ;
pix = rgb_to_pixel( rr,gg,bb , old_cd ) ;
XSetWindowBackground( dpy , getwin_from_XDBE(dpy,w) , pix ) ;
return ;
}
/*--------------------------------------------------------------------------
Set a sub-box within a window into which the next X11 plot should
be scaled. (0,0,0,0) args means use the whole window. After
each drawing (memplot_to_X11_sef), will be reset to the whole window
anyway. -- 26 Feb 2001 -- RWCox
----------------------------------------------------------------------------*/
static int box_xbot=0 , box_xtop=0 ,
box_ybot=0 , box_ytop=0 ;
void set_memplot_X11_box( int xbot, int ybot, int xtop, int ytop )
{
if( xbot < xtop && ybot < ytop ){
box_xbot = xbot ; box_ybot = ybot ;
box_xtop = xtop ; box_ytop = ytop ;
} else {
box_xbot = box_ybot = box_xtop = box_ytop = 0 ;
}
}
/*------------------------------------------------------------------------*/
static int rectfill = 0 ;
void set_memplot_X11_rectfill( int i ){ rectfill = i ; } /* 26 Oct 2005 */
/*------------------------------------------------------------------------*/
/*! Get the layout of a window/pixmap. [12 Mar 2002]
--------------------------------------------------------------------------*/
static void drawable_geom( Display *dpy , Drawable ddd ,
int *width , int *height , int *depth )
{
int xx,yy ;
unsigned int ww,hh,bb,dd ;
Window rr ;
XGetGeometry( dpy,ddd , &rr,&xx,&yy,&ww,&hh,&bb,&dd ) ;
if( width != NULL ) *width = ww ;
if( height != NULL ) *height = hh ;
if( depth != NULL ) *depth = dd ;
}
/*--------------------------------------------------------------------------
Actually do the rendering.
Plotting will start with line #start and go to #end-1.
If end <= start, will do from #start to the last one in the plot.
To do all lines, set start=end=0.
The mask argument controls operations, and is the bitwise sum of
these possibilities:
MEMPLOT_FREE_ASPECT = allow the aspect ratio to be free
MEMPLOT_ERASE = draw white over the lines
----------------------------------------------------------------------------*/
#ifdef LMAX
#undef LMAX
#endif
#define LMAX 1023 /* max number of line segments to draw at once */
static XSegment xseg[LMAX] ; /* line segment drawing buffer */
static int nseg = 0 ; /* number in the buffer */
static void draw_xseg(void) ; /* prototype for function below */
void memplot_to_X11_sef( Display * dpy , Window w , MEM_plotdata * mp ,
int start , int end , int mask )
{
int ii , nline , same ;
float old_thick , old_color , new_color , new_thick ;
float scal,xscal,yscal , xoff,yoff ;
short x1,y1 , x2,y2 ; /* X11 screen coords are shorts */
int skip ;
int w_width, w_height, w_depth ; /* 12 Mar 2002 */
XGCValues gcv ;
int freee = (mask & MEMPLOT_FREE_ASPECT) != 0 ; /* 16 Nov 2001 */
int erase = (mask & MEMPLOT_ERASE ) != 0 ;
/*--- check for madness ---*/
if( dpy == NULL || w == (Window) 0 || mp == NULL ) return ;
if( start < 0 ) start = 0 ;
nline = MEMPLOT_NLINE(mp) ;
if( nline < 1 || start >= nline ) return ;
if( end <= start || end > nline ) end = nline ;
/*-- if we have a new X11 Display, get its coloring
(note the tacit assumption that all windows on the same
display in the same program will use the same visual and colormap!) --*/
setup_X11_plotting( dpy , w ) ;
/*-- 12 Mar 2002: replace use of XGetWindowAttributes with XGetGeometry --*/
drawable_geom( dpy, getwin_from_XDBE(dpy,w), &w_width,&w_height,&w_depth ) ;
if( w_depth != old_cd->depth ) return ; /* this is bad */
/*--- compute scaling from memplot objective
coordinates to X11 window coordinates, maintaining aspect ---*/
if( box_xbot >= box_xtop || box_ybot >= box_ytop ){
xscal = (w_width -0.001) / mp->aspect ; /* aspect = x-axis objective size */
yscal = (w_height-0.001) / 1.0 ; /* 1.0 = y-axis objective size */
xoff = yoff = 0.0 ;
} else { /* 26 Feb 2001: scale to a given sub-box in the window */
xscal = box_xtop - box_xbot ;
yscal = box_ytop - box_ybot ;
xoff = box_xbot + 0.0 ;
yoff = box_ybot + 0.0 ;
}
if( !freee ){ /* no aspect freedom ==> */
if( yscal < xscal ) xscal = yscal ; /* use smaller scaling */
else yscal = xscal ;
}
scal = sqrt(fabs(xscal*yscal)) ;
old_color = -1.0 ; /* these don't occur naturally */
old_thick = -THCODE_INVALID ;
if( erase ){ /* 16 Nov 2001: erase to white */
float rr=1.0 , gg=1.0 , bb=1.0 ;
unsigned long pix ;
pix = rgb_to_pixel( ZO_TO_TFS(rr), ZO_TO_TFS(gg), ZO_TO_TFS(bb), old_cd ) ;
XSetForeground( old_dpy , old_GC , pix ) ;
}
/* 23 Feb 2003: initialize line width to 0 for each entry
(in case a special case [box,circle, ...] comes first */
gcv.line_width = 0 ;
gcv.join_style = JoinBevel ;
XChangeGC( old_dpy , old_GC , GCLineWidth | GCJoinStyle , &gcv ) ;
/*--- loop over lines, scale and plot ---*/
for( ii=start ; ii < end ; ii++ ){
skip = 0 ;
/* check if need to change color or thickness of line */
new_color = MEMPLOT_COL(mp,ii) ;
if( !erase && new_color != old_color ){
float rr=COL_TO_RRR(new_color) ,
gg=COL_TO_GGG(new_color) , bb=COL_TO_BBB(new_color) ;
unsigned long pix ;
#if 0
fprintf(stderr,"Changing color to %f %f %f\n",rr,gg,bb) ;
#endif
draw_xseg() ; /* must draw before changing GC */
pix = rgb_to_pixel( ZO_TO_TFS(rr), ZO_TO_TFS(gg), ZO_TO_TFS(bb), old_cd ) ;
XSetForeground( old_dpy , old_GC , pix ) ;
old_color = new_color ;
}
new_thick = MEMPLOT_TH(mp,ii) ;
if( new_thick < 0.0 ){ /* 21 Mar 2001: negative thickness codes */
int thc = (int)(-new_thick) ;
switch( thc ){ /* default is to do nothing (e.g., thd = THCODE_INVALID) */
case THCODE_RECT:{ /* rectangle */
short xb,yb , xt,yt ;
unsigned short w,h ;
x1 = (short)( xoff + xscal * MEMPLOT_X1(mp,ii) ) ;
x2 = (short)( xoff + xscal * MEMPLOT_X2(mp,ii) ) ;
y1 = (short)( yoff + yscal * (1.0 - MEMPLOT_Y1(mp,ii)) ) ;
y2 = (short)( yoff + yscal * (1.0 - MEMPLOT_Y2(mp,ii)) ) ;
if( x1 < x2 ){ xb=x1; xt=x2; } else { xb=x2; xt=x1; }
if( y1 < y2 ){ yb=y1; yt=y2; } else { yb=y2; yt=y1; }
w = xt-xb ; h = yt-yb ;
if( w || h ){
if( rectfill )
XFillRectangle( old_dpy,old_w,old_GC , xb,yb,w,h ) ;
else
XDrawRectangle( old_dpy,old_w,old_GC , xb,yb,w,h ) ;
} else
XDrawPoint( old_dpy,old_w,old_GC , xb,yb ) ;
skip = 1 ;
}
break ;
case THCODE_CIRC:{ /* circle */
int xcor,ycor , xcen,ycen , xrad,yrad ;
unsigned int ww, hh ;
xcen = (int)(xoff + xscal * MEMPLOT_X1(mp,ii) );
ycen = (int)(yoff + yscal * (1.0 - MEMPLOT_Y1(mp,ii)) );
xrad = (int)( xscal * MEMPLOT_X2(mp,ii) );
yrad = (int)( yscal * MEMPLOT_X2(mp,ii) );
xcor = xcen - xrad ; ww = 2*xrad ;
ycor = ycen - yrad ; hh = 2*yrad ;
if( ww || hh )
XDrawArc( old_dpy,old_w,old_GC , xcor,ycor,ww,hh , 0,360*64 ) ;
else
XDrawPoint( old_dpy,old_w,old_GC , xcor,ycor ) ;
skip = 1 ;
}
break ;
}
} else if( new_thick != old_thick ){ /* normal case: change line thickness */
int lw = scal * new_thick ;
if( lw < 0 ) lw = 0 ;
#if 0
fprintf(stderr,"Changing thickness: old=%f new=%f\n",old_thick,new_thick) ;
#endif
draw_xseg() ; /* must draw before changing GC */
gcv.line_width = lw ;
gcv.join_style = JoinBevel ;
XChangeGC( old_dpy , old_GC , GCLineWidth | GCJoinStyle , &gcv ) ;
old_thick = new_thick ;
}
if( nseg == LMAX ) draw_xseg() ; /* draw if list is full */
/* scale coords to X11 shorts (also see zzphph.f) */
/* 26 Feb 2001: xoff,yoff are now variables, instead of 0.499 */
if( !skip ){
x1 = (short)( xoff + xscal * MEMPLOT_X1(mp,ii) ) ;
x2 = (short)( xoff + xscal * MEMPLOT_X2(mp,ii) ) ;
y1 = (short)( yoff + yscal * (1.0 - MEMPLOT_Y1(mp,ii)) ) ;
y2 = (short)( yoff + yscal * (1.0 - MEMPLOT_Y2(mp,ii)) ) ;
/* add to segment list */
xseg[nseg].x1 = x1 ; xseg[nseg].y1 = y1 ;
xseg[nseg].x2 = x2 ; xseg[nseg].y2 = y2 ; nseg++ ;
}
}
/*-- process any segments left over --*/
draw_xseg() ;
set_memplot_X11_box(0,0,0,0) ; /* 26 Feb 2001: clear box */
return ;
}
/*---------------- draw the line segments stored in xseg -----------------*/
static void draw_xseg(void)
{
int jbot,jtop , ii,nj ;
XPoint xpt[LMAX+1] ;
if( nseg <= 0 ) return ; /* nothing to do */
#if 0
fprintf(stderr,"draw_xseg: %d segments input.\n",nseg) ;
for( ii=0 ; ii < nseg ; ii++ )
fprintf(stderr," %4d: x1=%4d y1=%4d x2=%4d y2=%4d\n",
ii , xseg[ii].x1 , xseg[ii].y1 , xseg[ii].x2 , xseg[ii].y2 ) ;
#endif
jbot = 0 ;
while( jbot < nseg ){ /* scan the line segment list starting at jbot */
/* scan forward to find a set of connected lines */
jtop = jbot+1 ;
while( jtop < nseg ){ /* 23 Feb 2003: more complex connection tests */
if( xseg[jtop-1].x2 == xseg[jtop].x1
&& xseg[jtop-1].y2 == xseg[jtop].y1 ){ jtop++; continue; } /* OK */
if( xseg[jtop-1].x2 == xseg[jtop].x2
&& xseg[jtop-1].y2 == xseg[jtop].y2 ){ /* OK if flipped */
ii = xseg[jtop].x2; xseg[jtop].x2 = xseg[jtop].x1; xseg[jtop].x1 = ii;
ii = xseg[jtop].y2; xseg[jtop].y2 = xseg[jtop].y1; xseg[jtop].y1 = ii;
jtop++; continue;
}
break ; /* get to here ==> jtop-1 and jtop segments not connected */
}
/* jbot .. jtop-1 are connected;
if this is more than one line, draw them together
so that the X11 server will properly join the lines */
nj = jtop - jbot ;
if( nj > 1 ){
xpt[0].x = xseg[jbot].x1 ; xpt[0].y = xseg[jbot].y1 ;
for( ii=0 ; ii < nj ; ii++ ){
xpt[ii+1].x = xseg[jbot+ii].x2 ; xpt[ii+1].y = xseg[jbot+ii].y2 ;
}
XDrawLines( old_dpy,old_w,old_GC , xpt,nj+1 , CoordModeOrigin ) ;
#if 0
fprintf(stderr,"draw_xseg: XDrawLines for %d\n",nj) ;
#endif
jbot = jtop ; continue ; /* start the while loop over */
}
/* jbot is not connected to jbot+1;
scan forward to find a set of disconnected lines */
while( jtop < nseg &&
( xseg[jtop-1].x2 != xseg[jtop].x1 ||
xseg[jtop-1].y2 != xseg[jtop].y1 ) &&
( xseg[jtop-1].x2 != xseg[jtop].x2 ||
xseg[jtop-1].y2 != xseg[jtop].y2 ) ) jtop++ ;
/* jbot .. jtop-1 are disconnected, so draw them as such */
XDrawSegments( old_dpy,old_w,old_GC , xseg+jbot , jtop-jbot ) ;
#if 0
fprintf(stderr,"draw_xseg: XDrawSegments for %d\n",jtop-jbot) ;
#endif
jbot = jtop ; continue ; /* start the while loop over */
}
nseg = 0 ; return ;
}
/*------------------------------------------------------------------------
Returns position of highest set bit in 'ul' as an integer (0-31),
or returns -1 if no bit is set.
--------------------------------------------------------------------------*/
static int highbit(unsigned long ul)
{
int i; unsigned long hb;
hb = 0x80; hb = hb << 24; /* hb = 0x80000000UL */
for (i=31; ((ul & hb) == 0) && i>=0; i--, ul<<=1);
return i;
}
/*-------------------------------------------------------------------------
Return an X11 pixel value that represents the given color.
Inputs rr,gg,bb are from 0 to 255. Output is an unsigned long,
the standard X11 format for specifying colors to a GC.
Note that if the result is to be put into an XImage, further
bit manipulation must be done on the result.
---------------------------------------------------------------------------*/
unsigned long rgb_to_pixel( unsigned char rr , unsigned char gg ,
unsigned char bb , X11_colordef * cd )
{
/*--- TrueColor case: make color by appropriate bit twiddling ---*/
if( cd->classKRH == TrueColor ){
unsigned long r , g , b , rgb ;
r = (cd->rrshift<0) ? (rr<<(-cd->rrshift))
: (rr>>cd->rrshift) ; r = r & cd->rrmask ;
g = (cd->ggshift<0) ? (gg<<(-cd->ggshift))
: (gg>>cd->ggshift) ; g = g & cd->ggmask ;
b = (cd->bbshift<0) ? (bb<<(-cd->bbshift))
: (bb>>cd->bbshift) ; b = b & cd->bbmask ;
rgb = r | g | b ; /* assemble color from components */
return rgb ;
}
/*--- PseudoColor case: find closest match in colormap.
Red, green, and blue are weighted according
to their importance to the human visual system. ---*/
#define RW 2 /* the weights alluded to above */
#define GW 4
#define BW 1
if( cd->classKRH == PseudoColor ){
int ii , rdif,gdif,bdif,dif , ibest,dbest ;
rdif = cd->rr[0] - rr ;
gdif = cd->gg[0] - gg ;
bdif = cd->bb[0] - bb ; dif = RW*abs(rdif)+GW*abs(gdif)+BW*abs(bdif) ;
if( dif == 0 ) return 0 ;
ibest = 0 ; dbest = dif ;
for( ii=1 ; ii < cd->ncolors ; ii++ ){
rdif = cd->rr[ii] - rr ;
gdif = cd->gg[ii] - gg ;
bdif = cd->bb[ii] - bb ; dif = RW*abs(rdif)+GW*abs(gdif)+BW*abs(bdif) ;
if( dif == 0 ) return ii ;
if( dif < dbest ){ ibest = ii ; dbest = dif ; }
}
return ibest ;
}
/*--- Illegal case! ---*/
return 0 ; /* always valid */
}
/*-------------------------------------------------------------------------*/
static volatile int xwasbad ; /* 13 Mar 2002 */
typedef int (*xhandler)(Display *, XErrorEvent *) ;
static int qhandler( Display *dpy , XErrorEvent *xev )
{
xwasbad = 1 ; return 0 ;
}
/*-------------------------------------------------------------------------
Return a structure that defines the colors available for the given
window. This only works for PseudoColor and TrueColor visuals.
Returns NULL if an error occurs.
---------------------------------------------------------------------------*/
X11_colordef * get_X11_colordef( Display * display , Window w )
{
Status sss ;
XWindowAttributes xwat ;
XColor * xcol ;
XVisualInfo vinfo , * vin ;
X11_colordef * cd ; /* will be the output */
int count , ii ;
xhandler old_handler ; /* 13 Mar 2002 */
/*--- sanity check ---*/
if( display == NULL || w == (Window) 0 ) return NULL ;
/*--- get window attributes ---*/
/* 13 Mar 2002: deal with the error that occurs
when the Window is really a Pixmap */
xwat.depth = 0 ;
old_handler = XSetErrorHandler(qhandler) ; xwasbad = 0 ;
XGetWindowAttributes( display, getwin_from_XDBE(display,w), &xwat ) ;
(void) XSetErrorHandler(old_handler) ;
if( xwasbad ){
int xx,yy ; unsigned int ww,hh,bb,dd ; Window rr ;
XGetGeometry( display,w , &rr,&xx,&yy,&ww,&hh,&bb,&dd ) ;
XGetWindowAttributes( display, rr , &xwat ) ;
}
if( xwat.depth == 0 ) return NULL ; /* bad news */
/*--- get information about the window's Visual ---*/
vinfo.visualid = XVisualIDFromVisual(xwat.visual) ;
vin = XGetVisualInfo( display , VisualIDMask , &vinfo , &count ) ;
if( count == 0 || vin == NULL ) return NULL ;
/*--- PseudoColor case ---*/
#if defined(__cplusplus) || defined(c_plusplus)
if( vin->c_class == PseudoColor ){
#else
if( vin->class == PseudoColor ){
#endif
int iz ;
/* create output */
cd = (X11_colordef *) malloc( sizeof(X11_colordef) ) ;
cd->classKRH = PseudoColor ;
cd->depth = vin->depth ;
/* get all the colors in the colormap */
count = vin->colormap_size ;
xcol = (XColor *) malloc( sizeof(XColor) * count ) ;
for( ii=0 ; ii < count ; ii++ ) xcol[ii].pixel = ii ;
XQueryColors( display , xwat.colormap , xcol , count ) ;
/* store them in the output, truncated to 8 bits */
cd->ncolors = count ;
cd->rr = (unsigned char *) malloc( count ) ;
cd->gg = (unsigned char *) malloc( count ) ;
cd->bb = (unsigned char *) malloc( count ) ;
for( ii=0 ; ii < count ; ii++ ){
cd->rr[ii] = xcol[ii].red >> 8 ;
cd->gg[ii] = xcol[ii].green >> 8 ;
cd->bb[ii] = xcol[ii].blue >> 8 ;
}
/* find first all zero color; discard others at end of colormap */
for( iz=0 ; iz < count ; iz++ )
if( cd->rr[iz] == 0 && cd->gg[iz] == 0 && cd->bb[iz] == 0 ) break ;
if( iz < count-1 ){ /* if found one before the end */
for( ii=count-1 ; ii > iz ; ii-- ) /* scan backwards */
if( cd->rr[ii] != 0 || cd->gg[ii] != 0 || cd->bb[ii] != 0 ) break ;
count = ii+1 ; /* number of colors left */
if( count == 1 ){ /* colormap is all black?! */
free(xcol) ; XFree(vin) ; FREE_X11_colordef(cd) ; return NULL ;
}
cd->ncolors = count ;
}
free(xcol) ; XFree(vin) ; return cd ;
}
/*--- TrueColor case ---*/
#if defined(__cplusplus) || defined(c_plusplus)
if( vin->c_class == TrueColor ){
#else
if( vin->class == TrueColor ){
#endif
/* create output */
cd = (X11_colordef *) malloc( sizeof(X11_colordef) ) ;
cd->classKRH = TrueColor ;
cd->depth = vin->depth ;
cd->rrmask = vin->red_mask ; /* bit masks for color */
cd->ggmask = vin->green_mask ; /* storage inside pixel */
cd->bbmask = vin->blue_mask ;
cd->rrshift = 7 - highbit(cd->rrmask) ; /* shift puts high bit of */
cd->ggshift = 7 - highbit(cd->ggmask) ; /* a color byte into place */
cd->bbshift = 7 - highbit(cd->bbmask) ; /* +shift == >> ; - == << */
cd->rr = cd->gg = cd->bb = NULL ; /* not used */
XFree(vin) ; return cd ;
}
/*--- Illegal Visual class! ---*/
XFree(vin) ; return NULL ;
}
#ifdef HAVE_XDBE
/*---------------------------------- 24 Jan 1999 -------------------------------*/
void init_XDBE( Display * dpy )
{
int sss , ii , jj ;
char * ec ;
if( use_xdbe >= 0 ) return ;
ec = getenv("AFNI_NO_XDBE") ; /* 28 Jan 2000 - add this environment variable */
if( ec != NULL && (ec[0]=='Y' || ec[0]=='y') ){
use_xdbe = 0 ;
} else {
sss = (int) XdbeQueryExtension( dpy , &ii , &jj ) ;
use_xdbe = (sss != 0 ) ;
}
return ;
}
Window getwin_from_XDBE( Display * dpy , Drawable w )
{
XdbeBackBufferAttributes * bat ;
Window bw ;
if( w == (Window) 0 || use_xdbe <= 0 ) return w ;
bat = XdbeGetBackBufferAttributes( dpy , w ) ;
bw = bat->window ; XFree(bat) ;
if( bw == (Window) 0 ) bw = w ;
return bw ;
}
#endif /* HAVE_XDBE */
syntax highlighted by Code2HTML, v. 0.9.1