1 #include "mrilib.h"
2 #include "xutil.h"
3 #include "xim.h"
4
5 /*--------------------------------------------------------------------------*/
6 /** AFNI_XDrawLines() replacement for XDrawLines() **/
7 /*--------------------------------------------------------------------------*/
8
9 /*======================================================================*/
10 /* Upsampling code, adapted from code in mri_dup.c [28 May 2020] */
11
12 #ifndef SHORTIZE
13 # define SHORTIZE(xx) ( ((xx) < -32767.0f) ? (short)-32767 \
14 : ((xx) > 32767.0f) ? (short) 32767 : (short)rint(xx) )
15 #endif
16
17 #define RENUP_VEC(vv,kk) { (vv) = (float *)realloc((vv),(kk)*sizeof(float)); }
18
19 /*-- seventh order interpolation polynomials --*/
20
21 #define S_M3(x) (x*(x*x-1.0f)*(x*x-4.0f)*(x-3.0f)*(4.0f-x)*0.0001984126984f)
22 #define S_M2(x) (x*(x*x-1.0f)*(x-2.0f)*(x*x-9.0f)*(x-4.0f)*0.001388888889f)
23 #define S_M1(x) (x*(x-1.0f)*(x*x-4.0f)*(x*x-9.0f)*(4.0f-x)*0.004166666667f)
24 #define S_00(x) ((x*x-1.0f)*(x*x-4.0f)*(x*x-9.0f)*(x-4.0f)*0.006944444444f)
25 #define S_P1(x) (x*(x+1.0f)*(x*x-4.0f)*(x*x-9.0f)*(4.0f-x)*0.006944444444f)
26 #define S_P2(x) (x*(x*x-1.0f)*(x+2.0f)*(x*x-9.0f)*(x-4.0f)*0.004166666667f)
27 #define S_P3(x) (x*(x*x-1.0f)*(x*x-4.0f)*(x+3.0f)*(4.0f-x)*0.001388888889f)
28 #define S_P4(x) (x*(x*x-1.0f)*(x*x-4.0f)*(x*x-9.0f)*0.0001984126984f)
29
30 /* 7 point interpolate as floats, cast to short at end */
31
32 #define INT7(k,i) \
33 ( vout = ((k)==0) ? ( far[i] ) \
34 : ( fm3[k] * far[i-3] + fm2[k] * far[i-2] \
35 + fm1[k] * far[i-1] + f00[k] * far[i ] \
36 + fp1[k] * far[i+1] + fp2[k] * far[i+2] \
37 + fp3[k] * far[i+3] + fp4[k] * far[i+4] ), SHORTIZE(vout) )
38
39 /*----------------------------------------------------------------------------
40 Up sample a short-valued array sar[0..nar-1] nup times to produce
41 sout[0..(nar-1)*nup] -- so have (nar-1)*nup+1 output points.
42 Uses 7th order polynomial interpolation (mostly).
43 NOTE: the number of output points, and the use of shorts, is where
44 this function differs from the original in mri_dup.c
45 NOTE: for use in AFNI_XDrawLines(), which uses XPoint,
46 which uses shorts as the pixel locations
47 ------------------------------------------------------------------------------*/
48
upsample_7short(int nup,int nar,short * sar,short * sout)49 static void upsample_7short( int nup , int nar , short *sar , short *sout )
50 {
51 int kk,ii , ibot,itop ;
52 float nupi , val,vout ;
53 /* static arrays for interpolation */
54 static int nupold = -1 ;
55 static int nupmax = 0;
56 static float *fm3=NULL, *fm2=NULL, *fm1=NULL, *f00=NULL,
57 *fp1=NULL, *fp2=NULL, *fp3=NULL, *fp4=NULL;
58 static float *far=NULL , *qar=NULL ;
59
60 /*-- sanity checks --*/
61
62 if( nup < 1 || nar < 2 || sar == NULL || sout == NULL ) return ;
63
64 if( nup == 1 ){ memcpy( sout, sar, sizeof(short)*nar ); return; }
65
66 /* temp float array for data;
67 with trickery (pointer arithmetic) to use negative subscripts,
68 because we need input [i-3]..[i+4] values to interpolate the
69 output values between input indexes i and i+1, for i=0..nar-2 */
70
71 qar = (float *)malloc(sizeof(float)*(nar+7)) ;
72 far = qar + 3 ; /* start the indexing trickery! */
73 for( kk=0 ; kk < nar ; kk++ ) far[kk] = (float)sar[kk] ;
74 val = far[1] - far[0] ;
75 far[-1] = far[0] - 3.0f*val ; /* linear extrapolate before start */
76 far[-2] = far[0] - 2.0f*val ;
77 far[-3] = far[0] - val ;
78 val = far[nar-1] - far[nar-2] ;
79 far[nar] = far[nar-1] + val ; /* and after end */
80 far[nar+1] = far[nar-1] + 2.0f*val ;
81 far[nar+2] = far[nar-1] + 3.0f*val ;
82 far[nar+3] = far[nar-1] + 4.0f*val ; /* this value not really needed */
83
84 nupi = 1.0f / (float)nup ;
85
86 /*-- initialize 7th order interpolation coefficients, if nup has changed --*/
87
88 if (nupmax < nup) { /* resize coefficient arrays if bigger than of old */
89 RENUP_VEC(fm3,nup); RENUP_VEC(fm2,nup);
90 RENUP_VEC(fm1,nup); RENUP_VEC(f00,nup);
91 RENUP_VEC(fp1,nup); RENUP_VEC(fp2,nup);
92 RENUP_VEC(fp3,nup); RENUP_VEC(fp4,nup);
93 nupmax = nup ; /* keep track of largest array size ever used */
94 }
95
96 if( nup != nupold ){ /* recalculate coefs if not the same as last time in */
97 for( kk=0 ; kk < nup ; kk++ ){
98 val = ((float)kk) * nupi ;
99 fm3[kk] = S_M3(val); fm2[kk] = S_M2(val); fm1[kk] = S_M1(val);
100 f00[kk] = S_00(val); fp1[kk] = S_P1(val); fp2[kk] = S_P2(val);
101 fp3[kk] = S_P3(val); fp4[kk] = S_P4(val);
102 }
103 nupold = nup ; /* for the historical records */
104 }
105
106 /*-- FINALLY: interpolate --*/
107
108 ibot = 0 ; itop = nar-2 ; /* add points between [ii] and [ii+1] */
109
110 switch( nup ){
111 default: /* outer and inner loops */
112 for( ii=ibot ; ii <= itop ; ii++ )
113 for( kk=0 ; kk < nup ; kk++ ) sout[kk+ii*nup] = INT7(kk,ii) ;
114 break ;
115
116 case 2: /* inner loop unrolled for optimizer */
117 for( ii=ibot ; ii <= itop ; ii++ ){
118 sout[ii*nup] = INT7(0,ii) ; sout[ii*nup+1] = INT7(1,ii) ;
119 }
120 break ;
121
122 case 3: /* inner loop unrolled for optimizer */
123 for( ii=ibot ; ii <= itop ; ii++ ){
124 sout[ii*nup] = INT7(0,ii) ; sout[ii*nup+1] = INT7(1,ii) ;
125 sout[ii*nup+2] = INT7(2,ii) ;
126 }
127 break ;
128
129 case 4: /* inner loop unrolled for optimizer */
130 for( ii=ibot ; ii <= itop ; ii++ ){
131 sout[ii*nup] = INT7(0,ii) ; sout[ii*nup+1] = INT7(1,ii) ;
132 sout[ii*nup+2] = INT7(2,ii) ; sout[ii*nup+3] = INT7(3,ii) ;
133 }
134 break ;
135 }
136 sout[(nar-1)*nup] = sar[nar-1] ; /* need final value */
137
138 free(qar) ;
139 return ;
140 }
141
142 /*--------------------------------------------------------------------------*/
143
144 /* Hermite spline basis funcs (damn, them Frenchies was SMART) */
145
146 #define H00(x) ( (x)*(x)*(2.0f*(x)-3.0f)+1.0f )
147 #define H01(x) ( (x)*(x)*(3.0f-2.0f*(x)) )
148 #define H10(x) ( (x)*((x)*(x)-2.0f*(x)+1.0f) )
149 #define H11(x) ( (x)*(x)*((x)-1.0f) )
150
151 /* interpolate as floats, cast to short at end */
152
153 #define INTM(k,i) \
154 ( vout = ((k)==0) ? ( far[i] ) \
155 : ( h00[k] * far[i] + h01[k] * far[i+1] \
156 + h10[k] * mk[i] + h11[k] * mk[i+1] ) , SHORTIZE(vout) )
157
158 /*-------- Monotonic cubic spline interpolation:
159 https://en.wikipedia.org/wiki/Monotone_cubic_interpolation --------*/
160
upsample_monoshort(int nup,int nar,short * sar,short * sout)161 static void upsample_monoshort( int nup , int nar , short *sar , short *sout )
162 {
163 int kk,ii , ibot,itop ;
164 float nupi , val,vout , ak,bk,tk , dmax=0.0f,fmax=0.0f ;
165 /* static arrays to avoid reallocation a milliard of times */
166 static int nupold = -1 ;
167 static int nupmax = 0;
168 static float *far , *dk , *mk ;
169 static float *h00=NULL , *h01=NULL , *h10=NULL , *h11=NULL ;
170
171 /*-- sanity checks --*/
172
173 if( nup < 1 || nar < 2 || sar == NULL || sout == NULL ) return ;
174
175 if( nup == 1 ){ memcpy( sout, sar, sizeof(short)*nar ); return; }
176
177 /* temp float arrays for data, etc */
178
179 far = (float *)calloc(sizeof(float),nar) ;
180 dk = (float *)calloc(sizeof(float),nar) ; /* secants */
181 mk = (float *)calloc(sizeof(float),nar) ; /* slopes */
182
183 for( kk=0 ; kk < nar ; kk++ ){ /* copy input to floats */
184 far[kk] = (float)sar[kk] ;
185 val = fabsf(far[kk]) ; if( val > fmax ) fmax = val ;
186 }
187
188 for( kk=0 ; kk < nar-1 ; kk++ ){ /* get secants */
189 dk[kk] = far[kk+1]-far[kk] ;
190 val = fabsf(dk[kk]) ; if( val > dmax ) dmax = val ;
191 }
192
193 mk[0] = dk[0] ; mk[nar-1] = dk[nar-2] ; /* get slopes */
194 for( kk=1 ; kk < nar-1 ; kk++ ) mk[kk] = 0.5f*(dk[kk]*dk[kk-1]) ;
195
196 /* adjust slopes for monotonicity */
197
198 vout = 0.001f*dmax + 0.0000001f*fmax ;
199 for( kk=0 ; kk < nar-1 ; kk++ ){
200 if( fabsf(dk[kk]) <= vout ){
201 mk[kk] = mk[kk+1] = 0.0f ;
202 } else {
203 ak = mk[kk] / dk[kk] ; bk = mk[kk+1] / dk[kk] ;
204 if( ak < 0.0f ){ mk[kk] = 0.0f ; ak = 0.0f ; }
205 if( bk < 0.0f ){ mk[kk+1] = 0.0f ; bk = 0.0f ; }
206 val = ak*ak+bk*bk ;
207 if( val > 9.0f ){
208 tk = 3.0f / sqrtf(val) ;
209 mk[kk] = tk * ak * dk[kk] ;
210 mk[kk+1] = tk * bk * dk[kk] ;
211 }
212 }
213 }
214
215 nupi = 1.0f / (float)nup ;
216
217 /*-- initialize Hermite interpolation coefficients, if nup has changed --*/
218
219 if (nupmax < nup) { /* resize coefficient arrays if bigger than of old */
220 RENUP_VEC(h00,nup); RENUP_VEC(h01,nup);
221 RENUP_VEC(h10,nup); RENUP_VEC(h11,nup);
222 nupmax = nup ; /* keep track of largest every used */
223 }
224
225 if( nup != nupold ){ /* recalculate if not the same as last time in */
226 for( kk=0 ; kk < nup ; kk++ ){
227 val = ((float)kk) * nupi ;
228 h00[kk] = H00(val) ; h01[kk] = H01(val) ;
229 h10[kk] = H10(val) ; h11[kk] = H11(val) ;
230 }
231 nupold = nup ; /* for the historical records */
232 }
233
234 /*-- FINALLY: interpolate --*/
235
236 ibot = 0 ; itop = nar-2 ; /* add points between [ii] and [ii+1] */
237
238 switch( nup ){
239 default: /* outer and inner loops */
240 for( ii=ibot ; ii <= itop ; ii++ )
241 for( kk=0 ; kk < nup ; kk++ ) sout[kk+ii*nup] = INTM(kk,ii) ;
242 break ;
243
244 case 2: /* inner loop unrolled for optimizer */
245 for( ii=ibot ; ii <= itop ; ii++ ){
246 sout[ii*nup] = INTM(0,ii) ; sout[ii*nup+1] = INTM(1,ii) ;
247 }
248 break ;
249
250 case 3: /* inner loop unrolled for optimizer */
251 for( ii=ibot ; ii <= itop ; ii++ ){
252 sout[ii*nup] = INTM(0,ii) ; sout[ii*nup+1] = INTM(1,ii) ;
253 sout[ii*nup+2] = INTM(2,ii) ;
254 }
255 break ;
256
257 case 4: /* inner loop unrolled for optimizer */
258 for( ii=ibot ; ii <= itop ; ii++ ){
259 sout[ii*nup] = INTM(0,ii) ; sout[ii*nup+1] = INTM(1,ii) ;
260 sout[ii*nup+2] = INTM(2,ii) ; sout[ii*nup+3] = INTM(3,ii) ;
261 }
262 break ;
263 }
264 sout[(nar-1)*nup] = sar[nar-1] ; /* need final value */
265
266 free(mk) ; free(dk) ; free(far) ;
267 return ;
268 }
269
270 /*--------- Upsample/interpolate both ways, and combine them;
271 Why? It looked better than either way alone, to me [RWC] ---------*/
272
upsample_comboshort(int nup,int nar,short * sar,short * sout)273 static void upsample_comboshort( int nup , int nar , short *sar , short *sout )
274 {
275 short *st1 , *st2 ; int ii,nout=(nar-1)*nup+1 ; float val ;
276
277 if( nup < 1 || nar < 2 || sar == NULL || sout == NULL ) return ;
278 if( nup == 1 ){ memcpy( sout, sar, sizeof(short)*nar ); return; }
279
280 st1 = (short *)malloc(sizeof(short)*nout) ;
281 st2 = (short *)malloc(sizeof(short)*nout) ;
282
283 upsample_7short ( nup,nar,sar,st1 ) ;
284 upsample_monoshort( nup,nar,sar,st2 ) ;
285 for( ii=0 ; ii < nout ; ii++ ){
286 val = 0.3f*(float)st1[ii] + 0.7f*(float)st2[ii] ; /* mixing */
287 sout[ii] = SHORTIZE(val) ;
288 }
289 free(st2) ; free(st1) ; return ;
290 }
291
292 /**---- this macro defines the function used for the actual upsampling ----**/
293
294 #define UPSAMPLE upsample_comboshort
295
296 /*--------------------------------------------------------------------------*/
297 /* Draw X11 lines, but upsampled to look smoother and more delicious */
298
AFNI_XDrawLines(Display * display,Drawable d,GC gc,XPoint * points,int npoints,int mode,int nupsam)299 void AFNI_XDrawLines( Display *display, Drawable d,
300 GC gc, XPoint *points, int npoints, int mode , int nupsam )
301 {
302 XPoint *new_points ;
303 int new_npoints , ii ;
304 short *old_xy , *new_xy ;
305
306 /* this is for the jaggedy losers */
307
308 if( nupsam <= 1 || npoints < 3 ){
309 XDrawLines(display,d,gc,points,npoints,mode) ; return ;
310 }
311
312 /* this is for the REAL MEN out there (insert Tarzan yell) */
313
314 new_npoints = (npoints-1)*nupsam+1 ;
315 new_points = (XPoint *)malloc(sizeof(XPoint)*new_npoints) ;
316
317 old_xy = (short *)malloc(sizeof(short)*npoints) ;
318 new_xy = (short *)malloc(sizeof(short)*new_npoints) ;
319
320 /* upsample the x coordinates */
321 for( ii=0 ; ii < npoints ; ii++ ) old_xy[ii] = points[ii].x ;
322 UPSAMPLE( nupsam , npoints , old_xy , new_xy ) ;
323 for( ii=0 ; ii < new_npoints ; ii++ ) new_points[ii].x = new_xy[ii] ;
324
325 /* upsample the y coordinates */
326 for( ii=0 ; ii < npoints ; ii++ ) old_xy[ii] = points[ii].y ;
327 UPSAMPLE( nupsam , npoints , old_xy , new_xy ) ;
328 for( ii=0 ; ii < new_npoints ; ii++ ) new_points[ii].y = new_xy[ii] ;
329
330 /* and draw the straight lines between them */
331 XDrawLines(display,d,gc,new_points,new_npoints,mode) ;
332
333 free(new_xy) ; free(old_xy) ; free(new_points) ; return ;
334 }
335
336 /*----------------------------------------------------------------------------*/
337 /* Similar code for drawing a smooted (smoothed?) filled polygon (for pmplot) */
338 /* https://en.wikipedia.org/wiki/Reed_Smoot */
339 /* https://en.wikipedia.org/wiki/Oliver_R._Smoot */
340 /*----------------------------------------------------------------------------*/
341
AFNI_XFillPolygon(Display * display,Drawable d,GC gc,XPoint * points,int npoints,int shape,int mode,int nupsam)342 void AFNI_XFillPolygon( Display *display, Drawable d,
343 GC gc, XPoint *points, int npoints, int shape ,
344 int mode , int nupsam )
345 {
346 XPoint *new_points ;
347 int new_npoints , ii ;
348 short *old_xy , *new_xy ;
349
350 if( nupsam <= 1 ){
351 XFillPolygon(display,d,gc,points,npoints,shape,mode) ; return ;
352 }
353
354 new_npoints = (npoints-1)*nupsam+1 ;
355 new_points = (XPoint *)malloc(sizeof(XPoint)*new_npoints) ;
356
357 old_xy = (short *)malloc(sizeof(short)*npoints) ;
358 new_xy = (short *)malloc(sizeof(short)*new_npoints) ;
359
360 for( ii=0 ; ii < npoints ; ii++ ) old_xy[ii] = points[ii].x ;
361 UPSAMPLE( nupsam , npoints , old_xy , new_xy ) ;
362 for( ii=0 ; ii < new_npoints ; ii++ ) new_points[ii].x = new_xy[ii] ;
363
364 for( ii=0 ; ii < npoints ; ii++ ) old_xy[ii] = points[ii].y ;
365 UPSAMPLE( nupsam , npoints , old_xy , new_xy ) ;
366 for( ii=0 ; ii < new_npoints ; ii++ ) new_points[ii].y = new_xy[ii] ;
367
368 XFillPolygon(display,d,gc,new_points,new_npoints,shape,mode) ;
369
370 free(new_xy) ; free(old_xy) ; free(new_points) ; return ;
371 }
372