1 #include "mrilib.h"
2 #include "coxplot.h"
3
4 #undef DEBUG
5
6 /*--------------------------------------------------------------------------
7 Routines to render a memplot into an MRI_rgb image.
8 The assumption is that the plot is over the "object" region [0,1]x[0,1],
9 which is rendered into the actual image [0..nx-1]x[0..ny-1].
10 The actual rendering is done by functions in mri_drawing.c,
11 which were lifted from the PPM library, with some light edits.
12 ----------------------------------------------------------------------------*/
13
14 /*--------------------------------------------------------------------------*/
15 /*! Set a sub-box within a window into which the next RGB plot should
16 be scaled. (0,0,0,0) args means use the whole window. After
17 each drawing (memplot_to_RGB_sef), will be reset to the whole window
18 anyway.
19 ----------------------------------------------------------------------------*/
20
21 static int box_xbot=0 , box_xtop=0 ,
22 box_ybot=0 , box_ytop=0 ;
23
24 static int do_thick=0 ;
memplot_to_mri_set_dothick(int dt)25 void memplot_to_mri_set_dothick( int dt ){ do_thick = dt ; }
26 static int do_freee=0 ;
memplot_to_mri_set_dofreee(int df)27 void memplot_to_mri_set_dofreee( int df ){ do_freee = df ; }
28
set_memplot_RGB_box(int xbot,int ybot,int xtop,int ytop)29 void set_memplot_RGB_box( int xbot, int ybot, int xtop, int ytop )
30 {
31 if( xbot < xtop && ybot < ytop ){
32 box_xbot = xbot ; box_ybot = ybot ;
33 box_xtop = xtop ; box_ytop = ytop ;
34 } else {
35 box_xbot = box_ybot = box_xtop = box_ytop = 0 ;
36 }
37 }
38
39 /*--------------------------------------------------------------------------*/
40 /*! Actually do the rendering of a memplot into an existing RGB image.
41 - That is, we are drawing on top of whatever is there already.
42 - Plotting will start with line #start and go to #end-1.
43 - If end <= start, will do from #start to the last one in the plot.
44 - To do all lines, set start=end=0.
45 - "freee" controls whether the aspect ratio will be free to vary (!= 0),
46 or will be fixed (==0).
47 - 18 Sep 2001: adapted from X11 routines in coxplot/plot_x11.c
48 - 23 Mar 2002: actually tested for the first time
49 ----------------------------------------------------------------------------*/
50
memplot_to_RGB_sef(MRI_IMAGE * im,MEM_plotdata * mp,int start,int end,int freee)51 void memplot_to_RGB_sef( MRI_IMAGE *im , MEM_plotdata *mp ,
52 int start , int end , int freee )
53 {
54 byte rrr=0,ggg=0,bbb=0 ;
55 int ii , nline , same ;
56 float old_thick , old_color , new_color , new_thick , sthick=0.0f ;
57 float scal,xscal,yscal , xoff,yoff ;
58 int x1,y1 , x2,y2 ;
59 int x1_old=-666,y1_old=-666 , x2_old=-666,y2_old=-666 ; float sthick_old=-666.f ;
60 int skip ;
61
62 ENTRY("memplot_to_RGB_sef") ;
63
64 /*--- check for madness ---*/
65
66 if( im == NULL || im->kind != MRI_rgb || mp == NULL ) EXRETURN ;
67
68 if( start < 0 ) start = 0 ;
69
70 nline = MEMPLOT_NLINE(mp) ;
71 if( nline < 1 || start >= nline ) EXRETURN ;
72
73 if( end <= start || end > nline ) end = nline ;
74
75 /*--- compute scaling from memplot objective
76 coordinates to RGB window coordinates ---*/
77
78 if( box_xbot >= box_xtop || box_ybot >= box_ytop ){
79
80 xscal = im->nx / mp->aspect ; /* aspect = x-axis objective size */
81 yscal = im->ny / 1.0f ; /* 1.0 = y-axis objective size */
82 xoff = yoff = 0.499f ;
83
84 } else { /* scale to a given sub-box in the window */
85
86 xscal = box_xtop - box_xbot ;
87 yscal = box_ytop - box_ybot ;
88 xoff = box_xbot + 0.499f ;
89 yoff = box_ybot + 0.499f ;
90 }
91
92 if( !freee && !do_freee ){ /* no aspect freedom ==> */
93 if( yscal < xscal ) xscal = yscal ; /* use smaller scaling */
94 else yscal = xscal ;
95 }
96 scal = sqrt(fabs(xscal*yscal)) ;
97
98 old_color = -1.0f ; /* these don't occur naturally */
99 old_thick = -THCODE_INVALID ;
100
101 /*--- loop over lines, scale and plot ---*/
102
103 mri_draw_opacity( 1.0f ) ;
104
105 #ifdef DEBUG
106 INFO_message("memplot_to_RGB_sef from %d to %d",start,end) ;
107 #endif
108
109 for( ii=start ; ii < end ; ii++ ){
110
111 skip = 0 ;
112
113 /* check if need to change color or thickness of line */
114
115 new_color = MEMPLOT_COL(mp,ii) ;
116 if( new_color != old_color ){
117 float rr=COL_TO_RRR(new_color) ,
118 gg=COL_TO_GGG(new_color) , bb=COL_TO_BBB(new_color) ;
119
120 rrr = ZO_TO_TFS(rr) ; ggg = ZO_TO_TFS(gg) ; bbb = ZO_TO_TFS(bb) ;
121 old_color = new_color ;
122 #ifdef DEBUG
123 ININFO_message("Changing color to %f->%d %f->%d %f->%d\n",rr,(int)rrr,gg,(int)ggg,bb,(int)bbb) ;
124 #endif
125 }
126
127 new_thick = MEMPLOT_TH(mp,ii) ;
128 if( new_thick < 0.0 ){ /* special negative thickness codes */
129 int thc = (int)(-new_thick) ; /* mean special drawing actions */
130 switch( thc ){
131 case THCODE_FRECT:
132 case THCODE_RECT:{ /* rectangle */
133 int xb,yb , xt,yt ;
134 int w,h ;
135 x1 = rint( xoff + xscal * MEMPLOT_X1(mp,ii) ) ;
136 x2 = rint( xoff + xscal * MEMPLOT_X2(mp,ii) ) ;
137 y1 = rint( yoff + yscal * (1.0 - MEMPLOT_Y1(mp,ii)) ) ;
138 y2 = rint( yoff + yscal * (1.0 - MEMPLOT_Y2(mp,ii)) ) ;
139 if( x1 < x2 ){ xb=x1; xt=x2; } else { xb=x2; xt=x1; }
140 if( y1 < y2 ){ yb=y1; yt=y2; } else { yb=y2; yt=y1; }
141 w = xt-xb+1 ; h = yt-yb+1 ;
142 #ifdef DEBUG
143 ININFO_message("drawfilledrectangle") ;
144 #endif
145 mri_drawfilledrectangle( im , xb,yb , w,h , rrr,ggg,bbb ) ;
146 skip = 1 ;
147 }
148 break ;
149
150 case THCODE_BALL:
151 case THCODE_CIRC:{
152 int xcor,ycor , xcen,ycen , rad ; float xrad,yrad ;
153 unsigned int ww, hh ;
154 xcen = rint(xoff + xscal * MEMPLOT_X1(mp,ii) );
155 ycen = rint(yoff + yscal * (1.0 - MEMPLOT_Y1(mp,ii)) );
156 xrad = xscal * MEMPLOT_X2(mp,ii) ;
157 yrad = yscal * MEMPLOT_X2(mp,ii) ; rad = rintf(sqrtf(xrad*yrad)) ;
158 #ifdef DEBUG
159 ININFO_message("drawcircle") ;
160 #endif
161 mri_drawcircle( im , xcen,ycen , rad, rrr,ggg,bbb , (thc==THCODE_BALL) ) ;
162 skip = 1 ;
163 }
164 break ;
165
166 case THCODE_OPAC:{ /* opacity [22 Jul 2004] */
167 #ifdef DEBUG
168 ININFO_message("set opacity %f",MEMPLOT_X1(mp,ii)) ;
169 #endif
170 mri_draw_opacity( MEMPLOT_X1(mp,ii) ) ;
171 skip = 1 ;
172 }
173 break ;
174 }
175
176 } else if( new_thick != old_thick ){ /* normal case: change line thickness */
177
178 old_thick = new_thick ; /* thickness not used at this time */
179 sthick = new_thick * scal ; /* sthick = MIN(sthick,9.0f) ; */
180 #ifdef DEBUG
181 ININFO_message("set thick %f",sthick) ;
182 #endif
183
184 }
185
186 /* scale line endpoint coords to ints (also see zzphph.f) */
187
188 if( !skip ){
189 float a1 = MEMPLOT_X1(mp,ii) ;
190 float a2 = MEMPLOT_X2(mp,ii) ;
191 float b1 = (1.0f - MEMPLOT_Y1(mp,ii)) ;
192 float b2 = (1.0f - MEMPLOT_Y2(mp,ii)) ;
193
194 /* scale from objective coords to pixels */
195 x1 = (int)( xoff + xscal * a1 ) ; x2 = (int)( xoff + xscal * a2 ) ;
196 y1 = (int)( yoff + yscal * b1 ) ; y2 = (int)( yoff + yscal * b2 ) ;
197
198 /* draw it */
199 #ifdef DEBUG
200 ININFO_message("drawline x1=%d y1=%d x2=%d y2=%d rrr=%d ggg=%d bbb=%d",x1,y1,x2,y2,(int)rrr,(int)ggg,(int)bbb) ;
201 #endif
202 mri_drawline( im , x1,y1 , x2,y2 , rrr,ggg,bbb ) ;
203
204 /* the following code "cheats" to draw thick lines,
205 by drawing multiple 1-pixel lines around the central line */
206
207 if( do_thick && sthick >= 1.0f && (x1 != x2 || y1 != y2) ){ /* 06 Dec 2007 */
208 float da=a2-a1 , db=b2-b1 , dl=new_thick/sqrtf(da*da+db*db) ;
209 float c1,c2 , d1,d2 ;
210 int jj , ss=(int)(4.44f*sthick+0.444f) ; /* ss = num parallel lines */
211
212 /* spacing for the parallel lines (in objective space) */
213
214 dl /= (2*ss) ; da *= dl ; db *= dl ; ss = MAX(ss,2) ;
215 #if 1
216 /* this code draws filled circles at the endpoints, to
217 minimize possible gaps where thick lines meet at a sharp corner */
218
219 if( sthick >= 2.0f && sthick == sthick_old ){ /* 01 May 2012 */
220 int rad = (int)(0.505f*sthick+0.444f) ;
221 if( x1 == x2_old && y1 == y2_old ){
222 mri_drawcircle( im , x1,y1 , rad, rrr,ggg,bbb , 1 ) ;
223 }
224 else if( x2 == x1_old && y2 == y1_old ){
225 mri_drawcircle( im , x2,y2 , rad, rrr,ggg,bbb , 1 ) ;
226 }
227 }
228 x1_old = x1; x2_old = x2; y1_old = y1; y2_old = y2; sthick_old = sthick;
229 #endif
230 for( jj=-ss ; jj <= ss ; jj++ ){ /* multiple parallel line segments: */
231 if( jj == 0 ) continue ; /* cheap, but it works, so there!! */
232 c1 = a1 + jj*db ; c2 = a2 + jj*db ; /* offsets in x direction */
233 d1 = b1 - jj*da ; d2 = b2 - jj*da ; /* offsets in y direction */
234 /* scale from objective coords to pixels */
235 x1 = (int)( xoff + xscal * c1 ) ; x2 = (int)( xoff + xscal * c2 ) ;
236 y1 = (int)( yoff + yscal * d1 ) ; y2 = (int)( yoff + yscal * d2 ) ;
237 mri_drawline( im , x1,y1 , x2,y2 , rrr,ggg,bbb ) ;
238 }
239 }
240 }
241 }
242
243 #ifdef DEBUG
244 ININFO_message("----- EXIT -----") ;
245 #endif
246 set_memplot_RGB_box(0,0,0,0) ; /* clear box */
247 EXRETURN ;
248 }
249
250 /*-----------------------------------------------------------------------*/
251 # undef BOr
252 # undef BOg
253 # undef BOb
254 # define BOr(i,j) bout[3*((i)+(j)*nxout)+0]
255 # define BOg(i,j) bout[3*((i)+(j)*nxout)+1]
256 # define BOb(i,j) bout[3*((i)+(j)*nxout)+2]
257 # undef BIr
258 # undef BIg
259 # undef BIb
260 # define BIr(i,j) ((unsigned int)bin[3*((i)+(j)*nxin)+0])
261 # define BIg(i,j) ((unsigned int)bin[3*((i)+(j)*nxin)+1])
262 # define BIb(i,j) ((unsigned int)bin[3*((i)+(j)*nxin)+2])
263
264 /* this func is used to scale down RGB image size
265 by factor of 2, which in turn is used to anti-alias the line drawing;
266 the algorithm used is simple averaging over each output pixel's 2x2 region */
267
mri_downsize_by2(MRI_IMAGE * imin)268 MRI_IMAGE * mri_downsize_by2( MRI_IMAGE *imin )
269 {
270 MRI_IMAGE *imout ; int nxin,nyin , nxout,nyout , ii,jj,i2,j2 ;
271 byte *bin , *bout ; unsigned int val ;
272
273 if( imin == NULL || imin->kind != MRI_rgb ) return NULL ;
274
275 nxin = imin->nx ; nyin = imin->ny ;
276 nxout = nxin /2 ; nyout = nyin / 2 ;
277
278 imout = mri_new( nxout , nyout , MRI_rgb ) ;
279 bout = MRI_RGB_PTR(imout) ;
280 bin = MRI_RGB_PTR(imin) ;
281
282 for( jj=0 ; jj < nyout ; jj++ ){ /* loop over output pixels */
283 j2 = 2*jj ; /* input pixel index = double output index */
284 for( ii=0 ; ii < nxout ; ii++ ){
285 i2 = 2*ii ;
286 val = BIr(i2,j2)+BIr(i2+1,j2)+BIr(i2,j2+1)+BIr(i2+1,j2+1)+1; BOr(ii,jj) = (byte)(val >> 2);
287 val = BIg(i2,j2)+BIg(i2+1,j2)+BIg(i2,j2+1)+BIg(i2+1,j2+1)+1; BOg(ii,jj) = (byte)(val >> 2);
288 val = BIb(i2,j2)+BIb(i2+1,j2)+BIb(i2,j2+1)+BIb(i2+1,j2+1)+1; BOb(ii,jj) = (byte)(val >> 2);
289 }
290 }
291
292 return imout ;
293 }
294
295 # undef BOr
296 # undef BOg
297 # undef BOb
298 # undef BIr
299 # undef BIg
300 # undef BIb
301
302 /*-----------------------------------------------------------------------*/
303
304 #undef IMSIZ_DEF
305 #define IMSIZ_DEF 1024
306 #undef IMSIZ_MAX
307 #define IMSIZ_MAX 8192
308
memplot_to_mri(MEM_plotdata * mp)309 static MRI_IMAGE * memplot_to_mri( MEM_plotdata *mp ) /* 05 Dec 2007 */
310 {
311 MRI_IMAGE *im ; int nx , ny , imsiz ;
312 byte *imp ;
313 int did_dup=0 ;
314
315 if( mp == NULL || MEMPLOT_NLINE(mp) < 1 ) return NULL ;
316
317 /* set image dimensions */
318
319 imsiz = (int)AFNI_numenv("AFNI_1DPLOT_IMSIZE") ; /* might be 0 */
320 if( imsiz < 128 ) imsiz = IMSIZ_DEF ;
321 else if( imsiz > IMSIZ_MAX ) imsiz = IMSIZ_MAX ;
322
323 if( mp->aspect > 1.0f ){
324 nx = imsiz ; ny = nx / mp->aspect ;
325 } else {
326 nx = imsiz * mp->aspect ; ny = imsiz ;
327 }
328
329 /* for smaller output images, make image twice the size,
330 render lines into it, then scale down == anti-aliasing == looks better */
331
332 if( imsiz <= 2048 ){ nx *=2 ; ny *=2 ; did_dup = 1 ; }
333
334 im = mri_new( nx , ny , MRI_rgb ) ; /* full of 0s = blacked out */
335 imp = MRI_RGB_PTR(im) ; memset( imp , 255 , 3*nx*ny ) ; /* white-ize image */
336 set_memplot_RGB_box(0,0,0,0) ;
337 do_thick = 1 ; /* allow thick lines */
338 memplot_to_RGB_sef( im , mp , 0 , 0 , 0 ) ; /* the actual drawing! */
339 do_thick = 0 ;
340
341 if( did_dup ){ /* scale image down to output size */
342 MRI_IMAGE *qim = mri_downsize_by2(im) ; mri_free(im) ; im = qim ;
343 }
344
345 return im ;
346 }
347
348 /*-----------------------------------------------------------------------*/
349 /* Functions to write memplot to image files */
350
memplot_to_jpg(char * fname,MEM_plotdata * mp)351 void memplot_to_jpg( char *fname , MEM_plotdata *mp ) /* 05 Dec 2007 */
352 {
353 MRI_IMAGE *im ;
354
355 if( fname == NULL || *fname == '\0' ) return ;
356
357 im = memplot_to_mri( mp ) ; if( im == NULL ) return ;
358 mri_write_jpg(fname,im) ; mri_free(im) ;
359 return ;
360 }
361
362 /*-----------------------------------------------------------------------*/
363
memplot_to_png(char * fname,MEM_plotdata * mp)364 void memplot_to_png( char *fname , MEM_plotdata *mp ) /* 05 Dec 2007 */
365 {
366 MRI_IMAGE *im ;
367
368 if( fname == NULL || *fname == '\0' ) return ;
369
370 im = memplot_to_mri( mp ) ; if( im == NULL ) return ;
371 mri_write_png(fname,im) ; mri_free(im) ;
372 return ;
373 }
374
375 /*-----------------------------------------------------------------------*/
376
memplot_to_pnm(char * fname,MEM_plotdata * mp)377 void memplot_to_pnm( char *fname , MEM_plotdata *mp ) /* 06 Jan 2015 */
378 {
379 MRI_IMAGE *im ;
380
381 if( fname == NULL || *fname == '\0' ) return ;
382
383 im = memplot_to_mri( mp ) ; if( im == NULL ) return ;
384 mri_write_pnm(fname,im) ; mri_free(im) ;
385 return ;
386 }
387
388 /*-----------------------------------------------------------------------*/
389 /* Find the min/max coordinates used in a plot structure
390 (abstract coords in range 0..1, not yet scaled to actual plotting).
391 Error return has out.a >= out.b and/or out.c >= out.d -- 19 Aug 2021
392 *//*---------------------------------------------------------------------*/
393
memplot_bbox(MEM_plotdata * mp)394 float_quad memplot_bbox( MEM_plotdata *mp )
395 {
396 float_quad out = {0.0f,0.0f,0.0f,0.f} ;
397 float xbot,xtop , ybot,ytop ;
398 float x1,y1,x2,y2 , new_thick ;
399 int ii , nline ;
400
401 if( mp == NULL ) return out ;
402
403 nline = MEMPLOT_NLINE(mp) ; if( nline < 1 ) return out ;
404
405 /* swap to make them ordered */
406 #define SORD(a,b) if( a > b ){ float t=a; a=b; b=t; } else {}
407
408 xbot = ybot = 666.0f ;
409 ytop = xtop = -666.0f ;
410
411 for( ii=0 ; ii < nline ; ii++ ){
412
413 new_thick = MEMPLOT_TH(mp,ii) ; /* thickness of line segment */
414
415 if( new_thick < 0.0f ){ /* special negative thickness codes */
416 int thc = (int)(-new_thick) ; /* mean special drawing actions */
417 switch( thc ){
418 case THCODE_FRECT:
419 case THCODE_RECT:{ /* rectangle */
420 x1 = MEMPLOT_X1(mp,ii) ; x2 = MEMPLOT_X2(mp,ii) ; SORD(x1,x2) ;
421 y1 = MEMPLOT_Y1(mp,ii) ; y2 = MEMPLOT_Y2(mp,ii) ; SORD(y1,y2) ;
422 xbot = MIN(xbot,x1) ; xtop = MAX(xtop,x2) ;
423 ybot = MIN(ybot,y1) ; ytop = MAX(ytop,y2) ;
424 }
425 break ;
426
427 case THCODE_BALL:
428 case THCODE_CIRC:{
429 int xcen,ycen , rad ; float xrad,yrad ;
430 unsigned int ww, hh ;
431 xcen = MEMPLOT_X1(mp,ii) ;
432 ycen = MEMPLOT_Y1(mp,ii) ;
433 rad = fabsf(MEMPLOT_X2(mp,ii)) ;
434 x1 = xcen - rad ; x2 = xcen + rad ;
435 y1 = ycen - rad ; y2 = ycen + rad ;
436 xbot = MIN(xbot,x1) ; xtop = MAX(xtop,x2) ;
437 ybot = MIN(ybot,y1) ; ytop = MAX(ytop,y2) ;
438 }
439 break ;
440
441 /* other special codes don't actually cause drawing */
442 }
443
444 } else { /* normal line segment */
445
446 x1 = MEMPLOT_X1(mp,ii) ; x2 = MEMPLOT_X2(mp,ii) ; SORD(x1,x2) ;
447 y1 = MEMPLOT_Y1(mp,ii) ; y2 = MEMPLOT_Y2(mp,ii) ; SORD(y1,y2) ;
448 xbot = MIN(xbot,x1) ; xtop = MAX(xtop,x2) ;
449 ybot = MIN(ybot,y1) ; ytop = MAX(ytop,y2) ;
450
451 }
452 }
453
454 out.a = xbot ; out.b = xtop ;
455 out.c = ybot ; out.d = ytop ;
456 return out ;
457 }
458